diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index cc76854adca94414fda61dce656a012c33872a4d..9bd648fd2d2182589d089c684f7b9d3a624bd2f9 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -200,7 +200,7 @@ import java.util.List; * *

An activity has essentially four states:

* -

Animações de feedback de toque são integradas em várias vistas padrões, como botões. As novas APIs +

Animações de feedback de toque são integradas em várias vistas padrão, como botões. As novas APIs permitem personalizar essas animações e adicioná-las às vistas personalizadas.

Para obter mais informações, consulte Como definir diff --git a/docs/html-intl/intl/pt-br/design/patterns/compatibility.jd b/docs/html-intl/intl/pt-br/design/patterns/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..775af0c312a0e8acbd7105d36846881146af5b61 --- /dev/null +++ b/docs/html-intl/intl/pt-br/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=Compatibilidade com versões anteriores +page.tags="support" +page.metaDescription=Observações sobre como o Android 4.x adapta IUs projetadas para versões de SO e hardware mais antigos. +@jd:body + + +

+

Documentos do desenvolvedor

+

Suporte a diferentes dispositivos

+
+ + +

Mudanças significativas no Android 3.0 incluem:

+ +

O Android 4.0 leva essas mudanças para tablets à plataforma de telefone.

+ +

Adaptação do Android 4.0 a hardware e aplicativos mais antigos

+ +
+
+ +

Telefones com controles de navegação virtuais

+

Aplicativos Android escritos para o Android 3.0 e posteriores exibem ações na barra de ações. Ações que não cabem +na barra de ações ou não são importantes o suficiente para serem exibidas no nível superior aparecem nas +ações adicionais.

+

Os usuários acessam as ações adicionais tocando na barra de ações.

+ +
+
+ + + +
+
+ +
+
+ +

Telefones com teclas de navegação físicas

+

Telefones Android com teclas de navegação de hardware tradicionais não exigem a barra de navegação virtual na +parte inferior da tela. Em vez disso, as ações adicionais estão disponíveis na tecla de hardware de menu. A janela pop-up +de ações resultante tem o mesmo estilo que no exemplo anterior, mas é exibida na parte inferior da tela.

+ +
+
+ + + +
+
+ +
+
+ +

Aplicativos legados em telefones com controles de navegação virtuais

+

Ao executar um aplicativo que foi desenvolvido para Android 2.3 ou anterior em um telefone com controles de +navegação virtuais, um controle de ações adicionais é exibido no lado direito da barra de navegação virtual. É possível +tocar no controle para exibir as ações do aplicativo no estilo tradicional de menu do Android.

+ +
+
+ + + +
+
diff --git a/docs/html-intl/intl/pt-br/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/pt-br/design/patterns/confirming-acknowledging.jd index 934dbddf55228455abfa3c9f6363f12bf845bf8c..237c7199ad7d7f4952e0ef40d45c9bfe27d3c3a4 100644 --- a/docs/html-intl/intl/pt-br/design/patterns/confirming-acknowledging.jd +++ b/docs/html-intl/intl/pt-br/design/patterns/confirming-acknowledging.jd @@ -4,12 +4,12 @@ page.tags=dialog,toast,notification

Em algumas situações, quando um usuário invoca uma ação em seu aplicativo, é uma boa ideia confirmar ou reconhecer essa ação com texto.

-
-
+
+

Confirmar é pedir ao usuário que verifique se realmente quer prosseguir com a ação que acabou de invocar. Em alguns casos, a confirmação é apresentada com uma advertência ou informações críticas relacionadas à ação que ele precisa considerar.

-
+

Reconhecer é exibir texto para avisar ao usuário que a ação que acabou de ser invocada foi concluída. Isso remove a incerteza sobre operações implícitas que o sistema adota. Em alguns casos, o reconhecimento é apresentado com uma opção para desfazer a ação.

@@ -22,14 +22,14 @@ page.tags=dialog,toast,notification

Confirmação

-
-
-

Exemplo: livros do Google Play

+
+
+

Exemplo: Livros do Google Play

Nesse exemplo, o usuário solicitou a exclusão de um livro da biblioteca do Google Play. Um alerta aparece para confirmar essa ação porque é importante entender que o livro não estará mais disponível em nenhum dispositivo.

Ao montar uma caixa de diálogo de confirmação, use um título significativo que ecoe a ação solicitada.

-
+

Exemplo: Android Beam

As confirmações não necessariamente precisam ser apresentadas em um alerta com dois botões. Depois de iniciar o Android Beam, o usuário é solicitado a tocar no conteúdo a ser compartilhado (nesse exemplo, uma foto). Se ele decidir não prosseguir, simplesmente afastará o celular.

@@ -37,30 +37,30 @@ page.tags=dialog,toast,notification

Reconhecimento

-
-
-

Exemplo: abandonar rascunho salvo do Gmail

+
+
+

Exemplo: Abandonar rascunho salvo do Gmail

Nesse exemplo, se o usuário navegar para trás ou para cima na tela de composição do Gmail, alguma coisa possivelmente inesperada acontecerá: o rascunho atual será salvo automaticamente. Um reconhecimento na forma de aviso torna isso aparente. Ele desaparece depois de alguns segundos.

-

Desfazer não é adequado aqui, pois o ato de salvar foi iniciado pelo aplicativo, não pelo usuário, Além de ser rápido e fácil retomar a composição da mensagem navegando para a lista de rascunhos.

+

Desfazer não é adequado aqui, pois o ato de salvar foi iniciado pelo aplicativo, não pelo usuário, além de ser rápido e fácil retomar a composição da mensagem navegando para a lista de rascunhos.

-
-

Exemplo: conversa do Gmail excluída

+
+

Exemplo: Conversa do Gmail excluída

Depois que o usuário exclui uma conversa da lista no Gmail, um reconhecimento aparece com a opção de desfazer. O reconhecimento permanece até que o usuário tome uma ação não relacionada, como rolar a lista.

Sem confirmação nem reconhecimento

-
-
+
+

Exemplo: +1

A confirmação é desnecessária. Se o usuário usar +1 por acidente, não é um problema. Poderá simplesmente tocar no botão novamente para desfazer a ação.

O reconhecimento é desnecessário. O usuário verá o botão +1 oscilar e ficar vermelho. Esse é um sinal muito claro.

-
+

Exemplo: Remover um aplicativo da tela inicial

A confirmação é desnecessária. Essa é uma ação deliberada: o usuário precisa arrastar e soltar um item em um alvo relativamente grande e isolado. Portanto, acidentes são altamente improváveis. Mas, se o usuário se arrepender da decisão, levará apenas alguns segundos para trazê-lo de volta.

diff --git a/docs/html-intl/intl/pt-br/design/patterns/navigation.jd b/docs/html-intl/intl/pt-br/design/patterns/navigation.jd index 920ccec6fd96dd2c6c95af5d1d5976ed370f2c86..8ed12f71b60b791042f0bed2905d37491c0cea2b 100644 --- a/docs/html-intl/intl/pt-br/design/patterns/navigation.jd +++ b/docs/html-intl/intl/pt-br/design/patterns/navigation.jd @@ -20,13 +20,13 @@ o botão Para Cima, consistindo no ícone do aplicativo e em um cursor -

Para Cima vs. Voltar

+

Para Cima vs Voltar

O botão Para Cima é usado para navegar dentro de um aplicativo com base nos relacionamentos hierárquicos -entre telas. Por exemplo, se a tela A exibe uma lista de itens e, selecionar um item leva à -tela B (que apresenta aquele item em mais detalhes), então a tela B deve oferecer um botão Para Cima que +entre telas. Por exemplo, se a tela A exibe uma lista de itens e selecionar um item leva à +tela B (que apresenta aquele item em mais detalhes), a tela B deve oferecer um botão Para Cima que volte à tela A.

-

Se determinada tela é a superior na hierarquia de um aplicativo (ou seja, a página inicial do aplicativo), ela não deve apresentar um botão +

Se determinada tela é a superior na hierarquia de um aplicativo (ou seja, a tela inicial do aplicativo), ela não deve apresentar um botão Para Cima.

O botão Voltar do sistema é usado para navegar, em ordem cronológica inversa, pelo histórico @@ -50,9 +50,9 @@ Voltar pode retornar o usuário à tela inicial ou até mesmo a um aplicativo di

Navegação dentro do seu aplicativo

Navegação para telas com vários pontos de entrada

-

Algumas vezes, uma tela não tem uma posição estrita dentro da hierarquia do aplicativo e pode ser atingida -de vários pontos de entrada — como uma tela de configurações que pode ser atingida de qualquer outra tela -do aplicativo. Nesse caso, o botão Para Cima deve escolher voltar para a tela anterior, comportando-se +

Algumas vezes, uma tela não tem uma posição rigorosa dentro da hierarquia do aplicativo e pode ser acessada +de vários pontos de entrada — como uma tela de configurações que pode ser acessada de qualquer outra tela +no aplicativo. Nesse caso, o botão Para Cima deve escolher voltar à tela anterior, comportando-se de forma idêntica a Voltar.

Mudança de vista dentro de uma tela

Mudar opções de vista de uma tela não muda o comportamento de Para Cima nem de Voltar: a tela ainda @@ -60,7 +60,7 @@ estará no mesmo lugar dentro da hierarquia do aplicativo e nenhum histórico de

Exemplos de tais mudanças de vista são:

  • Alternar vistas usando guias e/ou deslizando para a esquerda e para a direita
  • -
  • Alternar vistas usando um menu suspenso (também chamados de abas recolhidas)
  • +
  • Alternar vistas usando um menu suspenso (também chamadas de abas recolhidas)
  • Filtrar uma lista
  • Classificar uma lista
  • Mudar características de exibição (como mudar o zoom)
  • @@ -78,7 +78,7 @@ navegação não muda o comportamento de Para Cima ou Voltar.

    vinculadas pela lista de referência — por exemplo, ao navegar na Play Store entre aplicativos do mesmo desenvolvedor ou álbuns do mesmo artista. Nesses casos, seguir cada link cria um histórico, fazendo com que o botão Voltar passe por cada tela visualizada anteriormente. Para Cima deve continuar a -ignorar essas relacionadas e navegar para a tela do contêiner visualizada mais recentemente.

    +ignorar essas telas relacionadas e navegar para a tela do contêiner visualizada mais recentemente.

    @@ -89,9 +89,9 @@ Livro visualizado para os detalhes da adaptação do Filme. Nesse caso, Para Cim -

    Navegação para o seu aplicativo pelos widgets de página inicial e notificações

    +

    Navegação para o seu aplicativo pelos widgets de tela inicial e notificações

    -

    Você pode usar widget de página inicial ou notificações para ajudar seus usuários a navegar diretamente para telas +

    Você pode usar widgets de tela inicial ou notificações para ajudar os usuários a navegar diretamente para telas profundas na hierarquia do seu aplicativo. Por exemplo, o widget Caixa de Entrada do Gmail e a notificação de nova mensagem podem ignorar a tela Caixa de Entrada, levando o usuário diretamente a uma vista de conversa.

    @@ -100,17 +100,17 @@ ignorar a tela Caixa de Entrada, levando o usuário diretamente a uma vista de c
    • Se a tela de destino é normalmente acessada de uma determinada tela dentro do aplicativo, Para Cima deve navegar para essa tela.
    • -
    • Caso contrário, Para Cima deve navegar para a tela superior ("Página inicial") do aplicativo.
    • +
    • Caso contrário, Para Cima deve navegar para a tela superior ("Tela inicial") do aplicativo.

    No caso do botão Voltar, você deve tornar a navegação mais previsível inserindo o caminho de navegação para cima -completo na pilha de retorno da tarefa, até a tela superior do aplicativo. Isso permite que usuários +completo na pilha de retorno da tarefa até a tela superior do aplicativo. Isso permite que usuários que se esqueceram de como entraram no aplicativo naveguem para a tela superior do aplicativo antes de saírem.

    -

    Como exemplo, o widget da Página inicial do Gmail tem um botão para mergulhar diretamente para a tela +

    Como exemplo, o widget da Tela inicial do Gmail tem um botão para mergulhar diretamente para a tela de composição. Para Cima ou Voltar na tela de composição deve levar o usuário à Caixa de Entrada e, de lá, o -botão Voltar continua até a Página inicial.

    +botão Voltar continua até a Tela inicial.

    @@ -123,7 +123,7 @@ chamadas de notificações indiretas.

    Diferentemente de em notificações padrão (diretas), pressionar Voltar em uma tela intersticial da notificação indireta retorna o usuário ao ponto em que a notificação foi acionada — nenhuma -tela adicional é inserida na pilha de retorno. Quando o usuário prossegue para o aplicativo a partir da +tela adicional é inserida na pilha de retorno. Quando o usuário prossegue para o aplicativo da tela intersticial, Para Cima e Voltar se comportam como em notificações padrão, como descrito acima: navegando dentro do aplicativo em vez de voltar à tela intersticial.

    @@ -166,13 +166,13 @@ discutida abaixo.

    No Android, uma atividade é um componente do aplicativo que define uma tela de informações e todas as ações associadas que o usuário pode executar. Seu aplicativo é uma coleção de -atividades, consistindo em atividades que você cria e aquelas que reutiliza de outros aplicativos.

    +atividades, consistindo em atividades que você cria e naquelas que reutiliza de outros aplicativos.

    Uma tarefa é a sequência de atividades que um usuário segue para atingir um objetivo. Uma única tarefa pode usar atividades apenas de um aplicativo ou pode retirar atividades de uma série de outros aplicativos.

    -

    Uma intenção é um mecanismo para que um aplicativo sinalize que gostaria da assistência de outro +

    Uma intenção é um mecanismo para que um aplicativo sinalize que gostaria a assistência de outro aplicativo para realizar uma ação. As atividades de um aplicativo podem indicar a que intenções ele responde. Para intenções comuns, como "Compartilhar", o usuário pode ter vários aplicativos instalados que atendam a essa solicitação.

    @@ -180,7 +180,7 @@ instalados que atendam a essa solicitação.

    Exemplo: navegação entre aplicativos para suporte a compartilhamento

    Para entender como atividades, tarefas e intenções funcionam juntas, entenda como um aplicativo permite que usuários -compartilhem conteúdo usando outro aplicativo. Por exemplo, executar o aplicativo Play Store na tela inicial inicia +compartilhem conteúdo usando outro aplicativo. Por exemplo, executar o aplicativo Play Store na tela inicial começa uma nova Tarefa A (veja a figura baixo). Depois de navegar pela Play Store e tocar em um livro em promoção para ver os detalhes, o usuário permanecerá na mesma tarefa, estendendo-a ao adicionar atividades. Acionar a ação Compartilhar exibe ao usuário uma caixa de diálogo listando cada uma das atividades (de diferentes aplicativos) @@ -209,5 +209,5 @@ Recentes). Se o Gmail já tivesse a própria tarefa em execução em segundo pla pela Tarefa B — o contexto anterior é abandonado em favor do novo objetivo do usuário.

    Quando o aplicativo é registrado para tratar intenções com uma atividade em um ponto profundo da hierarquia do aplicativo, -consulte Navegação para o seu aplicativo pelos widgets de página +consulte Navegação para o seu aplicativo pelos widgets de tela inicial e notificações para ver orientações sobre como especificar a navegação Para Cima.

    diff --git a/docs/html-intl/intl/pt-br/guide/components/activities.jd b/docs/html-intl/intl/pt-br/guide/components/activities.jd new file mode 100644 index 0000000000000000000000000000000000000000..71986abe649bf508fa380c200e439875405b2dbb --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Atividades +page.tags=atividade,intenção +@jd:body + + + + + +

    {@link android.app.Activity} é um componente de aplicativo que fornece uma tela com a qual +os usuários podem interagir para fazer algo, como discar um número no telefone, tirar uma foto, enviar um e-mail +ou ver um mapa. Cada atividade recebe uma janela que exibe a interface do usuário. Geralmente, a janela +preenche a tela, mas pode ser menor que a tela e flutuar +sobre outras janelas.

    + +

    Aplicativos geralmente possuem várias atividades pouco vinculadas +entre si. Normalmente, uma atividade em um aplicativo é especificada como "principal", +que é a apresentada ao usuário ao iniciar o aplicativo pela primeira vez. Cada +atividade pode, então, iniciar outra atividade para executar diferentes ações. Ao iniciar uma nova +atividade, a atividade anterior é interrompida, mas o sistema conserva a atividade +em uma pilha (a "pilha de retorno"). Quando uma atividade inicia, ela é enviada para a pilha de retorno +e obtém o foco do usuário. A pilha de retorno segue o mecanismo básico de pilha UEPS (o último que entra é o primeiro que sai). +Assim, quando o usuário terminar a atividade atual e apertar o botão Voltar, +ela sairá da pilha (é destruída) e a atividade anterior será retomada (a pilha de retorno +é discutida em mais detalhes no documento Tarefas +e Pilha de Retorno).

    + +

    Quando uma atividade é interrompida devido ao início de uma nova atividade, ela é notificada acerca dessa alteração de estado +por meio de métodos de retorno de chamada do ciclo de vida da atividade. +Há diversos métodos de retorno de chamada que uma atividade pode receber devido a uma alteração +em seu estado — quando o sistema a está criando, interrompendo, retomando ou destruindo — e cada +retorno de chamada oferece uma oportunidade de executar trabalhos específicos +adequados a essa alteração de estado. Por exemplo: quando interrompida, a atividade deve liberar +todos os objetos grandes, como conexões com a rede ou com um banco de dados. Quando a atividade for retomada, será possível +readquirir os recursos necessários e retomar as ações interrompidas. Essas transições de estado +são parte do ciclo de vida da atividade.

    + +

    O restante deste documento discute o básico sobre a compilação e o uso de uma atividade, +incluindo uma discussão completa sobre o funcionamento do ciclo de vida da atividade para gerenciar adequadamente +a transição entre os diversos estados da atividade.

    + + + +

    Criação de uma atividade

    + +

    Para criar uma atividade, é preciso criar uma subclasse de {@link android.app.Activity} +(ou uma respectiva subclasse existente). Na subclasse, é preciso implementar um método de retorno de chamada +que o sistema chama quando ocorre a transição entre os diversos estados de seu ciclo de vida, +como na criação, interrupção, retomada ou destruição da atividade. Os dois métodos mais importantes +de retorno de chamada são:

    + +
    +
    {@link android.app.Activity#onCreate onCreate()}
    +
    É preciso implementar esse método. O sistema o chama ao criar +a atividade. Na implementação, é preciso inicializar os componentes essenciais +da atividade. + E, fundamentalmente, é quando se deve chamar {@link android.app.Activity#setContentView +setContentView()} para definir o layout da interface do usuário da atividade.
    +
    {@link android.app.Activity#onPause onPause()}
    +
    O sistema chama esse método como o primeiro indício de que o usuário está saindo +da atividade (embora não seja sempre uma indicação de que a atividade será destruída). É quando geralmente +se deve confirmar qualquer alteração que deva persistir além da sessão do usuário atual (porque +o usuário pode não retornar).
    +
    + +

    Há outros métodos de retorno de chamada do ciclo de vida que se pode usar para oferecer +uma experiência fluida ao usuário entre atividades e manipular interrupções inesperadas que venham a parar +ou até a destruir a atividade. Todos os métodos de retorno de chamada do ciclo de vida serão discutidos mais adiante +na seção sobre Gerenciamento do ciclo de vida da atividade.

    + + + +

    Implementação de uma interface do usuário

    + +

    A interface do usuário de uma atividade é fornecida por uma hierarquia de objetos — de vistas derivados +da classe {@link android.view.View}. Cada vista controla um espaço retangular específico +dentro da janela da atividade e pode responder à interação com o usuário. Por exemplo: uma vista pode ser +um botão que inicia uma ação quando o usuário toca nele.

    + +

    O Android oferece algumas vistas prontas que podem ser usadas para projetar e organizar +o layout. "Widgets" são vistas que fornecem elementos visuais (e interativos) à tela, +como um botão, um campo de texto, uma caixa de seleção ou apenas uma imagem. "Layouts" são vistas derivadas de {@link +android.view.ViewGroup} que oferecem um modelo de layout exclusivo para suas vistas filhas, +como um layout linear, um layout em grade ou um layout relativo. Também é possível definir como subclasse as classes +{@link android.view.View} e {@link android.view.ViewGroup} (ou subclasses existentes) para criar widgets e layouts próprios +e aplicá-los no layout da atividade.

    + +

    A forma mais comum de definir um layout usando vistas é com um arquivo de layout XML salvo +nos recursos do aplicativo. Assim, é possível manter o projeto da interface do usuário separado +do código fonte que define o comportamento da atividade. É possível definir o layout como a IU da atividade +com {@link android.app.Activity#setContentView(int) setContentView()}, passando +o ID de recurso do layout. No entanto, também é possível criar novas {@link android.view.View}s +no código da atividade e compilar uma hierarquia de vistas inserindo novas {@link +android.view.View}s em um {@link android.view.ViewGroup} e, em seguida, usar esse layout passando a raiz +{@link android.view.ViewGroup} para {@link android.app.Activity#setContentView(View) +setContentView()}.

    + +

    Para obter mais informações sobre a criação de uma interface do usuário, consulte a documentação Interface do Usuário.

    + + + +

    Declaração de uma atividade no manifesto

    + +

    É preciso declarar a atividade no arquivo de manifesto para torná-la +acessível para o sistema. Para declarar a atividade, abra o arquivo de manifesto e adicione um elemento {@code <activity>} +como filho do elemento +{@code <application>}. Por exemplo:

    + +
    +<manifest ... >
    +  <application ... >
    +      <activity android:name=".ExampleActivity" />
    +      ...
    +  </application ... >
    +  ...
    +</manifest >
    +
    + +

    Existem outros atributos que podem ser incluídos nesse elemento para definir propriedades +como o rótulo da atividade, um ícone para a atividade ou um tema para estilizar a IU da atividade. + O atributo {@code android:name} + é o único atributo obrigatório — ele especifica o nome de classe da atividade. Depois +de publicar o aplicativo, não se deve alterar-lhe o nome porque, se isso acontecer, podem ocorrer danos +em algumas funcionalidades, como os atalhos do aplicativo (leia a publicação do blogue Coisas +que não podem mudar).

    + +

    Consulte a referência do elemento {@code <activity>} +para obter mais informações sobre como declarar a atividade no manifesto.

    + + +

    Uso de filtros de intenções

    + +

    Um elemento {@code +<activity>} também pode especificar vários filtros de intenções — usando o elemento {@code +<intent-filter>} — para declarar o modo com que os componentes de aplicativo +podem ativá-lo.

    + +

    Ao criar um novo aplicativo com as ferramentas do Android SDK, o esboço da atividade +criado contém automaticamente um filtro de intenção que declara que a atividade responde +à ação "main" (principal) e deve ser colocada na categoria "launcher” (inicializador). O filtro de intenção +tem a seguinte aparência:

    + +
    +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +
    + +

    O elemento {@code +<action>} especifica que este é o “principal” ponto de entrada do aplicativo. O elemento {@code +<category>} especifica que essa atividade deve ser listada no inicializador do aplicativo +do sistema (para permitir que os usuários iniciem essa atividade).

    + +

    Se pretende-se que o aplicativo seja autônomo e que não permita que outros aplicativos ativem +suas atividades, não será necessário nenhum outro filtro de intenção. Só uma atividade deve ter +a ação "main" e a categoria "launcher", como no exemplo anterior. As atividades +que não devem estar disponíveis a outros aplicativos não devem ter filtros de intenção, já que é possível +iniciá-las por meio de intenções explícitas (conforme discutido na seção a seguir).

    + +

    No entanto, se a atividade deve responder a intenções implícitas derivadas de outros aplicativos +(e do aplicativo em questão), é preciso definir filtros de intenções adicionais +para a atividade. Para cada tipo de intenção que deve responder, é preciso incluir um {@code +<intent-filter>} que contenha um elemento +{@code +<action>} e, opcionalmente, um elemento {@code +<category>} e/ou um elemento {@code +<data>}. Esses elementos especificam o tipo de intenção a que a atividade pode +responder.

    + +

    Para obter mais informações sobre a forma com que as atividades podem responder a intenções, consulte o documento +Intenções e filtros de intenções.

    + + + +

    Início de uma atividade

    + +

    Para iniciar outra atividade, é possível chamar {@link android.app.Activity#startActivity +startActivity()} passando uma {@link android.content.Intent} que descreva a atividade +que se deseja iniciar. A intenção especifica a atividade exata que deve ser iniciada ou descreve o tipo +de ação que ela deve executar (e o sistema seleciona a atividade adequada, +que +pode ser até de um outro aplicativo). Uma intenção também pode portar pequenas quantidades de dados +a serem usados pela atividade iniciada.

    + +

    Ao trabalhar no aplicativo, frequentemente será necessário iniciar uma atividade conhecida. + Para isso, pode-se criar uma intenção que defina explicitamente a atividade que deve ser iniciada +por meio de um nome de classe. Por exemplo, a seguir é demonstrado como uma atividade inicia outra atividade de nome {@code +SignInActivity}:

    + +
    +Intent intent = new Intent(this, SignInActivity.class);
    +startActivity(intent);
    +
    + +

    No entanto, o aplicativo também pode ter que executar algumas ações, como enviar um e-mail, +mensagem de texto ou atualização de status usando os dados da atividade em questão. Nesse caso, o aplicativo +pode não ter suas próprias atividades para executar tais ações; para isso, pode-se aproveitar as atividades +fornecidas por outros aplicativos do dispositivo que podem executar essas ações. Esses são os casos +em que as intenções são muito importantes — é possível criar uma intenção que descreva +uma ação a executar para que o sistema +inicie a atividade apropriada de outro aplicativo. Se houver +mais de uma atividade que possa manipular a intenção, o usuário poderá escolher qual usará. Por exemplo: + se quiser que o usuário envie uma mensagem de e-mail, é possível criar +a seguinte intenção:

    + +
    +Intent intent = new Intent(Intent.ACTION_SEND);
    +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
    +startActivity(intent);
    +
    + +

    O {@link android.content.Intent#EXTRA_EMAIL} adicionado à intenção é uma matriz de sequência +de endereços de e-mail para os quais o e-mail poderá ser enviado. Quando um aplicativo de e-mail responde à essa intenção, +ele lê a matriz de sequência fornecida no extra e a coloca no campo "para" +do formulário de composição do e-mail. Nessa situação, a atividade do aplicativo de e-mail inicia e, quando o usuário termina o trabalho, +sua atividade é retomada.

    + + + + +

    Início de uma atividade para um resultado

    + +

    Às vezes, é necessário receber um resultado de alguma atividade iniciada. Nesse caso, + inicie a atividade chamando {@link android.app.Activity#startActivityForResult +startActivityForResult()} (em vez de {@link android.app.Activity#startActivity + startActivity()}). Para receber o resultado de uma atividade +subsequente, implemente o método de retorno de chamada +{@link android.app.Activity#onActivityResult onActivityResult()}. Quando a atividade subsequente estiver concluída, ela retornará um resultado em uma {@link +android.content.Intent} para o método +{@link android.app.Activity#onActivityResult onActivityResult()}.

    + +

    Por exemplo, talvez você queira que o usuário escolha um dos contatos dele, deste modo, a atividade poderá +fazer algo com as informações naquele contato. A seguir expõe-se como criar uma intenção desse tipo +e manipular o resultado:

    + +
    +private void pickContact() {
    +    // Create an intent to "pick" a contact, as defined by the content provider URI
    +    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    +    startActivityForResult(intent, PICK_CONTACT_REQUEST);
    +}
    +
    +@Override
    +protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    +    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    +    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
    +        // Perform a query to the contact's content provider for the contact's name
    +        Cursor cursor = getContentResolver().query(data.getData(),
    +        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
    +        if (cursor.moveToFirst()) { // True if the cursor is not empty
    +            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
    +            String name = cursor.getString(columnIndex);
    +            // Do something with the selected contact's name...
    +        }
    +    }
    +}
    +
    + +

    Esse exemplo mostra a lógica básica que deve ser usada no método {@link +android.app.Activity#onActivityResult onActivityResult()} para manipular +o resultado de uma atividade. A primeira condição verifica se a solicitação foi bem-sucedida — se for, +o {@code resultCode} será {@link android.app.Activity#RESULT_OK} — e se a solicitação +a que esse resultado responderá for desconhecida —, nesse caso, o {@code requestCode} corresponderá +ao segundo parâmetro com {@link android.app.Activity#startActivityForResult +startActivityForResult()}. A partir daí, o código manipula o resultado da atividade com uma consulta +dos dados retornados em uma {@link android.content.Intent} (o parâmetro {@code data}).

    + +

    Nesse momento, um {@link +android.content.ContentResolver} executa uma consulta em um provedor de conteúdo, que retorna +um {@link android.database.Cursor} que permite a leitura dos dados consultados. Para obter mais informações, +consulte o documento Provedores de conteúdo.

    + +

    Para obter mais informações sobre intenções, consulte o documento +Intenções e filtros de intenções.

    + + +

    Encerramento de uma atividade

    + +

    Para encerrar uma atividade, chame o método {@link android.app.Activity#finish +finish()}. Também é possível encerrar uma atividade separada iniciada anteriormente chamando +{@link android.app.Activity#finishActivity finishActivity()}.

    + +

    Observação: na maioria dos casos, não se deve finalizar explicitamente +uma atividade usando esses métodos. Conforme discutido na seção anterior sobre o ciclo de vida da atividade, +o sistema Android gerencia a vida de uma atividade, portanto não é necessário finalizar +as atividades. Chamar esses métodos poderia afetar negativamente a experiência +do usuário esperada e isso só deve ser usado quando realmente não se desejar que o usuário +retorne a essa instância da atividade.

    + + +

    Gerenciamento do ciclo de vida da atividade

    + +

    O gerenciamento do ciclo de vida das atividades por meio da implementação +de métodos de retorno de chamada +é essencial para desenvolver um aplicativo flexível. O ciclo de vida de uma atividade é diretamente afetado +por sua associação a outras atividades, sua tarefa e sua pilha de retorno.

    + +

    Uma atividade pode existir essencialmente em três estados:

    + +
    +
    Retomada
    +
    A atividade está em primeiro plano na tela e tem o foco do usuário (em geral, +chama-se esse estado de "em execução”).
    + +
    Pausada
    +
    A atividade ainda está visível, mas outra atividade está em primeiro plano e tem o foco. Ou seja, +outra atividade está visível por cima desta e está parcialmente transparente +ou não cobre inteiramente a tela. Uma atividade pausada está totalmente ativa (o objeto +{@link android.app.Activity} está retido na memória, mantém todas as informações de estado e do membro e permanece anexado +ao gerenciador de janela), mas pode ser eliminada pelo sistema em situações de memória extremamente baixa.
    + +
    Interrompida
    +
    A atividade está totalmente suplantada por outra (a atividade passa para +"segundo plano"). Uma atividade interrompida ainda está ativa (o objeto +{@link android.app.Activity} está retido na memória, mantém todas as informações de estado e do membro, mas não está +anexado ao gerenciador de janelas). No entanto, ela não fica mais visível para o usuário +e pode ser eliminada pelo sistema se a memória for necessária em outro processo.
    +
    + +

    Se uma atividade estiver pausada ou interrompida, o sistema poderá descartá-la da memória solicitando a +finalização do processo (chamando seu método {@link android.app.Activity#finish finish()}) ou simplesmente +eliminando-o. Quando a atividade for reaberta (depois de finalizada ou eliminada), ele deverá ser +totalmente recriada.

    + + + +

    Implementação de retornos de chamada do ciclo de vida

    + +

    Quando uma atividade transita entre os diferentes estados descritos acima, ela é notificada +por meio de vários métodos de retorno de chamada. Todos os métodos de retorno de chamada são ganchos +que podem ser substituídos para executar um trabalho adequado quando o estado da atividade muda. O esqueleto de atividade +a seguir contém cada um dos métodos do ciclo de vida fundamentais:

    + + +
    +public class ExampleActivity extends Activity {
    +    @Override
    +    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // The activity is being created.
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onStart onStart()} {
    +        super.onStart();
    +        // The activity is about to become visible.
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onResume onResume()} {
    +        super.onResume();
    +        // The activity has become visible (it is now "resumed").
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onPause onPause()} {
    +        super.onPause();
    +        // Another activity is taking focus (this activity is about to be "paused").
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onStop onStop()} {
    +        super.onStop();
    +        // The activity is no longer visible (it is now "stopped")
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onDestroy onDestroy()} {
    +        super.onDestroy();
    +        // The activity is about to be destroyed.
    +    }
    +}
    +
    + +

    Observação: a implementação desses métodos do ciclo de vida +deve sempre chamar a implementação da superclasse antes de realizar qualquer trabalho, conforme ilustrado no exemplo acima.

    + +

    Juntos, esses métodos definem todo o ciclo de vida da atividade. Ao implementá-los, +é possível monitorar três loops aninhados no ciclo de vida da atividade:

    + +
      +
    • Todo o tempo de vida de uma atividade acontece entre a chamada de {@link +android.app.Activity#onCreate onCreate()} e a chamada de {@link +android.app.Activity#onDestroy}. A atividade deve executar configuração +de estado "global" (como definindo layout) em {@link android.app.Activity#onCreate onCreate()} +e liberar todos os recursos restantes em {@link android.app.Activity#onDestroy}. Por exemplo: se a sua atividade +tiver um encadeamento em execução em segundo plano para baixar dados da rede, ela pode +criá-lo em {@link android.app.Activity#onCreate onCreate()} e, em seguida, interrompê-lo em {@link +android.app.Activity#onDestroy}.
    • + +
    • O tempo de vida visível de uma atividade acontece entre a chamada de {@link +android.app.Activity#onStart onStart()} e a chamada de {@link +android.app.Activity#onStop onStop()}. Durante esse tempo, o usuário pode ver a atividade +na tela e interagir com ela. Por exemplo: {@link android.app.Activity#onStop onStop()} é chamado +quando uma nova atividade inicia e esta não fica mais visível. Entre esses dois métodos, é possível +manter os recursos necessários para exibir a atividade ao usuário. Por exemplo: você pode registrar +um {@link android.content.BroadcastReceiver} em {@link +android.app.Activity#onStart onStart()} para monitorar as alterações que afetem a IU e cancelar o registro + em {@link android.app.Activity#onStop onStop()} quando o usuário não puder mais ver +o que você está exibindo. O sistema pode chamar {@link android.app.Activity#onStart onStart()} e {@link +android.app.Activity#onStop onStop()} várias vezes durante todo o tempo de vida de uma atividade +enquanto ela alterna entre visível e oculta ao usuário.

    • + +
    • O tempo de vida em primeiro plano de uma atividade ocorre entre a chamada de {@link +android.app.Activity#onResume onResume()} e a chamada de {@link android.app.Activity#onPause +onPause()}. Durante esse tempo, a atividade está na frente de todas as outras atividades na tela +e tem o foco de interação do usuário. Frequentemente, uma atividade pode transitar entre o primeiro e +o segundo plano — por exemplo, {@link android.app.Activity#onPause onPause()} é chamado quando o dispositivo está em suspensão +ou quando uma caixa de diálogo é exibida. Como esse estado pode transitar frequentemente, o código nesses dois métodos deve +ser bem leve para evitar transições lentas que façam o usuário esperar.

    • +
    + +

    A figura 1 ilustra esses loops e os caminhos que uma atividade pode tomar entre os estados. +Os retângulos representam os métodos de retorno de chamada que podem ser implementados para executar operações +quando a atividade transita entre estados.

    + + +

    Figura 1. Ciclo de vida da atividade.

    + +

    Os mesmos métodos de retorno de chamada do ciclo de vida são listados na tabela 1, que descreve cada um deles +em mais detalhes e localiza cada um dentro +do ciclo de vida geral da atividade, inclusive se o sistema puder eliminar a atividade depois +da conclusão do método de retorno de chamada.

    + +

    Tabela 1. Resumo dos métodos de retorno de chamada +do ciclo de vida da atividade.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Método Descrição Eliminável depois de? Próximo
    {@link android.app.Activity#onCreate onCreate()}Chamado quando a atividade é criada pela primeira vez. + É quando deve-se fazer toda a configuração estática normal — +criar vistas, vincular dados a listas etc. Esse método recebe +um objeto Bundle (pacote) contendo o estado anterior da atividade, se esse +estado for capturado (consulte Gravação do estado da atividade +mais adiante). +

    Sempre seguido de {@code onStart()}.

    Não{@code onStart()}
        {@link android.app.Activity#onRestart +onRestart()}Chamado depois que atividade tiver sido interrompida, logo antes de ser +reiniciada. +

    Sempre seguido de {@code onStart()}.

    Não{@code onStart()}
    {@link android.app.Activity#onStart onStart()}Chamado logo antes de a atividade se tornar visível ao usuário. +

    Seguido de {@code onResume()} se a atividade +for para segundo plano ou {@code onStop()} se ficar oculta.

    Não{@code onResume()}
    ou
    {@code onStop()}
        {@link android.app.Activity#onResume onResume()}Chamado logo antes de a atividade iniciar +a interação com o usuário. Nesse ponto, a atividade estará +no topo da pilha de atividades com a entrada do usuário direcionada a ela. +

    Sempre seguido de {@code onPause()}.

    Não{@code onPause()}
    {@link android.app.Activity#onPause onPause()}Chamado quando o sistema está prestes a retomar +outra atividade. Esse método normalmente é usado para confirmar alterações +não salvas a dados persistentes, animações interrompidas e outras coisas que talvez +estejam consumindo CPU e assim por diante. Ele sempre deve fazer tudo bem rapidamente porque +a próxima atividade não será retomada até ela retornar. +

    Seguido de {@code onResume()} se a atividade +retornar para a frente ou de {@code onStop()} se ficar +invisível ao usuário.

    Sim{@code onResume()}
    ou
    {@code onStop()}
    {@link android.app.Activity#onStop onStop()}Chamado quando a atividade não está mais visível ao usuário. Isso +pode acontecer porque ela está sendo destruída ou porque outra atividade +(uma existente ou uma nova) foi retomada e está cobrindo-a. +

    Seguido de {@code onRestart()} se a atividade +estiver voltando a interagir com o usuário +ou {@code onDestroy()} se estiver saindo.

    Sim{@code onRestart()}
    ou
    {@code onDestroy()}
    {@link android.app.Activity#onDestroy +onDestroy()}Chamado antes de a atividade ser destruída. É a última chamada +que a atividade receberá. Pode ser chamado porque a atividade +está finalizando (alguém chamou {@link android.app.Activity#finish + finish()} nela) ou porque o sistema está destruindo temporariamente essa instância +da atividade para poupar espaço. É possível distinguir +entre essas duas situações com o método {@link + android.app.Activity#isFinishing isFinishing()}.Simnada
    + +

    A coluna de nome "Eliminável depois de?" indica se o sistema pode ou não +eliminar o processo que hospeda a atividade a qualquer momento após o método retornar +sem executar outra linha de código da atividade. Estes três métodos são marcados como "sim": ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()} e {@link android.app.Activity#onDestroy +onDestroy()}). Como {@link android.app.Activity#onPause onPause()} é o primeiro +dos três, assim que a atividade é criada, {@link android.app.Activity#onPause onPause()} é +o último método que certamente será chamado antes que o processo possa ser eliminado — +se o sistema precisar recuperar memória em uma emergência, {@link +android.app.Activity#onStop onStop()} e {@link android.app.Activity#onDestroy onDestroy()} poderão +não ser chamados. Portanto, deve-se usar {@link android.app.Activity#onPause onPause()} para gravar +dados persistentes cruciais (como edições do usuário) no armazenamento. No entanto, deve-se sempre ser seletivo +acerca das informações que devem ser retidas durante {@link android.app.Activity#onPause onPause()} porque +qualquer procedimento de bloqueio nesse método bloqueará a transição para a próxima atividade e retardará +a experiência do usuário.

    + +

    Os métodos marcados como "Não" na coluna Elimináveis protegem o processo que hospeda +a atividade. evitando a eliminação dele no momento em que é chamado. Assim, uma atividade é eliminável +do momento em que {@link android.app.Activity#onPause onPause()} retorna ao momento em que +{@link android.app.Activity#onResume onResume()} é chamado. Ela não será eliminável novamente até que +{@link android.app.Activity#onPause onPause()} seja chamado e retorne novamente.

    + +

    Observação: uma atividade tecnicamente não "eliminável”, por essa definição +na tabela 1, ainda pode ser eliminada pelo sistema — mas isso só ocorreria +em circunstâncias extremas, quando não houvesse outra opção. A possibilidade de uma atividade ser eliminada +é discutida em mais detalhes no documento Processos +e encadeamentos.

    + + +

    Gravação do estado da atividade

    + +

    A introdução a Gerenciamento do ciclo de vida da atividade menciona brevemente +que, +quando uma atividade é pausada ou interrompida, o estado da atividade é retido. Isso acontece porque +o objeto {@link android.app.Activity} continua mantido na memória quando a atividade está pausada +ou interrompida — todas as informações sobre seus membros e estado atual ainda estão ativas. Assim, todas as alterações +feitas pelo usuário dentro da atividade são retidas, de forma que, quando a atividade retornar +ao primeiro plano (quando é "retomada"), essas alterações ainda estejam lá.

    + +

    No entanto, quando o sistema destrói uma atividade para recuperar memória, o objeto {@link +android.app.Activity} é destruído, por isso o sistema não pode simplesmente retomá-la com seu estado +intacto. Em vez disso, o sistema tem que recriar o objeto {@link android.app.Activity} se o usuário +navegar de volta a ele. Ainda assim, o usuário não estará ciente +de que o sistema destruiu a atividade e recriou-a e, assim, provavelmente +esperará que a atividade esteja exatamente como antes. Nessa situação, para garantir que +as informações importantes sobre o estado da atividade sejam preservadas, implementa-se um método +adicional de retorno de chamada que permite salvar as informações sobre o estado da atividade: {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}.

    + +

    O sistema chama {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +antes de deixar a mensagem vulnerável à destruição. O sistema passa a esse método +um {@link android.os.Bundle} no qual é possível salvar +informações de estado acerca da atividade, como pares nome-valor, usando métodos como {@link +android.os.Bundle#putString putString()} e {@link +android.os.Bundle#putInt putInt()}. Em seguida, se o sistema eliminar o processo +do aplicativo e o usuário voltar à atividade, o sistema recriará a atividade e passará +o {@link android.os.Bundle} a {@link android.app.Activity#onCreate onCreate()} e a {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}. Usando qualquer um desses métodos, +é possível extrair o estado salvo de {@link android.os.Bundle} e restaurar +o estado da atividade. Se não houver informações de estado a restaurar, o {@link +android.os.Bundle} passado será nulo (que é o caso quando a atividade é criada +pela primeira vez).

    + + +

    Figura 2. As duas formas pelas quais uma atividade retorna ao foco +do usuário com seu estado intacto: ou a atividade é destruída e recriada em seguida — e ela deve restaurar +o estado salvo anteriormente —, ou a atividade é interrompida e retomada em seguida — e o estado dela +permanece intacto.

    + +

    Observação: não há garantia nenhuma de que {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} será chamado antes de a atividade +ser destruída porque há casos em que não será necessário salvar o estado +(como quando o usuário sai da atividade usando o botão Voltar) porque o usuário está fechando +explicitamente +a atividade). Se o sistema chamar {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, ele o fará antes de {@link +android.app.Activity#onStop onStop()} e possivelmente antes de {@link android.app.Activity#onPause +onPause()}.

    + +

    No entanto, mesmo se você não fizer nada e não implementar {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, parte do estado da atividade +será restaurada pela implementação padrão da classe {@link android.app.Activity} de {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Especificamente, a implementação +padrão chama o método {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} correspondente para cada {@link +android.view.View} no layout, o que permite que cada vista forneça informações próprias sobre o que +deve ser salvo. Quase todo widget na estrutura do Android implementa esse método +conforme o necessário, de forma que qualquer alteração visível na IU seja automaticamente salva e restaurada +ao criar a atividade. Por exemplo, o widget {@link android.widget.EditText} salva qualquer texto +inserido pelo usuário e o widget {@link android.widget.CheckBox} salva independente +de verificação. O único trabalho necessário será fornecer um ID exclusivo (com +o atributo {@code android:id}) para cada widget que for salvar seu estado. Se o widget não salvar nenhum ID, o sistema +não poderá salvar seu estado.

    + + + +

    Embora a implementação padrão de {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} salve informações úteis sobre +a IU da atividade, talvez ainda seja necessário substituí-la para salvar informações adicionais. +Por exemplo: pode ser necessário salvar valores de membro alterados durante a vida da atividade (possivelmente +correlacionados a valores restaurados na IU, mas os membros que retêm esses valores de IU, por padrão, +não são restaurados).

    + +

    Como a implementação padrão de {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} ajuda a salvar o estado da IU, +se o método for substituído para salvar informações de estado adicionais, deve-se sempre chamar a implementação +da superclasse de {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +antes de fazer qualquer trabalho. Da mesma forma, deve-se também chamar a implementação da superclasse de {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} se ela for substituída para que +a implementação padrão possa restaurar estados da vista.

    + +

    Observação: Como nem sempre {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} é chamado, +deve-se usá-lo somente para registrar o estado temporário da atividade (o estado +da IU) — nunca se deve usá-lo para armazenar dados persistentes. Em vez disso, deve-se usar {@link +android.app.Activity#onPause onPause()} para armazenar dados persistentes (como dados que devem ser salvos +em um banco de dados) quando o usuário sair da atividade.

    + +

    Uma boa forma de testar a capacidade do aplicativo de restaurar seu estado é girar +o dispositivo para alterar a orientação da tela. Quando a orientação de tela muda, o sistema +destrói e recria a atividade para aplicar recursos alternativos que podem ser disponibilizados +para a nova configuração de tela. Por esse motivo somente, é muito importante que a atividade +restaure completamente seu estado quando for recriada porque os usuários normalmente giram a tela +ao usar aplicativos.

    + + +

    Manipulação de alterações de configuração

    + +

    Algumas configurações do dispositivo podem mudar em tempo de execução (como A orientação da tela, disponibilidade +do teclado e idioma). Quando ocorre uma alteração, o Android recria a atividade em execução +(o sistema chama {@link android.app.Activity#onDestroy} e, em seguida, chama {@link +android.app.Activity#onCreate onCreate()} imediatamente). Esse comportamento foi projetado +para ajudar o aplicativo a se adaptar a novas configurações recarregando-o automaticamente +com recursos alternativos fornecidos pelo programador (como diferentes layouts +para orientações e tamanhos de telas diferentes).

    + +

    Se você projetar adequadamente a atividade para manipular um reinício devido a uma alteração na orientação da tela +e restaurar o estado da atividade conforme descrito acima, o aplicativo será mais resiliente a outros +eventos inesperados no ciclo de vida da atividade.

    + +

    A melhor forma de manipular um reinício desse tipo +é salvar e restaurar o estado da atividade com {@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()} e {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (ou com {@link +android.app.Activity#onCreate onCreate()}), conforme abordado na seção anterior.

    + +

    Para obter mais informações sobre alterações de configuração que podem ocorrer em tempo de execução e como manipulá-las, +leia o guia em Tratamento de +alterações em tempo de execução.

    + + + +

    Coordenação de atividades

    + +

    Quando uma atividade inicia outra, ambas passam por transições no ciclo de vida. A primeira atividade +é pausada e interrompida (embora ela não seja interrompida se ainda estiver visível em segundo plano) enquanto a outra +atividade é criada. Caso essas atividades compartilhem dados salvos em disco ou em outro lugar, é importante +compreender que a primeira atividade não é totalmente interrompida antes da criação da segunda. +Em vez disso, o processo de iniciar a segunda se sobrepõe ao processo de interromper +a primeira.

    + +

    A ordem dos retornos de chamada do ciclo de vida é bem definida, especialmente quando as duas atividades estão +no mesmo processo e uma está iniciando a outra. A seguir há a ordem das operações que ocorrem quando a atividade A +inicia a atividade B:

    + +
      +
    1. O método {@link android.app.Activity#onPause onPause()} da atividade A é executado.
    2. + +
    3. Os métodos {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart onStart()} e {@link android.app.Activity#onResume onResume()} +da atividade B são executados em sequência (a atividade B agora tem o foco do usuário).
    4. + +
    5. Em seguida, se a atividade A não estiver mais visível na tela, seu método {@link +android.app.Activity#onStop onStop()} é executado.
    6. +
    + +

    Essa sequência previsível de retornos de chamada do ciclo de vida permite gerenciar a transição +de informações de uma atividade para outra. Por exemplo: se você for gravar em um banco de dados o momento em que +a primeira atividade é interrompida para que a atividade a seguir possa lê-lo, é preciso realizar a gravação +no banco de dados durante {@link android.app.Activity#onPause onPause()} e não durante {@link +android.app.Activity#onStop onStop()}.

    + + diff --git a/docs/html-intl/intl/pt-br/guide/components/bound-services.jd b/docs/html-intl/intl/pt-br/guide/components/bound-services.jd new file mode 100644 index 0000000000000000000000000000000000000000..aa024943160f9609acda72f5ae8aeec4fb68fa9c --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=Serviços vinculados +parent.title=Serviços +parent.link=services.html +@jd:body + + +
    +
      +

      Neste documento

      +
        +
      1. Conceitos básicos
      2. +
      3. Criação de um serviço vinculado +
          +
        1. Extensão da classe Binder
        2. +
        3. Uso de um mensageiro
        4. +
        +
      4. +
      5. Vinculação a um serviço
      6. +
      7. Gerenciamento do ciclo de vida de um serviço vinculado
      8. +
      + +

      Classes principais

      +
        +
      1. {@link android.app.Service}
      2. +
      3. {@link android.content.ServiceConnection}
      4. +
      5. {@link android.os.IBinder}
      6. +
      + +

      Exemplos

      +
        +
      1. {@code + RemoteService}
      2. +
      3. {@code + LocalService}
      4. +
      + +

      Veja também

      +
        +
      1. Serviços
      2. +
      +
    + + +

    Um serviço vinculado é o servidor em uma interface servidor-cliente. Um serviço vinculado permite que componentes +(como atividades) sejam vinculados ao serviço, enviem solicitações, recebam respostas e até estabeleçam +comunicação entre processos (IPC). Um serviço vinculado, geralmente, vive somente enquanto serve +outro componente do aplicativo e não é executado em segundo plano indefinidamente.

    + +

    Este documento mostra como criar um serviço vinculado, inclusive como criar vínculos +com o serviço a partir de outros componentes do aplicativo. No entanto, você também deve consultar a documentação Serviços para obter +informações adicionais sobre serviços de forma geral, por exemplo: como enviar notificações de um serviço, +como definir o serviço a ser executado em primeiro plano etc.

    + + +

    Conceitos básicos

    + +

    Um serviço vinculado é uma implementação da classe {@link android.app.Service} que permite +que outros aplicativos sejam vinculados e interajam com ele. Para fornecer a vinculação +a um serviço, você deve implementar o método de retorno de chamada {@link android.app.Service#onBind onBind()}. Este método +retorna um objeto {@link android.os.IBinder} que define a interface de programação +que os clientes podem usar para interagir com o serviço.

    + + + +

    Um cliente pode vincular-se ao serviço chamando {@link android.content.Context#bindService +bindService()}. Quando isto ocorre, é preciso fornecer uma implementação de {@link +android.content.ServiceConnection}, que monitora a conexão com o serviço. O método {@link +android.content.Context#bindService bindService()} retorna imediatamente sem um valor, +mas quando o sistema Android cria a conexão entre +o cliente e o serviço, ele chama {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} em {@link +android.content.ServiceConnection} para fornecer o {@link android.os.IBinder} +que o cliente pode usar para comunicar-se com o serviço.

    + +

    Vários clientes podem conectar-se ao serviço de uma vez. No entanto, o sistema chama o método +{@link android.app.Service#onBind onBind()} do serviço para recuperar o {@link android.os.IBinder} +somente quando o primeiro cliente é vinculado. Em seguida, o sistema entrega o mesmo {@link android.os.IBinder} +para quaisquer clientes adicionais que vincularem-se, sem chamar {@link android.app.Service#onBind onBind()} novamente.

    + +

    Quando o último cliente desvincular-se do serviço, o sistema destruirá o serviço +(a não ser que ele também seja iniciado por {@link android.content.Context#startService startService()}).

    + +

    Ao implementar o serviço vinculado, o mais importante é definir a interface +que o método de retorno de chamada {@link android.app.Service#onBind onBind()} retornará. Há poucas maneiras diferentes +de definir a interface {@link android.os.IBinder} do serviço e a +seção a seguir discute cada técnica.

    + + + +

    Criação de um serviço vinculado

    + +

    Ao criar um serviço que fornece vinculação, você deve fornecer um {@link android.os.IBinder} +que ofereça a interface de programação que os clientes podem usar para interagir com o serviço. Há três maneiras +possíveis de definir a interface:

    + +
    +
    Extensão da classe Binder
    +
    Se o serviço for privado para o próprio aplicativo e for executado no mesmo processo que o cliente +(o que é comum), deve-se criar a interface estendendo-se a classe {@link android.os.Binder} +e retornando uma instância dela a partir de +{@link android.app.Service#onBind onBind()}. O cliente receberá {@link android.os.Binder} +e poderá usá-lo para acessar os métodos públicos disponíveis na implementação de {@link android.os.Binder} +ou até em {@link android.app.Service} diretamente. +

    Esta é a técnica preferencial quando o serviço é meramente um trabalhador de segundo plano +para o aplicativo. O único motivo pelo qual não se criaria a interface desta maneira +é porque o serviço está sendo usado por outros aplicativos ou em processos separados.

    + +
    Uso de um mensageiro
    +
    Caso precise que a interface funcione em diferentes processos, é possível +criar uma interface para o serviço com {@link android.os.Messenger}. Desta maneira, o serviço +define um {@link android.os.Handler} que responde a diferentes tipos de objetos {@link +android.os.Message}. Este {@link android.os.Handler} +é a base para {@link android.os.Messenger}, que pode então compartilhar um {@link android.os.IBinder} +com o cliente, permitindo que ele envie comandos ao serviço usando objetos {@link +android.os.Message}. Além disso, o cliente pode definir o próprio {@link android.os.Messenger} +para que o serviço possa enviar as mensagens de volta. +

    Esta é a maneira mais simples de estabelecer comunicação entre processos (IPC), pois o {@link +android.os.Messenger} coloca todas as solicitações em fila em um único encadeamento para que você não precise +projetar o serviço de modo que seja seguro para encadeamentos.

    +
    + +
    Uso de AIDL
    +
    O AIDL (Android Interface Definition Language, linguagem de definição de interface do Android) realiza todo o trabalho de decomposição de objetos +em primitivos para que o sistema operacional possa entendê-los e dispô-los em processos +para realizar a IPC. A técnica anterior, usando o {@link android.os.Messenger}, tem base em AIDL +como a estrutura fundamental. Como mencionado acima, o {@link android.os.Messenger} cria uma fila +de todas as solicitações de cliente em um único encadeamento para que o serviço receba uma solicitação por vez. Se, no entanto, +você quiser que o serviço lide com várias solicitações simultaneamente, é possível usar a AIDL +diretamente. Neste caso, o serviço deverá ser capaz de realizar vários encadeamentos e ser programado de forma segura para encadeamentos. +

    Para usar a AIDL diretamente, deve-se +criar um arquivo {@code .aidl} que defina a interface de programação. As ferramentas SDK do Android +usam este arquivo para gerar uma classe abstrata que implemente a interface e lide com a IPC, +que pode ser estendida dentro do serviço.

    +
    +
    + +

    Observação: a maioria dos aplicativos não deve usar a AIDL +para criar um serviço vinculado, pois isto requer capacidade de se trabalhar com encadeamentos múltiplos +e pode resultar em uma implementação mais complicada. Portanto, a AIDL não é adequada para a maioria dos aplicativos +e este documento não discute o seu uso para o serviço. Caso tenha certeza de que +precisa usar a AIDL diretamente, consulte a documentação +AIDL.

    + + + + +

    Extensão da classe Binder

    + +

    Se o serviço for usado somente pelo aplicativo local e não precisar trabalhar entre processos, +então será possível implementar a própria classe {@link android.os.Binder} que fornece ao cliente +acesso direto aos métodos públicos no serviço.

    + +

    Observação: isto funciona somente se o cliente e o serviço +estiverem no mesmo aplicativo e processo, o que é muito comum. Por exemplo, isto funcionaria bem para um +aplicativo de música que precise vincular uma atividade ao próprio serviço que está +reproduzindo música em segundo plano.

    + +

    Como configurar:

    +
      +
    1. No serviço, crie uma instância de {@link android.os.Binder} que: +
        +
      • contenha métodos públicos que o cliente possa chamar
      • +
      • retorne ao cliente a instância {@link android.app.Service}, que tenha métodos públicos +que ele possa chamar
      • +
      • ou retorne uma instância de outra classe hospedada pelo serviço com métodos públicos +que o cliente possa chamar
      • +
      +
    2. Retorne esta instância de {@link android.os.Binder} a partir do método de retorno de chamada {@link +android.app.Service#onBind onBind()}.
    3. +
    4. No cliente, receba o {@link android.os.Binder} a partir do método de retorno de chamada {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} +e faça chamadas para o serviços vinculados usando os métodos fornecidos.
    5. +
    + +

    Observação: o motivo pelo qual o serviço e o cliente devem estar no mesmo aplicativo +é para que o cliente possa lançar o objeto retornado e chamar as APIs adequadamente. O serviço e o cliente +também devem estar no mesmo processo, pois esta técnica não possui +nenhuma ingerência entre processos.

    + +

    Por exemplo, a seguir há um serviço que fornece aos clientes acesso aos métodos no serviço +por meio de uma implementação de {@link android.os.Binder}:

    + +
    +public class LocalService extends Service {
    +    // Binder given to clients
    +    private final IBinder mBinder = new LocalBinder();
    +    // Random number generator
    +    private final Random mGenerator = new Random();
    +
    +    /**
    +     * Class used for the client Binder.  Because we know this service always
    +     * runs in the same process as its clients, we don't need to deal with IPC.
    +     */
    +    public class LocalBinder extends Binder {
    +        LocalService getService() {
    +            // Return this instance of LocalService so clients can call public methods
    +            return LocalService.this;
    +        }
    +    }
    +
    +    @Override
    +    public IBinder onBind(Intent intent) {
    +        return mBinder;
    +    }
    +
    +    /** method for clients */
    +    public int getRandomNumber() {
    +      return mGenerator.nextInt(100);
    +    }
    +}
    +
    + +

    O {@code LocalBinder} fornece o método {@code getService()} para que os clientes +recuperem a instância atual de {@code LocalService}. Isto permite que os clientes chamem métodos públicos +no serviço. Por exemplo, eles podem chamar {@code getRandomNumber()} a partir do serviço.

    + +

    Abaixo há uma atividade que vincula-se a {@code LocalService} e chama {@code getRandomNumber()} +quando um botão é pressionado:

    + +
    +public class BindingActivity extends Activity {
    +    LocalService mService;
    +    boolean mBound = false;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +    }
    +
    +    @Override
    +    protected void onStart() {
    +        super.onStart();
    +        // Bind to LocalService
    +        Intent intent = new Intent(this, LocalService.class);
    +        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    +    }
    +
    +    @Override
    +    protected void onStop() {
    +        super.onStop();
    +        // Unbind from the service
    +        if (mBound) {
    +            unbindService(mConnection);
    +            mBound = false;
    +        }
    +    }
    +
    +    /** Called when a button is clicked (the button in the layout file attaches to
    +      * this method with the android:onClick attribute) */
    +    public void onButtonClick(View v) {
    +        if (mBound) {
    +            // Call a method from the LocalService.
    +            // However, if this call were something that might hang, then this request should
    +            // occur in a separate thread to avoid slowing down the activity performance.
    +            int num = mService.getRandomNumber();
    +            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
    +        }
    +    }
    +
    +    /** Defines callbacks for service binding, passed to bindService() */
    +    private ServiceConnection mConnection = new ServiceConnection() {
    +
    +        @Override
    +        public void onServiceConnected(ComponentName className,
    +                IBinder service) {
    +            // We've bound to LocalService, cast the IBinder and get LocalService instance
    +            LocalBinder binder = (LocalBinder) service;
    +            mService = binder.getService();
    +            mBound = true;
    +        }
    +
    +        @Override
    +        public void onServiceDisconnected(ComponentName arg0) {
    +            mBound = false;
    +        }
    +    };
    +}
    +
    + +

    O exemplo acima mostra como o cliente vincula um serviço usando uma implementação +de {@link android.content.ServiceConnection} e o retorno de chamada {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}. A próxima seção +fornece mais informações sobre este processo de vinculação ao serviço.

    + +

    Observação: o exemplo acima não desfaz a vinculação explicitamente a partir do serviço, +mas todos os clientes devem fazê-lo no momento adequado (como quando a atividade pausa).

    + +

    Para obter mais códigos de exemplo, consulte a classe {@code +LocalService.java} e a classe {@code +LocalServiceActivities.java} em ApiDemos.

    + + + + + +

    Uso de um mensageiro

    + + + +

    Caso precise que o serviço comunique-se com processos remotos, é possível usar +o {@link android.os.Messenger} para fornecer a interface ao serviço. Esta técnica permite +estabelecer comunicação entre processos (IPC) sem precisar usar a AIDL.

    + +

    A seguir há um resumo sobre como usar um {@link android.os.Messenger}:

    + +
      +
    • O serviço implementa um {@link android.os.Handler} que recebe um retorno de chamada +para cada chamada de um cliente.
    • +
    • O {@link android.os.Handler} é usado para criar um objeto {@link android.os.Messenger} +(que é uma referência para o {@link android.os.Handler}).
    • +
    • O {@link android.os.Messenger} cria um {@link android.os.IBinder} que o serviço +retorna aos clientes a partir de {@link android.app.Service#onBind onBind()}.
    • +
    • Os clientes usam {@link android.os.IBinder} para instanciar o {@link android.os.Messenger} +(que menciona o {@link android.os.Handler} do serviço), que usam para enviar objetos +{@link android.os.Message} para o serviço.
    • +
    • O serviço recebe cada {@link android.os.Message} em seu {@link +android.os.Handler} — especificamente, no método {@link android.os.Handler#handleMessage +handleMessage()}.
    • +
    + + +

    Desta forma, não há "métodos" para o cliente chamar no serviço. Em vez disso, o cliente +envia "mensagens" (objetos {@link android.os.Message}) que o serviço recebe +no {@link android.os.Handler}.

    + +

    Abaixo há um exemplo simples de serviço que usa uma interface {@link android.os.Messenger}:

    + +
    +public class MessengerService extends Service {
    +    /** Command to the service to display a message */
    +    static final int MSG_SAY_HELLO = 1;
    +
    +    /**
    +     * Handler of incoming messages from clients.
    +     */
    +    class IncomingHandler extends Handler {
    +        @Override
    +        public void handleMessage(Message msg) {
    +            switch (msg.what) {
    +                case MSG_SAY_HELLO:
    +                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
    +                    break;
    +                default:
    +                    super.handleMessage(msg);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Target we publish for clients to send messages to IncomingHandler.
    +     */
    +    final Messenger mMessenger = new Messenger(new IncomingHandler());
    +
    +    /**
    +     * When binding to the service, we return an interface to our messenger
    +     * for sending messages to the service.
    +     */
    +    @Override
    +    public IBinder onBind(Intent intent) {
    +        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
    +        return mMessenger.getBinder();
    +    }
    +}
    +
    + +

    Observe que o método {@link android.os.Handler#handleMessage handleMessage()} +no {@link android.os.Handler} é onde o serviço recebe a {@link android.os.Message} +e decide o que fazer, com base no membro {@link android.os.Message#what}.

    + +

    Tudo que o cliente precisa fazer é criar um {@link android.os.Messenger} com base no {@link +android.os.IBinder} retornado pelo serviço e enviar uma mensagem usando {@link +android.os.Messenger#send send()}. Por exemplo, a seguir há uma atividade simples que vincula-se +ao serviço e envia a mensagem {@code MSG_SAY_HELLO} a ele:

    + +
    +public class ActivityMessenger extends Activity {
    +    /** Messenger for communicating with the service. */
    +    Messenger mService = null;
    +
    +    /** Flag indicating whether we have called bind on the service. */
    +    boolean mBound;
    +
    +    /**
    +     * Class for interacting with the main interface of the service.
    +     */
    +    private ServiceConnection mConnection = new ServiceConnection() {
    +        public void onServiceConnected(ComponentName className, IBinder service) {
    +            // This is called when the connection with the service has been
    +            // established, giving us the object we can use to
    +            // interact with the service.  We are communicating with the
    +            // service using a Messenger, so here we get a client-side
    +            // representation of that from the raw IBinder object.
    +            mService = new Messenger(service);
    +            mBound = true;
    +        }
    +
    +        public void onServiceDisconnected(ComponentName className) {
    +            // This is called when the connection with the service has been
    +            // unexpectedly disconnected -- that is, its process crashed.
    +            mService = null;
    +            mBound = false;
    +        }
    +    };
    +
    +    public void sayHello(View v) {
    +        if (!mBound) return;
    +        // Create and send a message to the service, using a supported 'what' value
    +        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
    +        try {
    +            mService.send(msg);
    +        } catch (RemoteException e) {
    +            e.printStackTrace();
    +        }
    +    }
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +    }
    +
    +    @Override
    +    protected void onStart() {
    +        super.onStart();
    +        // Bind to the service
    +        bindService(new Intent(this, MessengerService.class), mConnection,
    +            Context.BIND_AUTO_CREATE);
    +    }
    +
    +    @Override
    +    protected void onStop() {
    +        super.onStop();
    +        // Unbind from the service
    +        if (mBound) {
    +            unbindService(mConnection);
    +            mBound = false;
    +        }
    +    }
    +}
    +
    + +

    Observe que este exemplo não mostra como o serviço pode responder ao cliente. Caso queira que o serviço responda, +é necessário criar um {@link android.os.Messenger} no cliente também. Em seguida, +quando o cliente receber o retorno de chamada de {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, ele enviará uma {@link android.os.Message} para o serviço que inclui +o {@link android.os.Messenger} no parâmetro {@link android.os.Message#replyTo} +do método {@link android.os.Messenger#send send()}.

    + +

    É possível ver um exemplo de como fornecer mensagens de duas vias nos exemplos {@code +MessengerService.java} (serviço) e {@code +MessengerServiceActivities.java} (cliente).

    + + + + + +

    Vinculação a um serviço

    + +

    Um componente de aplicativo (cliente) pode vincular-se a um serviço chamando +{@link android.content.Context#bindService bindService()}. O sistema Android, em seguida, +chama o método {@link android.app.Service#onBind +onBind()} do serviço, que retorna um {@link android.os.IBinder} para interagir com o serviço.

    + +

    A vinculação é assíncrona. {@link android.content.Context#bindService +bindService()} retorna imediatamente e não retorna o {@link android.os.IBinder} +ao cliente. Para receber {@link android.os.IBinder}, o cliente deve criar uma instância de {@link +android.content.ServiceConnection} e passá-la para {@link android.content.Context#bindService +bindService()}. O {@link android.content.ServiceConnection} inclui um método de retorno de chamada +que o sistema chama para entregar o {@link android.os.IBinder}.

    + +

    Observação: somente atividades, serviços e provedores de conteúdo +podem vincular-se a um serviço — você não pode fazer isto a partir de um receptor de transmissão.

    + +

    Portanto, para vincular-se a um serviço a partir do cliente, deve-se:

    +
      +
    1. Implementar {@link android.content.ServiceConnection}. +

      Sua implementação deve substituir dois métodos de retorno de chamada:

      +
      +
      {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
      +
      O sistema chama isto para entregar o {@link android.os.IBinder} retornado +pelo método {@link android.app.Service#onBind onBind()} do serviço.
      +
      {@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}
      +
      O sistema Android chama isto quando a conexão ao serviço é perdida inesperadamente, + como quando o serviço apresenta problemas ou é fechado repentinamente. Isto não é chamado quando +o cliente desfaz o vínculo.
      +
      +
    2. +
    3. Chamar {@link +android.content.Context#bindService bindService()}, passando a implementação {@link +android.content.ServiceConnection}.
    4. +
    5. Quando o sistema chamar o método de retorno de chamada {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, é possível fazer chamadas para o serviço +usando os métodos definidos pela interface.
    6. +
    7. Para desconectar-se de um serviço, chamar {@link +android.content.Context#unbindService unbindService()}. +

      Quando um cliente é destruído, o vínculo com o serviço acaba. No entanto, sempre desvincule-se +ao terminar a interação com o serviço ou quando a atividade pausar para que o serviço +possa ser encerrado enquanto não estiver em uso (os momentos adequados para vincular ou desvincular +são abordados mais abaixo).

      +
    8. +
    + +

    Por exemplo, o fragmento a seguir conecta o cliente ao serviço criado acima +estendendo a classe Binder para que tudo que ele tenha que fazer seja lançar +o {@link android.os.IBinder} retornado para a classe {@code LocalService} e solicitar a instância de {@code +LocalService}:

    + +
    +LocalService mService;
    +private ServiceConnection mConnection = new ServiceConnection() {
    +    // Called when the connection with the service is established
    +    public void onServiceConnected(ComponentName className, IBinder service) {
    +        // Because we have bound to an explicit
    +        // service that is running in our own process, we can
    +        // cast its IBinder to a concrete class and directly access it.
    +        LocalBinder binder = (LocalBinder) service;
    +        mService = binder.getService();
    +        mBound = true;
    +    }
    +
    +    // Called when the connection with the service disconnects unexpectedly
    +    public void onServiceDisconnected(ComponentName className) {
    +        Log.e(TAG, "onServiceDisconnected");
    +        mBound = false;
    +    }
    +};
    +
    + +

    Com este {@link android.content.ServiceConnection}, o cliente pode vincular-se a um serviço +passando-o para {@link android.content.Context#bindService bindService()}. Por exemplo:

    + +
    +Intent intent = new Intent(this, LocalService.class);
    +bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    +
    + +
      +
    • O primeiro parâmetro de {@link android.content.Context#bindService bindService()} +é uma {@link android.content.Intent} que nomeia explicitamente o serviço que será vinculado (apesar de a intenção +poder ser implícita).
    • +
    • O segundo parâmetro é o objeto {@link android.content.ServiceConnection}.
    • +
    • O terceiro parâmetro é um sinalizador que indica as opções de vinculação. Geralmente, ele deve ser {@link +android.content.Context#BIND_AUTO_CREATE} para criar o serviço se não estiver ativo ainda. +Outros possíveis valores são {@link android.content.Context#BIND_DEBUG_UNBIND} +e {@link android.content.Context#BIND_NOT_FOREGROUND}, ou {@code 0} para nada.
    • +
    + + +

    Observações adicionais

    + +

    A seguir há algumas observações importantes sobre a vinculação com um serviço:

    +
      +
    • Deve-se sempre capturar exceções{@link android.os.DeadObjectException}, que são lançadas +quando a conexão apresenta erros. Esta é a única exceção lançada por métodos remotos.
    • +
    • Objetos são referências contadas em todos os processos.
    • +
    • Geralmente, alterna-se entre a vinculação e a desvinculação +durante os momentos crescentes e decrescentes do ciclo de vida do cliente. Por exemplo: +
        +
      • Caso precise interagir com o serviço enquanto a atividade estiver visível, +deve-se vincular durante {@link android.app.Activity#onStart onStart()} e desvincular durante {@link +android.app.Activity#onStop onStop()}.
      • +
      • Caso queira que a atividade receba mensagens mesmo quando for interrompida +em segundo plano, é possível vincular durante {@link android.app.Activity#onCreate onCreate()} +e desvincular durante {@link android.app.Activity#onDestroy onDestroy()}. Cuidado, pois isto significa que a atividade +precisa usar o serviço durante todo o tempo de execução (mesmo em segundo plano). +Portanto, se o serviço estiver em outro processo, o peso do processo será aumentado +e é mais provável que o sistema elimine-o.
      • +
      +

      Observação: geralmente, não se realiza o vínculo e o seu rompimento +durante o {@link android.app.Activity#onResume onResume()} e o {@link +android.app.Activity#onPause onPause()} da atividade, pois esses retornos de chamada ocorrem em todas as transições do ciclo de vida +e deve-se usar menos processamento possível nessas transações. Além disso, +se várias atividades no aplicativo vincularem-se ao mesmo serviço e houver uma transação entre duas +dessas atividades, o serviço poderá ser destruído e recriado à medida que a atividade desvincula-se, +durante a pausa, antes do próximo vínculo, durante a retomada (esta transição de atividade +para como as atividades coordenam os ciclos de vida é descrita no documento Atividades +).

      +
    + +

    Para obter mais códigos de exemplo mostrando como vincular a um serviço, consulte a classe {@code +RemoteService.java} no ApiDemos.

    + + + + + +

    Gerenciamento do ciclo de vida de um serviço vinculado

    + +

    Quando um serviço é desvinculado de todos os clientes, o sistema Android o destrói (a não ser que ele +também tenha sido inicializado com {@link android.app.Service#onStartCommand onStartCommand()}). Portanto, não é necessário +gerenciar o ciclo de vida do serviço se ele for puramente um serviço vinculado +— o sistema Android o gerencia com base nos vínculos com os clientes.

    + +

    No entanto, se você escolher implementar o método de retorno de chamada {@link android.app.Service#onStartCommand +onStartCommand()}, interrompa o serviço explicitamente, pois o serviço +já terá sido considerado como iniciado. Neste caso, o serviço permanece em execução até +ser interrompido com {@link android.app.Service#stopSelf()} ou outras chamadas de componente {@link +android.content.Context#stopService stopService()}, independente de vínculo +com qualquer cliente.

    + +

    Além disso, se o serviço for iniciado e aceitar vínculos, quando o sistema chamar +o método {@link android.app.Service#onUnbind onUnbind()}, será possível retornar +{@code true} opcionalmente se você quiser receber uma chamada de {@link android.app.Service#onRebind +onRebind()} na próxima vez em que um cliente vincular-se ao serviço (em vez de receber uma chamada de {@link +android.app.Service#onBind onBind()}). {@link android.app.Service#onRebind +onRebind()} retorna vazio, mas o cliente ainda recebe {@link android.os.IBinder} +no retorno de chamada {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}. +Abaixo, a figura 1 ilustra a lógica para este tipo de ciclo de vida.

    + + + +

    Figura 1. O ciclo de vida para um serviço que é iniciado +e também permite vínculos.

    + + +

    Para obter mais informações sobre o ciclo de vida de um serviço iniciado, consulte o documento Serviços.

    + + + + diff --git a/docs/html-intl/intl/pt-br/guide/components/fragments.jd b/docs/html-intl/intl/pt-br/guide/components/fragments.jd new file mode 100644 index 0000000000000000000000000000000000000000..7b1acf9e3439ce87687f833fe15c64a2e661f4cb --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=Fragmentos +parent.title=Atividades +parent.link=activities.html +@jd:body + + + +

    Um {@link android.app.Fragment} representa o comportamento ou uma parte da interface do usuário em um +{@link android.app.Activity}. É possível combinar vários fragmentos em uma única atividade para compilar +uma IU de painéis múltiplos e reutilizar um fragmento em diversas atividades. Um fragmento +é como uma seção modular de uma atividade, que tem o próprio ciclo de vida, recebe os próprios eventos de entrada +e que pode ser adicionado ou removido com a atividade em execução (uma espécie de "sub-atividade" +que pode ser reutilizada em diferentes atividades).

    + +

    Um fragmento deve sempre ser embutido em uma atividade e o ciclo de vida dele +é diretamente impactado pelo ciclo de vida da atividade do host. Por exemplo, quando a atividade é pausada, todos os fragmentos +também são e, quando a atividade é destruída, todos os fragmentos também são. No entanto, enquanto +uma atividade estiver em execução (estiver no estado do ciclo de vida retomado), é possível +manipular cada fragmento independentemente, como adicionar ou removê-los. Ao realizar tal operação com fragmentos, +também é possível adicioná-los a uma pilha de retorno que é gerenciada pela +atividade — cada entrada da pilha de retorno na atividade é um registro da operação de fragmento +que ocorreu. A pilha de retorno permite que o usuário inverta uma operação de fragmento (navegue ao contrário), +pressionando o botão Voltar.

    + +

    Ao adicionar um fragmento como parte do layout da atividade, ele viverá em um {@link +android.view.ViewGroup} dentro da hierarquia de vistas e o fragmento definirá o próprio +layout da vista. +É possível inserir um fragmento no layout, declarando-o no arquivo de layout +da atividade, como um elemento {@code <fragment>} ou a partir do código do aplicativo adicionando-o +a um {@link android.view.ViewGroup} existente. No entanto, não é necessário que um fragmento faça parte +do layout da atividade; também é possível usar um fragmento sem a IU como um trabalhador invisível +da atividade.

    + +

    Este documento descreve como compilar o aplicativo para usar fragmentos, +incluindo como os fragmentos podem manter seu estado ao serem adicionados à pilha de retorno da atividade, +como compartilhar eventos com a atividade e com outros fragmentos da atividade, como contribuir para a barra de ação +da atividade e muito mais.

    + + +

    Filosofia do projeto

    + +

    O Android introduziu os fragmentos no Android 3.0 (API de nível 11), principalmente para suportar +mais projetos de IU flexíveis e dinâmicos em telas grandes, como em tablets. Como a tela de um tablet +é muito maior que a de um celular, há mais espaço para combinar +e alternar componentes da IU. Os fragmentos permitem tais projetos sem haver a necessidade de gerenciar +alterações complexas na hierarquia de vistas. Ao dividir o layout de uma atividade em fragmentos, é possível +modificar a aparência da atividade em tempo de execução e preservar essas alterações na pilha de retorno, +que é gerenciada por esta atividade.

    + +

    Por exemplo, um aplicativo de notícias pode usar um fragmento para exibir uma lista de artigos +na esquerda e outro fragmento para exibir um artigo na direita — ambos os fragmentos aparecem +em uma atividade, lado a lado, e cada fragmento possui o próprio conjunto de métodos de retorno de chamada do ciclo de vida e lidam +com os próprios eventos de entrada do usuário. Portanto, em vez de usar uma atividade para selecionar um artigo +e outra atividade para lê-lo, o usuário pode selecionar um artigo e lê-lo por completo dentro +da mesma atividade, como ilustrado no layout do tablet na figura 1.

    + +

    Você deve projetar cada fragmento como um componente modular e reutilizável da atividade. Ou seja, como cada fragmento +define seu próprio layout e comportamento com os próprios retornos de chamada do ciclo de vida, é possível +incluir um fragmento em várias atividades para poder projetá-lo para reutilização e evitar +a manipulação direta de um fragmento a partir de outro fragmento. Isto é especialmente importante porque +um fragmento modular permite alterar as combinações de fragmentos para tamanhos de tela diferentes. Ao projetar o aplicativo +para ser compatível com tablets e celulares, você poderá reutilizar os fragmentos em diferentes configurações +de layout para otimizar a experiência do usuário com base no espaço de tela disponível. Por exemplo, +em um celular, talvez seja necessário separar os fragmentos para fornecer uma IU de painel único +quando mais de um não se encaixar dentro da mesma atividade.

    + + +

    Figura 1. Um exemplo de como dois módulos de IU definidos +pelos fragmentos podem ser combinados em uma atividade de um projeto para tablet mas separados +em um projeto para celular.

    + +

    Por exemplo, — continuando com o exemplo do aplicativo de notícias — o aplicativo pode embutir +dois fragmentos na atividade A, quando executado em um dispositivo do tamanho de um tablet. No entanto, +em uma tela de tamanho de celular, não há espaço suficiente para ambos os fragmentos, então a atividade A inclui +somente o fragmento da lista de artigos e, quando o usuário seleciona um artigo, ele inicia +a atividade B, que inclui o segundo fragmento para ler o artigo. Portanto, o aplicativo +é compatível com tablets e celulares através da reutilização dos fragmentos em combinações diferentes, como ilustrado +na figura 1.

    + +

    Para obter mais informações sobre o projeto de aplicativos com diferentes combinações de fragmentos +para configurações de tela diferentes, consulte o guia Compatibilidade com tablets e celulares.

    + + + +

    Criação de um fragmento

    + +
    + +

    Figura 2. O ciclo de vida de um fragmento +(enquanto sua atividade está em execução).

    +
    + +

    Para criar um fragmento, é preciso criar uma subclasse de {@link android.app.Fragment} (ou uma respectiva +subclasse existente). A classe {@link android.app.Fragment} tem um código que é muito parecido +com o de uma {@link android.app.Activity}. Ele contém métodos de retorno de chamada semelhantes aos de uma atividade, +como {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()} e {@link android.app.Fragment#onStop onStop()}. Na verdade, +caso esteja convertendo um aplicativo do Android existente para usar os fragmentos, basta mover +o código dos métodos de retorno de chamada da atividade para os respectivos métodos de retorno de chamada +do fragmento.

    + +

    Geralmente, deve-se implementar pelo menos os seguintes métodos de ciclo de vida:

    + +
    +
    {@link android.app.Fragment#onCreate onCreate()}
    +
    O sistema o chama ao criar o fragmento. Dentro da implementação, +deve-se inicializar os componentes essenciais do fragmento que deseja-se reter quando o fragmento +for pausado ou interrompido e, em seguida, retomado.
    +
    {@link android.app.Fragment#onCreateView onCreateView()}
    +
    O sistema chama isto quando é o momento de o fragmento desenhar a interface do usuário +pela primeira vez. Para desenhar uma IU para o fragmento, você deve retornar uma {@link android.view.View} +deste método, que é a raiz do layout do fragmento. É possível retornar como nulo se o fragmento +não fornecer uma IU.
    +
    {@link android.app.Activity#onPause onPause()}
    +
    O sistema chama esse método como o primeiro indício de que o usuário está saindo +do fragmento (embora não seja sempre uma indicação de que o fragmento está sendo destruído). É quando geralmente +deve-se confirmar qualquer alteração que deva se manter além da sessão atual do usuário (porque +o usuário pode não retornar).
    +
    + +

    A maioria dos aplicativos deve implementar pelo menos três destes métodos para cada fragmento, +mas há vários outros métodos de retorno de chamada que você deve usar para lidar com diversos estágios +do ciclo de vida do fragmento. Todos os métodos de retorno de chamada do ciclo de vida são abordados com mais detalhes na seção +Tratamento do ciclo de vida dos fragmentos.

    + + +

    Há também algumas subclasses que você pode querer estender, em vez de a classe {@link +android.app.Fragment} de base:

    + +
    +
    {@link android.app.DialogFragment}
    +
    Exibe uma caixa de diálogo flutuante. Usar esta classe para criar uma caixa de diálogo é uma boa alternativa +para usar métodos auxiliares das caixas de diálogo na classe {@link android.app.Activity}, +pois é possível incorporar uma caixa de diálogo de fragmento na pilha de retorno dos fragmentos gerenciados pela atividade, +permitindo que o usuário retorne a um fragmento dispensado.
    + +
    {@link android.app.ListFragment}
    +
    Exibe uma lista de itens que são gerenciados por um adaptador (como um {@link +android.widget.SimpleCursorAdapter}), semelhante à {@link android.app.ListActivity}. Ele fornece vários métodos +para gerenciar uma vista de lista, como o retorno de chamada {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} +para lidar com eventos de clique.
    + +
    {@link android.preference.PreferenceFragment}
    +
    Exibe uma hierarquia de objetos {@link android.preference.Preference} como uma lista, +parecido com {@link android.preference.PreferenceActivity}. Isto é útil ao criar uma atividade +de "configurações" para o aplicativo.
    +
    + + +

    Adição de uma interface do usuário

    + +

    Um fragmento é geralmente usado como parte de uma interface do usuário da atividade e contribui +para a atividade com seu próprio layout.

    + +

    Para fornecer um layout para um fragmento, você deve implementar o método de retorno de chamada {@link +android.app.Fragment#onCreateView onCreateView()}, que o sistema Android chama +no momento em que o fragmento deve desenhar o layout. A implementação deste método deve retornar +uma {@link android.view.View}, que é a raiz do layout do fragmento.

    + +

    Observação: se o fragmento for uma subclasse de {@link +android.app.ListFragment}, a implementação padrão retornará uma {@link android.widget.ListView} +de {@link android.app.Fragment#onCreateView onCreateView()}, então não será necessário implementá-lo.

    + +

    Para retornar um layout de {@link +android.app.Fragment#onCreateView onCreateView()}, é possível inflá-lo a partir de um recurso de layout definido no XML. Para ajudar +a fazer isto, o {@link android.app.Fragment#onCreateView onCreateView()} fornece +um objeto {@link android.view.LayoutInflater}.

    + +

    Por exemplo, a seguir há uma subclasse de {@link android.app.Fragment} que carrega um layout +do arquivo {@code example_fragment.xml}:

    + +
    +public static class ExampleFragment extends Fragment {
    +    @Override
    +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    +                             Bundle savedInstanceState) {
    +        // Inflate the layout for this fragment
    +        return inflater.inflate(R.layout.example_fragment, container, false);
    +    }
    +}
    +
    + + + +

    O parâmetro {@code container} passado para {@link android.app.Fragment#onCreateView +onCreateView()} é o pai de {@link android.view.ViewGroup} (do layout da atividade) +em que o layout do fragmento +será inserido. O parâmetro {@code savedInstanceState} é um {@link android.os.Bundle} +que fornece dados sobre a instância anterior do fragmento, se o fragmento estiver sendo retomado +(a restauração de estado é abordada mais detalhadamente na seção Tratamento do ciclo +de vida dos fragmentos).

    + +

    O método {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} +usa três argumentos:

    +
      +
    • O ID de recurso do layout que você quer inflar.
    • +
    • O {@link android.view.ViewGroup} que será pai do layout inflado. Passar o {@code +container} é importante para que o sistema aplique os parâmetros de layout à vista raiz +do layout inflado, especificado pela vista pai em que está ocorrendo.
    • +
    • Um booleano que indica se o layout inflado deve ser anexado a {@link +android.view.ViewGroup} (o segundo parâmetro) durante a inflação (neste caso, +isto é falso, pois o sistema já está inserindo o layout inflado no {@code +container} — retornar como verdadeiro criaria um grupo de vistas redundante no layout final).
    • +
    + +

    Este é o modo de criar um fragmento que fornece um layout. A seguir, +é preciso adicionar o fragmento à atividade.

    + + + +

    Adição de um fragmento a uma atividade

    + +

    Geralmente, um fragmento contribui com a atividade do host com uma parte da IU, que é embutida como parte +da hierarquia de vistas geral da atividade. Há duas formas de adicionar um fragmento ao layout +da atividade:

    + +
      +
    • Declarar o fragmento dentro do arquivo de layout da atividade. +

      Neste caso, +é possível especificar as propriedades do layout para o fragmento como se fosse uma vista. Por exemplo, a seguir há o arquivo de layout +para uma atividade com dois fragmentos:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:orientation="horizontal"
      +    android:layout_width="match_parent"
      +    android:layout_height="match_parent">
      +    <fragment android:name="com.example.news.ArticleListFragment"
      +            android:id="@+id/list"
      +            android:layout_weight="1"
      +            android:layout_width="0dp"
      +            android:layout_height="match_parent" />
      +    <fragment android:name="com.example.news.ArticleReaderFragment"
      +            android:id="@+id/viewer"
      +            android:layout_weight="2"
      +            android:layout_width="0dp"
      +            android:layout_height="match_parent" />
      +</LinearLayout>
      +
      +

      O atributo {@code android:name} em {@code <fragment>} especifica a classe {@link +android.app.Fragment} a instanciar no layout.

      + +

      Quando o sistema cria este layout de atividade, ele instancia cada fragmento especificado +no layout e chama o método {@link android.app.Fragment#onCreateView onCreateView()} para cada um +para recuperar o layout de cada fragmento. O sistema insere a {@link android.view.View} retornada +pelo fragmento diretamente no lugar do elemento {@code <fragment>}.

      + +
      +

      Observação: cada fragmento requer um identificador único +que o sistema possa usar para restaurá-lo se a atividade for reiniciada (e que possa ser usado +para capturar o fragmento para realizar operações, como a remoção). Há três maneiras de fornecer +um ID para um fragmento:

      +
        +
      • Fornecer o atributo {@code android:id} com um ID único.
      • +
      • Fornecer o atributo {@code android:tag} com uma string única.
      • +
      • Caso não forneça nenhuma das alternativas anteriores, o sistema usará o ID da vista +do recipiente.
      • +
      +
      +
    • + +
    • Ou adicionar programaticamente o fragmento a um {@link android.view.ViewGroup} existente. +

      A qualquer momento, enquanto a atividade está em execução, é possível adicionar fragmentos ao layout da atividade. Você precisa +apenas especificar um {@link +android.view.ViewGroup} para posicionar o fragmento.

      +

      Para realizar operações de fragmentos na atividade (como adicionar, remover ou substituir +um fragmento), você deve usar APIs de {@link android.app.FragmentTransaction}. É possível adquirir +uma instância de {@link android.app.FragmentTransaction} da {@link android.app.Activity} desta maneira:

      + +
      +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}
      +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
      +
      + +

      É possível adicionar um fragmento usando o método {@link +android.app.FragmentTransaction#add(int,Fragment) add()}, especificando o fragmento que será adicionado +e a vista em que será inserido. Por exemplo:

      + +
      +ExampleFragment fragment = new ExampleFragment();
      +fragmentTransaction.add(R.id.fragment_container, fragment);
      +fragmentTransaction.commit();
      +
      + +

      O primeiro argumento passado para {@link android.app.FragmentTransaction#add(int,Fragment) add()} +é {@link android.view.ViewGroup}, onde o fragmento deve ser colocado, especificado +pelo ID de recurso e o segundo parâmetro é o fragmento a ser adicionado.

      +

      Ao realizar as alterações +com {@link android.app.FragmentTransaction}, +deve-se chamar{@link android.app.FragmentTransaction#commit} para que as alterações entrem em vigor.

      +
    • +
    + + +

    Adição de um fragmento sem IU

    + +

    O exemplo acima mostra como adicionar um fragmento à atividade para fornecer uma IU. No entanto, +é possível também usar um fragmento para fornecer o comportamento de segundo plano para a atividade sem apresentar +IU adicional.

    + +

    Para adicionar um fragmento sem uma IU, adicione o fragmento da atividade usando {@link +android.app.FragmentTransaction#add(Fragment,String)} (fornecendo uma "tag" de string única +para o fragmento, em vez de um ID de vista). Isto adiciona o fragmento, mas, como ele não está associado +a uma vista no layout da atividade, não recebe uma chamada de {@link +android.app.Fragment#onCreateView onCreateView()}. Portanto, não é necessário implementar este método.

    + +

    Fornecer uma tag de string para o fragmento não é algo estrito para fragmentos que não sejam de IU — é possível também +fornecer tags de string para fragmentos que possuam uma IU — mas, se o fragmento não possuir uma IU, +a tag de string é a única maneira de identificá-lo. Caso queira obter o fragmento da atividade posteriormente, +você precisará usar {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

    + +

    Para ver uma atividade de exemplo que usa um fragmento como um trabalhador de segundo plano, sem uma IU, consulte o exemplo de {@code +FragmentRetainInstance.java}, incluso nos exemplos do SDK (disponibilizados pelo +Android SDK Manager) e localizado no sistema como +<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

    + + + +

    Gerenciamento de fragmentos

    + +

    Para gerenciar os fragmentos na atividade, você precisa usar {@link android.app.FragmentManager}. Para adquiri-lo, +chame {@link android.app.Activity#getFragmentManager()} a partir da atividade.

    + +

    Algumas das coisas que você pode fazer com {@link android.app.FragmentManager} incluem:

    + +
      +
    • Adquirir fragmentos existentes na atividade, com {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (para fragmentos que forneçam uma IU +no layout da atividade) ou {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (para fragmentos que forneçam ou não uma IU).
    • +
    • Retire os fragmentos da pilha de retorno com {@link +android.app.FragmentManager#popBackStack()} (simulando um comando de Voltar do usuário).
    • +
    • Registre uma escuta para as alterações na pilha de retorno com {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.
    • +
    + +

    Para obter mais informações sobre esses e outros métodos, consulte a documentação da classe {@link +android.app.FragmentManager}.

    + +

    Como demonstrado na seção anterior, é possível usar o {@link android.app.FragmentManager} +para abrir uma {@link android.app.FragmentTransaction}, que permite realizar operações, +como adicionar e remover fragmentos.

    + + +

    Realização de operações com fragmentos

    + +

    Um grande recurso fornecido por fragmentos em atividades é a possibilidade de adicionar, remover, substituir +e realizar outras ações com eles em resposta à interação do usuário. Cada conjunto de alterações que forem realizadas +na atividade é chamado de operação e podem ser feitas usando APIs em {@link +android.app.FragmentTransaction}. Também é possível salvar cada operação em uma pilha de retorno gerenciada pela atividade, +permitindo que o usuário navegue inversamente por meio de alterações de fragmento (semelhante à navegação +inversa por meio de atividades).

    + +

    É possível adquirir uma instância de {@link android.app.FragmentTransaction} a partir do {@link +android.app.FragmentManager} da seguinte forma:

    + +
    +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()};
    +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
    +
    + +

    Cada operação é um conjunto de alterações que você deseja realizar ao mesmo tempo. É possível definir +todas as alterações desejadas para uma operação usando métodos como {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()} +e {@link android.app.FragmentTransaction#replace replace()}. Em seguida, para aplicar a operação +à atividade, deve-se chamar {@link android.app.FragmentTransaction#commit()}.

    + + +

    Antes de chamar {@link +android.app.FragmentTransaction#commit()}, no entanto, você pode querer chamar {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} para adicionar a operação +a uma pilha de retorno de operações de fragmentos. A pilha de retorno é gerenciada pela atividade +e permite que o usuário retorne ao estado anterior do fragmento, ao pressionar o botão Voltar.

    + +

    Por exemplo, a seguir é apresentado o modo de substituir um fragmento por outro e preservar +o estado anterior da pilha de retorno:

    + +
    +// Create new fragment and transaction
    +Fragment newFragment = new ExampleFragment();
    +FragmentTransaction transaction = getFragmentManager().beginTransaction();
    +
    +// Replace whatever is in the fragment_container view with this fragment,
    +// and add the transaction to the back stack
    +transaction.replace(R.id.fragment_container, newFragment);
    +transaction.addToBackStack(null);
    +
    +// Commit the transaction
    +transaction.commit();
    +
    + +

    Neste exemplo, {@code newFragment} substitui qualquer fragmento (se houver) que estiver +no recipiente do layout identificado pelo ID {@code R.id.fragment_container}. Ao chamar {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, a operação de substituição +é salva na pilha de retorno para que o usuário possa reverter a operação +e voltar ao fragmento anterior pressionando o botão Voltar.

    + +

    Se você adicionar várias alterações à operação (como outro {@link +android.app.FragmentTransaction#add add()} ou {@link android.app.FragmentTransaction#remove +remove()}) e chamar {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, todas as alterações aplicadas +antes de chamar {@link android.app.FragmentTransaction#commit commit()} serão adicionadas +à pilha de retorno como uma única operação e o botão Voltar reverterá todas elas juntas.

    + +

    A ordem em que você adicionar as alterações em {@link android.app.FragmentTransaction} não importa, +exceto que:

    +
      +
    • Você deve chamar {@link android.app.FragmentTransaction#commit()} por último
    • +
    • Caso esteja adicionando vários fragmentos ao mesmo recipiente, a ordem em que +adicioná-los determina a ordem em que eles aparecerão na hierarquia de vistas
    • +
    + +

    Caso você não chame {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()} ao realizar uma operação que remove um fragmento, este fragmento +será destruído quando a operação for realizada e o usuário não poderá navegar de volta a ele. Considerando que, +se você não chamar {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} +ao remover um fragmento, o fragmento será interrompido e será retomado se o usuário +navegar de volta.

    + +

    Dica: para cada operação de fragmento, é possível aplicar uma animação +de transição, chamando {@link android.app.FragmentTransaction#setTransition setTransition()} +antes da realização.

    + +

    Chamar {@link android.app.FragmentTransaction#commit()} não realiza a operação +imediatamente. Em vez disso, o parâmetro agenda a execução no encadeamento da IU da atividade (o encadeamento “main”, ou principal) +assim que possível. Se necessário, no entanto, é possível chamar {@link +android.app.FragmentManager#executePendingTransactions()} a partir do encadeamento da IU para executar imediatamente +as operações enviadas por {@link android.app.FragmentTransaction#commit()}. Tal medida, geralmente, +não é necessária, a não ser que a operação represente uma dependência para trabalhos em outros encadeamentos.

    + +

    Atenção: É possível realizar uma operação usando {@link +android.app.FragmentTransaction#commit commit()} somente antes da atividade salvar +seu estado (quando o usuário deixa a atividade). Caso tente efetivas as alterações após este ponto, +uma exceção será lançada. Isto acontece porque o estado após a efetivação pode ser perdido se a atividade +precisar ser restaurada. Para situações em que não haja problema em perder a efetivação, use {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}.

    + + + + +

    Comunicação com a atividade

    + +

    Apesar de {@link android.app.Fragment} ser implementado como um objeto independente +de uma {@link android.app.Activity} e poder ser usado dentro de várias atividades, uma dada instância +de um fragmento é diretamente vinculada à atividade que o contém.

    + +

    Especificamente, o fragmento pode acessar a instância {@link android.app.Activity} com {@link +android.app.Fragment#getActivity()} e realizar facilmente tarefas como encontrar uma vista +no layout da atividade:

    + +
    +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
    +
    + +

    Do mesmo modo, a atividade pode chamar métodos no fragmento adquirindo uma referência +para o {@link android.app.Fragment} a partir do {@link android.app.FragmentManager} usando {@link +android.app.FragmentManager#findFragmentById findFragmentById()} ou {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Por exemplo:

    + +
    +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
    +
    + + +

    Criação de retornos de chamada de evento para a atividade

    + +

    Em alguns casos, um fragmento que compartilhe eventos com a atividade pode ser necessário. Uma boa maneira de fazer isto +é definir uma interface de retorno de chamada dentro do fragmento e solicitar que a atividade do host +implemente-a. Quando a atividade recebe um retorno de chamada por meio da interface, ela pode compartilhar as informações +com outros fragmentos no layout conforme necessário.

    + +

    Por exemplo, se um aplicativo de notícias em uma atividade tiver dois fragmentos — um para exibir uma lista +de artigos (fragmento A) e outro para exibir um artigo (fragmento B) — então o fragmento A +deve informar à atividade quando um item de lista é selecionado para que ela possa instruir o fragmento B a exibir o artigo. Neste caso, + a interface {@code OnArticleSelectedListener} é declarada dentro do fragmento A:

    + +
    +public static class FragmentA extends ListFragment {
    +    ...
    +    // Container Activity must implement this interface
    +    public interface OnArticleSelectedListener {
    +        public void onArticleSelected(Uri articleUri);
    +    }
    +    ...
    +}
    +
    + +

    Portanto, a atividade que hospeda o fragmento implementa a interface {@code OnArticleSelectedListener} +e suspende +{@code onArticleSelected()} para notificar o fragmento B do evento do fragmento A. Para garantir +que a atividade do host implemente esta interface, o método de retorno de chamada de {@link +android.app.Fragment#onAttach onAttach()} do fragmento A (que o sistema chama ao adicionar +o fragmento à atividade) instanciará {@code OnArticleSelectedListener} +lançando a {@link android.app.Activity}, que é passada para {@link android.app.Fragment#onAttach +onAttach()}:

    + +
    +public static class FragmentA extends ListFragment {
    +    OnArticleSelectedListener mListener;
    +    ...
    +    @Override
    +    public void onAttach(Activity activity) {
    +        super.onAttach(activity);
    +        try {
    +            mListener = (OnArticleSelectedListener) activity;
    +        } catch (ClassCastException e) {
    +            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
    +        }
    +    }
    +    ...
    +}
    +
    + +

    Se a atividade não implementar a interface, o fragmento lançará +{@link java.lang.ClassCastException}. +Se for bem-sucedida, o membro {@code mListener} reterá uma referência da implementação da atividade +de {@code OnArticleSelectedListener}, para que o fragmento A possa compartilhar os eventos com a atividade +chamando métodos definidos pela interface {@code OnArticleSelectedListener}. Por exemplo, se o fragmento A +for uma extensão de {@link android.app.ListFragment}, sempre +que o usuário clicar em um item de lista, o sistema chamará {@link android.app.ListFragment#onListItemClick +onListItemClick()} no fragmento que, em seguida, chamará {@code onArticleSelected()} para compartilhar +o evento com a atividade:

    + +
    +public static class FragmentA extends ListFragment {
    +    OnArticleSelectedListener mListener;
    +    ...
    +    @Override
    +    public void onListItemClick(ListView l, View v, int position, long id) {
    +        // Append the clicked item's row ID with the content provider Uri
    +        Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
    +        // Send the event and Uri to the host activity
    +        mListener.onArticleSelected(noteUri);
    +    }
    +    ...
    +}
    +
    + +

    O parâmetro {@code id} passado para {@link +android.app.ListFragment#onListItemClick onListItemClick()} é o ID da linha do item clicado +que a atividade (ou outro fragmento) usa para resgatar o artigo do {@link +android.content.ContentProvider} do aplicativo.

    + +

    Mais informações sobre +como usar o provedor de conteúdo estão disponíveis na documentação Provedores de conteúdo.

    + + + +

    Adição de itens à barra de ação

    + +

    Os fragmentos podem contribuir com itens de menu para o menu de opções da atividade (e, consequentemente, para a barra de ação) implementando +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Para que este método +receba chamadas, no entanto, você deve chamar {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} durante {@link +android.app.Fragment#onCreate(Bundle) onCreate()} para indicar que o fragmento +gostaria de adicionar itens ao menu de opções (caso contrário, o fragmento não receberá uma chamada +para {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).

    + +

    Quaisquer itens adicionados ao menu de opções do fragmento são anexados +aos itens de menu existentes. O fragmento também recebe retornos de chamada para {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} quando um item de menu +é selecionado.

    + +

    Também é possível registrar uma vista no layout do fragmento para fornecer um menu de contexto chamando {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Quando o usuário +abre o menu de contexto, o fragmento recebe uma chamada de {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}. Quando o usuário seleciona um item, o fragmento recebe uma chamada de {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.

    + +

    Observação: apesar de o fragmento receber um retorno de chamada selecionado no item +para cada item de menu que adiciona, a atividade é a primeira a receber o respectivo retorno de chamada quando o usuário +seleciona um item de menu. Se a implementação da atividade do retorno de chamada selecionado no item não +lida com o item selecionado, o evento é passado para o retorno de chamada do fragmento. Isto é verdadeiro +para o menu de opções e os menus de contexto.

    + +

    Para obter mais informações sobre menus, consulte os guias do desenvolvedor Menus e Barra de ação.

    + + + + +

    Tratamento do ciclo de vida dos fragmentos

    + +
    + +

    Figura 3. O efeito do ciclo de vida da atividade no ciclo de vida +do fragmento.

    +
    + +

    Gerenciar o ciclo de vida de um fragmento é muito parecido com gerenciar o ciclo de vida de uma atividade. Como uma atividade, +um fragmento pode existir em três estados:

    + +
    +
    Retomado
    +
    O fragmento é visível na atividade em execução.
    + +
    Pausado
    +
    Outra atividade está em primeiro plano, mas a atividade em que este fragmento +vive ainda está visível (a atividade de primeiro plano é parcialmente transparente +ou não cobre a tela inteira).
    + +
    Interrompido
    +
    O fragmento não é visível. A atividade do host foi interrompida +ou o fragmento foi removido da atividade mas adicionado à pilha de retorno. Um fragmento interrompido +ainda está vivo (todas as informações do membro e de estado estão retidas no sistema). No entanto, +não está mais visível e será eliminado se a atividade também for.
    +
    + +

    Além disso, como uma atividade, é possível reter o estado de um fragmento usando um {@link +android.os.Bundle}, caso o processo da atividade seja eliminado e você precise restaurar +o estado do fragmento quando a atividade for recriada. É possível salvar o estado durante o retorno de chamada {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} do fragmento e restaurá-lo +durante {@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()} ou {@link +android.app.Fragment#onActivityCreated onActivityCreated()}. Para obter mais informações sobre +como salvar o estado, consulte a documentação Atividades +.

    + +

    A diferença mais significante entre o ciclo de vida de uma atividade e de um fragmento +é o armazenamento em suas respectivas pilhas de retorno. Por padrão, uma atividade é colocada de volta na pilha de retorno de atividades, +que é gerenciada pelo sistema, quando interrompida (para que o usuário possa navegar +a ela com o botão Voltar, como discutido em Tarefas e pilha de retorno). +No entanto, um fragmento é posicionado em uma pilha de retorno gerenciada pela atividade do host somente +ao solicitar explicitamente que a instância seja salva chamando {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} durante uma operação +que remove o fragmento.

    + +

    Caso contrário, gerenciar o ciclo de vida do fragmento é muito semelhante a gerenciar +o ciclo de vida da atividade. Portanto, as mesmas práticas para gerenciar o ciclo de vida +da atividade aplicam-se aos fragmentos. O que você também precisa entender, no entanto, é como a vida +da atividade afeta a vida do fragmento.

    + +

    Atenção: caso precise de um objeto {@link android.content.Context} +dentro do {@link android.app.Fragment}, é possível chamar {@link android.app.Fragment#getActivity()}. +No entanto, tome cuidado para chamar {@link android.app.Fragment#getActivity()} somente quando o fragmento +estiver anexado a uma atividade. Quando o fragmento ainda não estiver anexado, ou tiver sido desvinculado durante o fim +do seu ciclo de vida, {@link android.app.Fragment#getActivity()} retornará como nulo.

    + + +

    Coordenação do ciclo de vida da atividade

    + +

    O ciclo de vida da atividade em que o fragmento vive afeta diretamente o ciclo de vida +do fragmento, da mesma forma que um retorno de chamada de cada ciclo de vida da atividade resulta em um retorno de chamada semelhante +de cada fragmento. Por exemplo, quando a atividade receber {@link android.app.Activity#onPause}, +cada fragmento na atividade receberá {@link android.app.Fragment#onPause}.

    + +

    Os fragmentos têm alguns retornos de chamada do ciclo de vida extras. No entanto, isto trata da interação única +com a atividade para realizar ações como compilar e destruir a IU do fragmento. Esses +métodos adicionais de retorno de chamada são:

    + +
    +
    {@link android.app.Fragment#onAttach onAttach()}
    +
    Chamado quando o fragmento tiver sido associado à atividade ({@link +android.app.Activity} é passado aqui).
    +
    {@link android.app.Fragment#onCreateView onCreateView()}
    +
    Chamado para criar a hierarquia de vistas associada ao fragmento.
    +
    {@link android.app.Fragment#onActivityCreated onActivityCreated()}
    +
    Chamado quando o método {@link android.app.Activity#onCreate +onCreate()} da atividade tiver retornado.
    +
    {@link android.app.Fragment#onDestroyView onDestroyView()}
    +
    Chamado quando a hierarquia de vistas associada ao fragmento estiver sendo removida.
    +
    {@link android.app.Fragment#onDetach onDetach()}
    +
    Chamado quando o fragmento estiver sendo desassociado da atividade.
    +
    + +

    O fluxo do ciclo de vida do fragmento, afetado pela atividade do host, como ilustrado +pela figura 3. Nesta figura, é possível ver como cada estado sucessivo da atividade determina +qual método de retorno de chamada um fragmento pode receber. Por exemplo, quando a atividade recebe o retorno de chamada {@link +android.app.Activity#onCreate onCreate()}, um fragmento na atividade recebe nada mais +do que o retorno de chamada {@link android.app.Fragment#onActivityCreated onActivityCreated()}.

    + +

    Quando a atividade atinge o estado retomado, é possível adicionar e remover fragmentos +dela livremente. Portanto, somente quando a atividade estiver no estado retomado, o ciclo de vida +de um fragmento poderá alterar-se de forma independente.

    + +

    No entanto, quando a atividade deixa o estado retomado, o fragmento é novamente forçado +a passar pelo ciclo de vida pela atividade.

    + + + + +

    Exemplo

    + +

    Para juntar tudo que foi discutido neste documento, abaixo há um exemplo de uma atividade +que usa dois fragmentos para criar um layout de dois painéis. A atividade abaixo inclui um fragmento +para exibir uma lista de títulos de peças de Shakespeare e outra para exibir um resumo da peça, +quando selecionada na lista. Ela também determina como fornecer diferentes configurações de fragmentos, +com base na configuração da tela.

    + +

    Observação: O código fonte completo desta atividade está disponível em +{@code +FragmentLayout.java}.

    + +

    A atividade principal aplica-se de maneira comum, durante {@link +android.app.Activity#onCreate onCreate()}:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +

    O layout aplicado é {@code fragment_layout.xml}:

    + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +

    Usando este layout, o sistema instancia {@code TitlesFragment} (que lista os títulos +das peças) assim que a atividade carrega o layout, enquanto que {@link android.widget.FrameLayout} +(onde o fragmento para exibir o resumo da peça aparecerá) consome o espaço do lado direito da tela, + mas primeiramente permanece vazio. Como verá abaixo, ele não estará visível até que o usuário selecione um item +na lista em que um fragmento esteja posicionado no {@link android.widget.FrameLayout}.

    + +

    No entanto, nem todas as configurações de tela são grandes o suficiente para exibir a lista +de peças e o resumo, lado a lado. Portanto, o layout acima é usado somente para configuração +de tela de paisagem, salvando-o em {@code res/layout-land/fragment_layout.xml}.

    + +

    Por isso, quando a tela está na orientação de retrato, o sistema aplica o seguinte layout, +que é salvo em {@code res/layout/fragment_layout.xml}:

    + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +

    Este layout inclui somente {@code TitlesFragment}. Isto significa que, quando o dispositivo +está na orientação de retrato, somente a lista de título das peças está disponível. Portanto, quando o usuário clicar +em um item da lista nesta configuração, o aplicativo iniciará uma nova atividade para exibir o resumo, +em vez de carregar um segundo fragmento.

    + +

    A seguir, é possível ver como isto é realizado com classes de fragmento. Primeiro, {@code +TitlesFragment}, que exibe a lista de peças de Shakespeare. Este fragmento estende {@link +android.app.ListFragment} e confia nele para lidar com grande parte do trabalho da vista de lista.

    + +

    Enquanto inspeciona este código, observe que há dois possíveis comportamentos quando um usuário +clica em um item de lista: dependendo de qual dos dois layouts está ativo, ele pode criar e exibir +um novo fragmento para exibir os detalhes na mesma atividade (adicionando o fragmento {@link +android.widget.FrameLayout}), ou iniciar uma nova atividade (onde o fragmento possa ser exibido).

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +

    O segundo fragmento, {@code DetailsFragment}, exibe o resumo da peça para o item selecionado +na lista de {@code TitlesFragment}:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +

    Uma nova chamada da classe {@code TitlesFragment}, ou seja, se o usuário clicar em um item de lista +e o layout original não incluir a vista {@code R.id.details} (a que +{@code DetailsFragment} pertence), o aplicativo iniciará a atividade {@code DetailsActivity} +para exibir o conteúdo do item.

    + +

    A seguir há {@code DetailsActivity}, que simplesmente embute {@code DetailsFragment} para exibir +o resumo da peça selecionada quando a tela está na orientação de retrato:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +

    Observe que esta atividade finaliza-se se a configuração for de paisagem, +pois a atividade principal pode tomar o controle e exibir {@code DetailsFragment} juntamente com {@code TitlesFragment}. +Isto pode acontecer se o usuário iniciar {@code DetailsActivity} enquanto estiver na orientação de retrato, +mas alternar para orientação de paisagem (o que reinicia a atividade atual).

    + + +

    Para obter mais exemplos do uso de fragmentos (e arquivos fonte completos deste exemplo), +consulte o aplicativo de exemplo API Demos disponível em +ApiDemos (disponível para download em Exemplos de componentes do SDK).

    + + diff --git a/docs/html-intl/intl/pt-br/guide/components/fundamentals.jd b/docs/html-intl/intl/pt-br/guide/components/fundamentals.jd new file mode 100644 index 0000000000000000000000000000000000000000..47b98458e6429755bb32db773853014f557382c2 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=Fundamentos de aplicativos +@jd:body + + + +

    Os aplicativos do Android são programados em linguagem de programação Java. As ferramentas Android SDK compilam +o código — em conjunto com todos os arquivos de dados e recursos — em um APK, que é o pacote do Android, +um arquivo de arquivamento com sufixo {@code .apk}. Os arquivos de APK contêm todo o conteúdo +de um aplicativo do Android e são os arquivos que os dispositivos desenvolvidos para Android usam para instalar o aplicativo.

    + +

    Depois de instalado em um dispositivo, cada aplicativo do Android é ativado em sua própria área de segurança:

    + +
      +
    • O sistema operacional Android é um sistema Linux multiusuário em que cada aplicativo +é um usuário diferente.
    • + +
    • Por padrão, o sistema atribui a cada aplicativo um ID de usuário do Linux exclusivo (o ID é usado somente +pelo sistema e é desconhecido para o aplicativo). O sistema define permissões para todos os arquivos +em um aplicativo, de modo que somente o ID de usuário atribuído àquele aplicativo pode acessá-los.
    • + +
    • Cada processo tem sua própria máquina virtual (VM), portanto o código de um aplicativo é executado isoladamente +de outros aplicativos.
    • + +
    • Por padrão, cada aplicativo é executado em seu próprio processo Linux. O Android inicia o processo quando é preciso +executar algum componente do aplicativo; em seguida, encerra-o quando não mais é +necessário ou quando o sistema precisa recuperar memória para outros aplicativos.
    • +
    + +

    Assim, o sistema Android implementa o princípio do privilégio mínimo. Ou seja, +cada aplicativo, por padrão, tem acesso somente aos componentes necessários para a execução do seu trabalho +e nada mais. Isso cria um ambiente muito seguro em que o aplicativo não pode acessar partes +do sistema para o qual não tem permissão.

    + +

    No entanto, sempre existe uma maneira de um aplicativo compartilhar dados com outros aplicativos +e acessar serviços do sistema:

    + +
      +
    • É impossível fazer com que dois aplicativos compartilhem o mesmo ID de usuário do Linux, caso em que +eles são capazes de acessar os arquivos um do outro. Para preservar os recursos do sistema, os aplicativos com o mesmo +ID de usuário também podem ser combinados para serem executados no mesmo processo Linux e compartilhar a mesma VM +(também é preciso atribuir o mesmo certificado aos aplicativos).
    • +
    • Um aplicativo pode solicitar permissão para acessar dados de dispositivo como +contatos do usuário, mensagens SMS, o sistema montável (cartão SD), câmera, Bluetooth etc. Todas +as permissões de aplicativo devem ser concedidas pelo usuário no momento da instalação.
    • +
    + +

    Essas são as informações básicas de como um aplicativo do Android existe dentro do sistema. O restante +deste documento apresenta o leitor a:

    +
      +
    • Componentes fundamentais de estrutura que definem o aplicativo.
    • +
    • O arquivo de manifesto em que os componentes são declarados e os recursos de dispositivo necessários +ao aplicativo.
    • +
    • Recursos separados do código do aplicativo que permitem +otimizar o comportamento de uma variedade de configurações de dispositivo.
    • +
    + + + +

    Componentes de aplicativo

    + +

    Os componentes de aplicativo são os blocos de construção fundamentais de um aplicativo do Android. +Cada componente é um ponto diferente pelo qual o sistema pode entrar no aplicativo. Nem todos +os componentes são pontos de entrada reais para o usuário e alguns dependem uns dos outros, mas cada um existe +como uma entidade independente e desempenha uma função específica — cada um é um bloco de construção exclusivo +que ajuda a definir o comportamento geral do aplicativo.

    + +

    Há quatro tipos diferentes de componentes de aplicativo. Cada tipo tem uma finalidade distinta +e tem um ciclo de vida específico que define a forma pela qual o componente é criado e destruído.

    + +

    A seguir apresentam-se os quatro tipos de componentes de aplicativos:

    + +
    + +
    Atividades
    + +
    As atividades representam uma tela única com uma interface do usuário. Por exemplo: +um aplicativo de e-mails pode ter uma atividade que mostra uma lista de novos +e-mails, outra atividade que compõe um e-mail e outra ainda que lê e-mails. Embora +essas atividades funcionem juntas para formar uma experiência de usuário coesa no aplicativo de e-mails, +elas são independentes entre si. Portanto, um aplicativo diferente pode iniciar qualquer uma +dessas atividades (se o aplicativo de e-mails permitir). Por exemplo: um aplicativo de câmera pode iniciar +a atividade no aplicativo de e-mail que compõe o novo e-mail para que o usuário compartilhe uma foto. + +

    Uma atividade é implementada como uma subclasse de {@link android.app.Activity} — saiba mais sobre isso +no guia do desenvolvedor +Atividades.

    +
    + + +
    Serviços
    + +
    Os serviços são componentes executados em segundo plano para realizar operações +de execução longa ou para realizar trabalho para processos remotos. Eles +não apresentam uma interface do usuário. Por exemplo: um serviço pode tocar música em segundo plano +enquanto o usuário está em um aplicativo diferente ou buscar dados na rede sem bloquear +a interação do usuário com uma atividade. Outro componente, como uma atividade, pode iniciar +o serviço e deixá-lo executar ou vincular-se a ele para interagir. + +

    Um serviço é implementado como uma subclasse de {@link android.app.Service} — saiba mais sobre isso +no guia do desenvolvedor +Serviços.

    +
    + + +
    Provedores de conteúdo
    + +
    Os provedores de conteúdo gerenciam um conjunto compartilhado de dados do aplicativo. É possível armazenar os dados +no sistema de arquivos, em um banco de dados SQLite ou em qualquer local de armazenamento persistente +que o aplicativo possa acessar. Por meio do provedor de conteúdo, outros aplicativos podem consultar ou até modificar +os dados (se o provedor de conteúdo permitir). Por exemplo: o sistema Android oferece um provedor +de conteúdo que gerencia as informações de contato do usuário. Assim, qualquer aplicativo +com as permissões adequadas pode consultar parte do provedor de conteúdo (como {@link +android.provider.ContactsContract.Data}) para ler e gravar informações sobre uma pessoa específica. + +

    Os provedores de conteúdo são úteis para ler e gravar dados privados +no aplicativo e não compartilhados. Por exemplo: o exemplo de aplicativo Note Pad usa +um provedor de conteúdo para salvar notas.

    + +

    Um provedor de conteúdo é implementado como uma subclasse de {@link android.content.ContentProvider} +e precisa implementar um conjunto padrão de APIs que permitem a outros aplicativos +realizar transações. Para obter mais informações, consulte o guia de desenvolvedor +Provedores de conteúdo.

    +
    + + +
    Receptores de transmissão
    + +
    Os receptores de transmissão são componentes que respondem a anúncios de transmissão +por todo o sistema. Muitas transmissões se originam do sistema — por exemplo, uma transmissão que anuncia +que uma tela foi desligada, a bateria está baixa ou uma tela foi capturada. +Os aplicativos também podem iniciar transmissões — por exemplo, para comunicar a outros dispositivos +que alguns dados foram baixados no dispositivo e estão disponíveis para uso. Embora os receptores +de transmissão não exibam nenhuma interface do usuário, eles podem criar uma notificação na barra de status +para alertar ao usuário quando ocorre uma transmissão. Mais comumente, no entanto, um receptor de transmissão +é somente um "portal" para outros componentes e realiza uma quantidade mínima de trabalho. Por +exemplo: ele pode iniciar um serviço para executar um trabalho baseado no evento. + +

    Os receptores de transmissão são implementados como subclasses de {@link android.content.BroadcastReceiver} +e cada transmissão é entregue como um objeto {@link android.content.Intent}. Para obter mais informações, +consulte a classe {@link android.content.BroadcastReceiver}.

    +
    + +
    + + + +

    Um aspecto exclusivo do projeto do sistema Android é que qualquer aplicativo pode iniciar +um componente de outro aplicativo. Por exemplo: se você quiser que o usuário capture +uma foto com a câmera do dispositivo, provavelmente haverá outro aplicativo que faz isso +e o seu aplicativo poderá usá-lo, ou seja, não será necessário desenvolver uma atividade para capturar uma foto. Não é +necessário incorporar nem mesmo vinculá-lo do aplicativo da câmera ao código. +Em vez disso, basta iniciar a atividade no aplicativo da câmera que captura +uma foto. Quando concluída, a foto até retorna ao aplicativo em questão para ser usada. Para o usuário, +parece que a câmera é realmente parte do aplicativo.

    + +

    Quando o sistema inicia um componente, ele inicia o processo daquele aplicativo (se ele já +não estiver em execução) e instancia as classes necessárias para o componente. Por exemplo: se o aplicativo +iniciar a atividade no aplicativo da câmera que captura uma foto, aquele aplicativo +é executado no processo que pertence ao aplicativo da câmera e não no processo do aplicativo. +Portanto, ao contrário dos aplicativos na maioria dos outros sistemas, os aplicativos do Android não têm um ponto +de entrada único (não há a função {@code main()}, por exemplo).

    + +

    Como o sistema executa cada aplicativo em um processo separado com permissões de arquivo +que restringem o acesso a outros aplicativos, o aplicativo não pode ativar diretamente um componente +a partir de outro aplicativo. No entanto, o sistema Android pode. Portanto, para ativar um componente +em outro aplicativo, é preciso enviar uma mensagem ao sistema que especifique a intenção +de iniciar um componente específico. Em seguida, o sistema ativa o componente.

    + + +

    Ativação de componentes

    + +

    Três dos quatro tipos de componente — atividades, serviços +e receptores de transmissão — são ativados por uma mensagem assíncrona chamada intenção. +As intenções vinculam componentes individuais entre si em tempo de execução (como +mensageiros que solicitam uma ação de outros componentes), seja o componente pertencente +ao aplicativo ou não.

    + +

    A intenção é criada com um objeto {@link android.content.Intent}, que define uma mensagem +para ativar um componente específico ou um tipo específico de componente — as intenções +podem ser explícitas ou implícitas, respectivamente.

    + +

    Para atividades e serviços, as intenções definem a ação a executar (por exemplo, "exibir" ou +"enviar" algo) e podem especificar a URI dos dados usados na ação (entre outras coisas +que o componente a iniciar precisa saber). Por exemplo: uma intenção pode transmitir uma solicitação +de uma atividade para exibir uma imagem ou abrir uma página da web. Em alguns casos, é preciso iniciar +uma atividade para receber um resultado; nesse caso, a atividade também retorna +o resultado em um {@link android.content.Intent} (por exemplo, é possível emitir uma intenção para +que o usuário selecione um contato pessoal e o retorne a você — a intenção de retorno contém +uma URI que aponta para o contato selecionado).

    + +

    Para receptores de transmissão, a intenção simplesmente define +o anúncio que está sendo transmitido (por exemplo, uma transmissão para indicar que a bateria do dispositivo está acabando +contém uma string de ação conhecida que indica "nível baixo da bateria").

    + +

    O outro tipo de componente, o provedor de conteúdo, não é ativado por intenções. Em vez disso, +ele é ativado por uma solicitação de um {@link android.content.ContentResolver}. O resolvedor +de conteúdo manipula todas as transações diretas com o provedor de conteúdo para que o componente +que executa as transações com o provedor não precise e, em vez disso, chama os métodos no objeto {@link +android.content.ContentResolver}. Isso deixa uma camada de abstração entre o provedor +de conteúdo e o componente que solicita informações (por segurança).

    + +

    Há dois métodos separados para ativar cada tipo de componente:

    +
      +
    • É possível iniciar uma atividade (ou dar-lhe algo novo para fazer) +passando uma {@link android.content.Intent} a {@link android.content.Context#startActivity +startActivity()} ou {@link android.app.Activity#startActivityForResult startActivityForResult()} +(para que, quando desejado, a atividade retorne um resultado).
    • +
    • É possível iniciar um dispositivo (ou dar novas instruções a um serviço em andamento) +passando uma {@link android.content.Intent} a {@link android.content.Context#startService +startService()}. Ou então, é possível vincular ao serviço passando uma{@link android.content.Intent} +a {@link android.content.Context#bindService bindService()}.
    • +
    • É possível iniciar uma transmissão passando uma{@link android.content.Intent} a métodos como +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} ou {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.
    • +
    • É possível iniciar uma consulta a um provedor de conteúdo chamando {@link +android.content.ContentProvider#query query()} em um {@link android.content.ContentResolver}.
    • +
    + +

    Para obter mais informações sobre intenções, consulte o documento +Intenções e filtros de intenções. Veja mais informações sobre a ativação de componentes específicos +nos seguintes documentos: Atividades, Serviços, {@link +android.content.BroadcastReceiver} e Provedores de conteúdo.

    + + +

    O arquivo de manifesto

    + +

    Antes de o sistema Android iniciar um componente de aplicativo, é preciso ler o arquivo {@code AndroidManifest.xml} +(o arquivo de "manifesto") +do aplicativo para que o sistema saiba se o componente existe. O aplicativo precisa declarar todos os seus componentes nesse arquivo, que deve estar na raiz +do diretório do projeto do aplicativo.

    + +

    O manifesto faz outras coisas além de declarar os componentes do aplicativo, +por exemplo:

    +
      +
    • Identifica todas as permissões do usuário de que o aplicativo precisa, como acesso à internet +ou acesso de somente leitura aos contatos do usuário.
    • +
    • Declara o nível de API mínimo +exigido pelo aplicativo com base nas APIs que o aplicativo usa.
    • +
    • Declara os recursos de hardware e software usados ou exigidos pelo aplicativo, como câmera, +serviços de bluetooth ou tela multitoque.
    • +
    • As bibliotecas de API às quais o aplicativo precisa vincular-se (outras além das APIs +de estrutura do Android), como a biblioteca +Google Maps.
    • +
    • E outros.
    • +
    + + +

    Declaração de componentes

    + +

    A principal tarefa do manifesto é informar ao sistema os componentes do aplicativo. Por +exemplo: um arquivo de manifesto pode declarar uma atividade da seguinte forma:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<manifest ... >
    +    <application android:icon="@drawable/app_icon.png" ... >
    +        <activity android:name="com.example.project.ExampleActivity"
    +                  android:label="@string/example_label" ... >
    +        </activity>
    +        ...
    +    </application>
    +</manifest>
    + +

    No elemento <application> +, o atributo {@code android:icon} aponta para recursos de um ícone que identifica +o aplicativo.

    + +

    No elemento <activity>, +o atributo {@code android:name} especifica o nome da classe totalmente qualificada da subclasse de {@link +android.app.Activity} e o atributo {@code android:label} especifica uma string +a usar como a etiqueta da atividade visível ao usuário.

    + +

    É preciso declarar todos os componentes desta forma:

    + + +

    Atividades, serviços e provedores de conteúdo incluídos na fonte, mas não declarados +no manifesto, não ficam visíveis para o sistema e, consequentemente, podem não ser executados. No entanto, receptores +de transmissão +podem ser declarados no manifesto dinamicamente no código (como objetos +{@link android.content.BroadcastReceiver}) e registrados no sistema chamando-se +{@link android.content.Context#registerReceiver registerReceiver()}.

    + +

    Para obter mais informações sobre a estrutura do arquivo de manifesto de aplicativos, consulte a documentação +O arquivo AndroidManifest.xml.

    + + + +

    Declaração de recursos de componentes

    + +

    Conforme abordado acima, em Ativação de componentes, é possível usar +uma {@link android.content.Intent} para iniciar atividades, serviços e receptores de transmissão. Isso pode ser feito +nomeando-se explicitamente o componente-alvo (usando o nome da classe do componente) na intenção. No entanto, +a verdadeira força das intenções reside no conceito de intenções implícitas. As intenções implícitas +descrevem simplesmente o tipo de ação a executar (e, opcionalmente, os dados em que +a ação deve ser executada) e permitem ao sistema encontrar e iniciar um componente no dispositivo que pode executar +a ação. Se houver mais de um componente que possa executar a ação descrita +pela intenção, o usuário selecionará qual deles usar.

    + +

    Para o sistema identificar os componentes que podem responder a uma intenção, compara-se +a intenção recebida com os filtros de intenções fornecidos no arquivo de manifesto de outros aplicativos +do dispositivo.

    + +

    Ao declarar uma atividade no manifesto do aplicativo, pode-se incluir +filtros de intenções que declarem os recursos da atividade para que ela responda +a intenções de outros aplicativos. Para declarar um filtro de intenções no componentes, +adiciona-se um elemento {@code +<intent-filter>} como filho do elemento de declaração do componente.

    + +

    Por exemplo: se você estiver programando um aplicativo de e-mail com uma atividade de compor um novo e-mail, +é possível declarar um filtro de intenções para responder a intenções "enviar" (para enviar um novo e-mail), assim:

    +
    +<manifest ... >
    +    ...
    +    <application ... >
    +        <activity android:name="com.example.project.ComposeEmailActivity">
    +            <intent-filter>
    +                <action android:name="android.intent.action.SEND" />
    +                <data android:type="*/*" />
    +                <category android:name="android.intent.category.DEFAULT" />
    +            </intent-filter>
    +        </activity>
    +    </application>
    +</manifest>
    +
    + +

    Em seguida, se outro aplicativo criar uma intenção com a ação {@link +android.content.Intent#ACTION_SEND} e passá-la para {@link android.app.Activity#startActivity +startActivity()}, o sistema poderá iniciar a atividade de forma que o usuário possa rascunhar e enviar +um e-mail.

    + +

    Para obter mais informações sobre filtros de intenções, consulte o documento Intenções e filtros de intenções. +

    + + + +

    Declaração de requisitos do aplicativo

    + +

    Existem vários dispositivos desenvolvidos para Android e nem todos apresentam os mesmos +recursos e características. Para evitar que o aplicativo seja instalado em dispositivos +que não contenham os recursos que o aplicativo necessita, é importante definir um perfil +para os tipos de dispositivo compatíveis com o aplicativo. É preciso declarar os requisitos de dispositivo e software +no arquivo de manifesto. A maior parte dessas declarações é somente informativa e o sistema não as lê, +mas serviços externos, como o Google Play, as leem para oferecer uma filtragem +aos usuários quando buscam esses aplicativos para seu dispositivo.

    + +

    Por exemplo: se o aplicativo exige uma câmera e usa APIs introduzidas no Android 2.1 (API de nível 7), +deve-se declarar esses requisitos no arquivo de manifesto da seguinte forma:

    + +
    +<manifest ... >
    +    <uses-feature android:name="android.hardware.camera.any"
    +                  android:required="true" />
    +    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    +    ...
    +</manifest>
    +
    + +

    Assim, dispositivos que não tenham câmera e tenham +versão Android anterior a 2.1 não poderão instalar o aplicativo a partir do Google Play.

    + +

    No entanto, também é possível declarar que o aplicativo usa a câmera como recurso +não obrigatório. Nesse caso, o aplicativo precisa definir o atributo {@code required} + como {@code "false"} e verificar em tempo de execução +se o dispositivo tem câmera e desativar os recursos da câmera conforme o necessário.

    + +

    Veja mais informações sobre o gerenciamento da compatibilidade do aplicativo com diferentes dispositivos +no documento Compatibilidade +do dispositivo.

    + + + +

    Recursos do aplicativo

    + +

    Os aplicativos Android são compostos por mais do que somente códigos — eles requerem recursos +separados do código-fonte, como imagens, arquivos de áudio e tudo o que se relaciona +com a apresentação visual do aplicativo. Por exemplo: deve-se definir animações, menus, estilos, cores +e o layout das interfaces do usuário da atividade com arquivos XML. O uso de recursos de aplicativo facilita +a atualização de diversas características do aplicativo sem a necessidade de modificar +o código e — fornecendo conjuntos de recursos alternativos — permite otimizar o aplicativo +para diversas configurações de dispositivo (como idiomas e tamanhos de tela diferentes).

    + +

    Para todo recurso incluído no projeto Android, as ferramentas de programação SDK definem +um ID inteiro exclusivo que o programador pode usar para referenciar o recurso do código do aplicativo +ou de outros recursos definidos no XML. Por exemplo: se o aplicativo contiver um arquivo de imagem de nome {@code +logo.png} (salvo no diretório {@code res/drawable/}), as ferramentas de SDK gerarão um ID de recurso +chamado {@code R.drawable.logo}, que pode ser usado para referenciar a imagem e inseri-la +na interface do usuário.

    + +

    Um dos aspectos mais importantes de fornecer recursos separados do código-fonte +é a capacidade de fornecer recursos alternativos para diferentes configurações +de dispositivo. Por exemplo: ao definir strings de IU em XML, é possível converter as strings em outros +idiomas e salvá-las em arquivos separados. Em seguida, com base em um qualificador de idioma +acrescentado ao nome do diretório do recurso (como {@code res/values-fr/} para valores +de string em francês) e a configuração de idioma do usuário, o sistema Android aplica as strings de idioma adequadas +à IU.

    + +

    O Android aceita vários qualificadores para recursos alternativos. O qualificador +é uma string curta incluída no nome dos diretórios de recurso para definir +a configuração de dispositivo em que esses recursos serão usados. Outro exemplo: +deve-se criar diferentes layouts para as atividades conforme a orientação +e o tamanho da tela do dispositivo. Por exemplo: quando a tela do dispositivo está em orientação +retrato (vertical), pode ser desejável um layout com botões na vertical, mas, quando a tela está em orientação +paisagem (horizontal), os botões devem estar alinhados horizontalmente. Para alterar o layout +conforme a orientação, pode-se definir dois layouts diferentes e aplicar o qualificador +adequado ao nome do diretório de cada layout. Em seguida, o sistema aplica automaticamente o layout adequado +conforme a orientação atual do dispositivo.

    + +

    Para obter mais informações sobre os diferentes tipos de recursos a incluir no aplicativo +e como criar recursos alternativos para diferentes configurações de dispositivo, leia Como fornecer recursos.

    + + + +
    +
    +

    Continue lendo sobre:

    +
    +
    Intenções e filtros de intenções +
    +
    Informações sobre o uso de APIs {@link android.content.Intent} para +ativar componentes de aplicativos, como atividades e serviços, e como disponibilizar componentes +de aplicativo para uso em outros aplicativos.
    +
    Atividades
    +
    Informações sobre a criação de uma instância da classe {@link android.app.Activity}, +que permite uma tela diferente no aplicativo com uma interface do usuário.
    +
    Como fornecer recursos
    +
    Informações sobre a estrutura de aplicativos Android para recursos de aplicativo separados do código-fonte +, inclusive como fornecer recursos alternativos para configurações +de dispositivo específicas. +
    +
    +
    +
    +

    Você também pode se interessar por:

    +
    +
    Compatibilidade do dispositivo
    +
    Informações sobre como o Android funciona em diferentes tipos de dispositivo e uma introdução +a como otimizar o aplicativo para cada dispositivo ou restringir a disponibilidade +a diferentes dispositivos.
    +
    Permissões do sistema
    +
    Informações sobre como o aplicativo restringe o acesso do Android a determinadas APIs sem um +sistema de permissão que exija o consentimento do usuário para que o aplicativo use essas APIs.
    +
    +
    +
    + diff --git a/docs/html-intl/intl/pt-br/guide/components/index.jd b/docs/html-intl/intl/pt-br/guide/components/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..02fcaa63c63a78d5b3982a429e71ee11f6037561 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=Componentes do aplicativo +page.landing=true +page.landing.intro=A estrutura de aplicativo do Android permite criar aplicativos ricos e inovadores usando um conjunto de componentes reutilizáveis. Esta seção explica como criar os componentes que definem os blocos de construção do aplicativo e como conectá-los usando intenções. +page.metaDescription=A estrutura de aplicativo do Android permite criar aplicativos ricos e inovadores usando um conjunto de componentes reutilizáveis. Esta seção mostra como criar os componentes que definem os blocos de construção do aplicativo e como conectá-los usando intenções. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +
    + + + + + +
    diff --git a/docs/html-intl/intl/pt-br/guide/components/intents-filters.jd b/docs/html-intl/intl/pt-br/guide/components/intents-filters.jd new file mode 100644 index 0000000000000000000000000000000000000000..80e3f08d375d40e3e9504d897fb16182ac2984d5 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Intenções e filtros de intenções +page.tags="IntentFilter" +@jd:body + + + + + + +

    A {@link android.content.Intent} é um objeto de mensagem que pode ser usado para solicitar uma ação +de outro componente de aplicativo. +Embora as intenções facilitem a comunicação entre componentes de diversos modos, há três +casos de uso fundamentais:

    + +
      +
    • Para iniciar uma atividade: +

      A {@link android.app.Activity} representa uma única tela em um aplicativo. É possível iniciar uma nova +instância de uma {@link android.app.Activity} passando uma {@link android.content.Intent} +a {@link android.content.Context#startActivity startActivity()}. A {@link android.content.Intent} +descreve a atividade a iniciar e carrega todos os dados necessários.

      + +

      Se você deseja receber um resultado da atividade quando ela finalizar, +chame {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Sua atividade recebe o resultado +como um objeto {@link android.content.Intent} separado no retorno de chamada de {@link +android.app.Activity#onActivityResult onActivityResult()} da atividade. +Para obter mais informações, consulte o guia Atividades.

    • + +
    • Para iniciar um serviço: +

      O {@link android.app.Service} é um componente que realiza operações em segundo plano +sem interface de usuário. É possível iniciar um serviço para realizar uma operação que acontece uma vez +(como baixar um arquivo) passando uma {@link android.content.Intent} +a {@link android.content.Context#startService startService()}. A {@link android.content.Intent} +descreve o serviço a iniciar e carrega todos os dados necessários.

      + +

      Se o serviço for projetado com uma interface servidor-cliente, é possível vincular ao serviço +a partir de outro componente passando uma {@link android.content.Intent} a {@link +android.content.Context#bindService bindService()}. Para obter mais informações, consulte o guia Serviços.

    • + +
    • Para fornecer uma transmissão: +

      Transmissão é uma mensagem que qualquer aplicativo pode receber. O sistema fornece diversas +transmissões para eventos do sistema, como quando o sistema inicializa ou o dispositivo inicia o carregamento. +Você pode fornecer uma transmissão a outros aplicativos passando uma {@link android.content.Intent} +a {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()} ou {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.

      +
    • +
    + + + + +

    Tipos de intenções

    + +

    Há dois tipos de intenções:

    + +
      +
    • As intenções explícitas especificam o componente a iniciar pelo nome +(o nome de classe totalmente qualificado). Normalmente, usa-se uma intenção explícita para iniciar um componente +no próprio aplicativo porque se sabe o nome de classe da atividade ou serviço que se deseja iniciar. +Por exemplo, iniciar uma nova atividade em resposta a uma ação do usuário ou iniciar um serviço para baixar +um arquivo em segundo plano.
    • + +
    • As intenções implícitas não nomeiam nenhum componente específico, mas declaram uma ação geral +a realizar, o que permite que um componente de outro aplicativo a trate. Por exemplo: se você deseja +exibir ao usuário uma localização em um mapa, pode usar uma intenção implícita para solicitar que outro aplicativo +capaz exiba uma localização especificada no mapa.
    • +
    + +

    Ao criar uma intenção explícita para iniciar uma atividade ou serviço, o sistema inicia imediatamente +o componente do aplicativo especificado no objeto {@link android.content.Intent} .

    + +
    + +

    Figura 1. Ilustração de como uma intenção implícita +é fornecida pelo sistema para iniciar outra atividade: [1] A atividade A cria uma +{@link android.content.Intent} com uma descrição de ação e passa-a para {@link +android.content.Context#startActivity startActivity()}. [2] O sistema Android busca, em todos +os aplicativos, um filtro de intenções que corresponda à intenção. Ao encontrar uma correspondência, [3] o sistema +inicia a atividade correspondente (atividade B) chamando seu método {@link +android.app.Activity#onCreate onCreate()} e passando a {@link android.content.Intent} para ele. +

    +
    + +

    Ao criar uma intenção implícita, o sistema Android encontra o componente adequado para iniciar, +comparando o conteúdo da intenção aos filtros de intenções declarados no arquivo de manifesto de outros aplicativos +no dispositivo. Se a intenção corresponder a um filtro de intenções, o sistema iniciará esse componente e entregará +o objeto {@link android.content.Intent}. Se diversos filtros de intenções corresponderem, o sistema +exibirá uma caixa de diálogo para que o usuário selecione o aplicativo que deseja usar.

    + +

    O filtro de intenções é uma expressão em um arquivo de manifesto do aplicativo +que especifica o tipo de intenções que o componente +gostaria de receber. Por exemplo, ao declarar um filtro de intenções para uma atividade, +outros aplicativos se tornam capazes de iniciar diretamente sua atividade com o determinado tipo de intenção. +Do mesmo modo, se você não declarar nenhum filtro de intenções para uma atividade, ela poderá ser iniciada +somente com uma intenção explícita.

    + +

    Atenção: para garantir a segurança do seu aplicativo, sempre use uma intenção +explícita ao iniciar um {@link android.app.Service} e não +declare filtros de intenções para os serviços. O uso de uma intenção implícita para iniciar um serviço representa +um risco de segurança porque não é possível determinar qual serviço responderá à intenção +e o usuário não poderá ver que serviço é iniciado. A partir do Android 5.0 (API de nível 21), o sistema +lança uma exceção ao chamar {@link android.content.Context#bindService bindService()} +com uma intenção implícita.

    + + + + + +

    Criação de uma intenção

    + +

    Um objeto {@link android.content.Intent} carrega informações que o sistema Android usa +para determinar o componente a iniciar (como o nome exato do componente ou categoria +do componente que deve receber a intenção), além de informações que o componente receptor usa para +realizar a ação adequadamente (como a ação a tomar e os dados a usar).

    + + +

    As informações principais contidas em uma {@link android.content.Intent} são as seguintes:

    + +
    + +
    Nome do componente
    +
    O nome do componente a iniciar. + +

    É opcional, mas é a informação fundamental que torna uma intenção +explícita, o que significa que a intenção deve ser entregue somente ao componente do aplicativo +definido pelo nome do componente. Sem nome de componente, a intenção será implícita +e o sistema decidirá qual componente deve receber a intenção, com base nas informações de outra intenção +(como a ação, os dados e a categoria — descritos abaixo). Portanto, se for necessário iniciar +um componente específico no seu aplicativo, deve-se especificar o nome do componente.

    + +

    Observação: ao iniciar um {@link android.app.Service}, deve-se +sempre especificar o nome do componente. Caso contrário, não será possível determinar qual serviço +responderá à intenção e o usuário não poderá ver que serviço é iniciado.

    + +

    Esse campo da {@link android.content.Intent} é um +objeto {@link android.content.ComponentName} que pode ser especificado usando um nome +de classe totalmente qualificado do componente-alvo, inclusive o nome do pacote do aplicativo. Por exemplo, +{@code com.example.ExampleActivity}. É possível definir o nome do componente com {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()} ou com +o construtor de {@link android.content.Intent}.

    + +
    + +

    Ação
    +
    String que especifica a ação genérica a realizar (como exibir ou selecionar). + +

    No caso de uma intenção de transmissão, essa é a ação que entrou em vigor e que está sendo relatada. +A ação determina amplamente como o resto da intenção é estruturado — especificamente, +o que está contido nos dados e em extras. + +

    É possível especificar as ações para uso por intenções dentro do aplicativo (ou para uso por outros +aplicativos para chamar componentes no seu aplicativo), mas normalmente usam-se constantes de ação +definidas pela classe {@link android.content.Intent} ou por outras classes de estrutura. A seguir há algumas +ações comuns para iniciar uma atividade:

    + +
    +
    {@link android.content.Intent#ACTION_VIEW}
    +
    Use essa ação em uma intenção com {@link + android.content.Context#startActivity startActivity()} quando houver informações que + uma atividade possa exibir ao usuário, como uma foto para exibição em um aplicativo de galeria ou um endereço + para exibição em um aplicativo de mapa.
    + +
    {@link android.content.Intent#ACTION_SEND}
    +
    Também conhecida como a intenção de "compartilhamento", ela deve ser usada em uma intenção com {@link + android.content.Context#startActivity startActivity()} quando houver alguns dados que o usuário possa + compartilhar por meio de outro aplicativo, como um aplicativo de e-mail ou de compartilhamento social.
    +
    + +

    Consulte a referência da classe {@link android.content.Intent} para obter mais +constantes que definem ações genéricas. Outras ações são definidas +em outros locais na estrutura do Android, como nas {@link android.provider.Settings} para ações +que abrem telas específicas no aplicativo de Configurações do sistema.

    + +

    É possível especificar a ação para uma intenção com {@link android.content.Intent#setAction +setAction()} ou com um construtor de {@link android.content.Intent}.

    + +

    Se você definir as próprias ações, certifique-se de incluir o nome do pacote do seu aplicativo +como prefixo. Por exemplo:

    +
    static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
    +
    + +
    Dados
    +
    A URI (um objeto {@link android.net.Uri}) que referencia os dados a serem aproveitados e/ou o +tipo MIME desses dados. O tipo dos dados fornecidos geralmente é determinado pela ação da intenção. +Por exemplo: se a ação for {@link android.content.Intent#ACTION_EDIT}, os dados devem conter +a URI do documento a editar. + +

    Ao criar uma intenção, +em geral, é importante especificar o tipo de dados (seu tipo MIME) em adição à URI. +Por exemplo: uma atividade capaz de exibir imagens provavelmente não será capaz +de reproduzir um arquivo de áudio, mesmo que os formatos da URI sejam similares. +Portanto, especificar o tipo MIME dos dados ajuda o sistema +Android a encontrar o melhor componente para receber a intenção. +Contudo, o tipo MIME, às vezes, pode ser inferido a partir da URI — especificamente quando os dados são +uma URI de {@code content:}, indicando que os dados se localizam no dispositivo e são controlados +por um {@link android.content.ContentProvider}, que torna o tipo MIME dos dados visíveis para o sistema.

    + +

    Para definir somente a URI de dados, chame {@link android.content.Intent#setData setData()}. +Para definir somente o tipo MIME, chame {@link android.content.Intent#setType setType()}. Se necessário, +é possível definir ambos explicitamente com {@link +android.content.Intent#setDataAndType setDataAndType()}.

    + +

    Atenção: se você deseja definir a URI e o tipo MIME, +não chame {@link android.content.Intent#setData setData()} +e {@link android.content.Intent#setType setType()}, pois eles anulam seus valores mutuamente. +Sempre use {@link android.content.Intent#setDataAndType setDataAndType()} para definir +a URI e o tipo MIME juntos.

    +
    + +

    Categoria
    +
    String que contém informações adicionais sobre o tipo de componente +que deve tratar da intenção. Qualquer número de descrições de categoria pode ser +inserido em uma intenção, mas a maioria das intenções não requer nenhuma categoria. +A seguir há algumas categorias comuns: + +
    +
    {@link android.content.Intent#CATEGORY_BROWSABLE}
    +
    A atividade-alvo permite seu início por um navegador da web para exibir dados + referenciados por um link — como uma imagem ou uma mensagem de e-mail. +
    +
    {@link android.content.Intent#CATEGORY_LAUNCHER}
    +
    A atividade é a atividade inicial de uma tarefa e é listada + no inicializador do aplicativo do sistema. +
    +
    + +

    Consulte a descrição da classe {@link android.content.Intent} para obter a lista completa +de categorias.

    + +

    É possível especificar uma categoria com {@link android.content.Intent#addCategory addCategory()}.

    +
    +
    + + +

    As propriedades listadas abaixo (nome do componente, ação, dados e categoria) representam +as características de definição de uma intenção. Ao ler estas propriedades, o sistema Android +será capaz de definir o componente de aplicativo que ele deve iniciar.

    + +

    Contudo, uma intenção pode carregar informações adicionais que não afetam +o modo com que é tratada para um componente do aplicativo. As intenções também podem fornecer:

    + +
    +
    Extras
    +
    Pares de valores-chave que carregam informações adicionais exigidas para realizar a ação solicitada. +Assim, como algumas ações usam determinados tipos de URIs de dados, outras também usam determinados extras. + +

    É possível adicionar dados extras com diversos métodos {@link android.content.Intent#putExtra putExtra()}, +cada um aceitando dois parâmetros: o nome principal e o valor. +Também é possível criar um objeto {@link android.os.Bundle} com todos os dados extras e, em seguida, inserir +o {@link android.os.Bundle} na {@link android.content.Intent} com {@link +android.content.Intent#putExtras putExtras()}.

    + +

    Por exemplo: ao criar uma intenção para enviar um e-mail com +{@link android.content.Intent#ACTION_SEND}, é possível especificar o recipiente "para" com +a chave {@link android.content.Intent#EXTRA_EMAIL} e especificar o "assunto" com +a chave {@link android.content.Intent#EXTRA_SUBJECT}.

    + +

    A classe {@link android.content.Intent} especifica diversas constantes {@code EXTRA_*} +para tipos de dados padronizados. Se for necessário declarar chaves extras (para intenções que +seu aplicativo receba), certifique-se de incluir o nome do pacote do aplicativo +como prefixo. Por exemplo:

    +
    static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
    +
    + +
    Sinalizadores
    +
    Sinalizadores definidos na classe {@link android.content.Intent} que funcionam como metadados +para a intenção. Os sinalizadores podem instruir o sistema Android sobre como inicializar uma atividade (por exemplo, a qual +tarefa a atividade deve +pertencer) e como tratá-la após sua inicialização (por exemplo, se ela pertencer a uma lista de atividades +recentes). + +

    Para obter mais informações, consulte o método {@link android.content.Intent#setFlags setFlags()}.

    +
    + +
    + + + + +

    Exemplo de intenção explícita

    + +

    A intenção explícita é usada para inicializar um componente específico de um aplicativo, como +uma atividade ou serviço em particular, no seu aplicativo. Para criar uma intenção explícita, defina +o nome do componente para o objeto {@link android.content.Intent} — todas +as outras propriedades da intenção são opcionais.

    + +

    Por exemplo: se você criar um serviço no aplicativo, chamado {@code DownloadService} e +projetado para baixar um arquivo da web, poderá iniciá-lo com o código a seguir:

    + +
    +// Executed in an Activity, so 'this' is the {@link android.content.Context}
    +// The fileUrl is a string URL, such as "http://www.example.com/image.png"
    +Intent downloadIntent = new Intent(this, DownloadService.class);
    +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl));
    +startService(downloadIntent);
    +
    + +

    O construtor {@link android.content.Intent#Intent(Context,Class)} + fornece {@link android.content.Context} ao aplicativo +e um objeto {@link java.lang.Class} ao componente. Assim, +essa intenção inicia explicitamente a classe {@code DownloadService} no aplicativo.

    + +

    Para obter mais informações sobre a criação e inicialização de um serviço, consulte +o guia Serviços.

    + + + + +

    Exemplo de intenção implícita

    + +

    A intenção implícita especifica uma ação que possa chamar qualquer aplicativo no dispositivo capaz +de realizar a ação. A intenção implícita é útil quando o aplicativo não pode realizar +a ação, mas outros aplicativos provavelmente podem e o usuário seleciona que aplicativo usar.

    + +

    Por exemplo: se você tem o conteúdo que deseja que o usuário compartilhe com outras pessoas, crie uma intenção +com a ação {@link android.content.Intent#ACTION_SEND} +e adicione extras que especifiquem o conteúdo a compartilhar. Ao chamar +{@link android.content.Context#startActivity startActivity()} com esta intenção, o usuário poderá +selecionar um aplicativo pelo qual deseja compartilhar o conteúdo.

    + +

    Atenção: é possível que um usuário não tenha nenhum +aplicativo que trate da intenção implícita enviada a {@link android.content.Context#startActivity +startActivity()}. Se isso acontecer, a chamada e seu aplicativo falharão. Para verificar +se uma atividade receberá a intenção, chame {@link android.content.Intent#resolveActivity +resolveActivity()} no objeto {@link android.content.Intent}. Se o resultado não for nulo, +há pelo menos um aplicativo que pode tratar da intenção e será seguro chamar +{@link android.content.Context#startActivity startActivity()}. Se o resultado for nulo, +você não deve usar a intenção e, se possível, deve desativar o recurso que emite +a intenção.

    + + +
    +// Create the text message with a string
    +Intent sendIntent = new Intent();
    +sendIntent.setAction(Intent.ACTION_SEND);
    +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
    +sendIntent.setType("text/plain");
    +
    +// Verify that the intent will resolve to an activity
    +if (sendIntent.resolveActivity(getPackageManager()) != null) {
    +    startActivity(sendIntent);
    +}
    +
    + +

    Observação: nesse caso, não será usada nenhuma URI, mas o tipo de dados da intenção +será declarado para especificar o conteúdo carregado pelos extras.

    + + +

    Quando {@link android.content.Context#startActivity startActivity()} é chamada, o sistema +avalia todos os aplicativos instalados para determinar quais deles podem tratar esse tipo de intenção +(uma intenção com a ação {@link android.content.Intent#ACTION_SEND} e que carrega dados +de "texto/simples". Se houver somente um aplicativo que possa tratá-la, o aplicativo abre imediatamente e recebe +a intenção. Se diversas atividades aceitarem a intenção, o sistema +exibirá uma caixa de diálogo para que o usuário selecione que aplicativo usar.

    + + +
    + +

    Figura 2. Caixa de diálogo seletora.

    +
    + +

    Forçar um seletor de aplicativo

    + +

    Quando há mais de um aplicativo que responde à intenção implícita, +o usuário pode selecionar o aplicativo que deseja usar e tornar este aplicativo a escolha padrão para +a ação. Isso é positivo ao executar uma ação para a qual o usuário +deseja usar o mesmo aplicativo todas as vezes, como quando abre uma página da web (os usuários +geralmente usam apenas um navegador).

    + +

    Contudo, se diversos aplicativos podem responder à intenção e o usuário deve ficar livre para usar um aplicativo +diferente em cada vez, é preciso exibir uma caixa de diálogo seletora explicitamente. A caixa de diálogo seletora pede que +o usuário selecione o aplicativo desejado para a ação todas as vezes (o usuário não pode selecionar um aplicativo padrão para +a ação). Por exemplo: quando o aplicativo realiza "compartilhar" com a ação {@link +android.content.Intent#ACTION_SEND}, os usuários podem querer compartilhar usando um aplicativo diferente +conforme a situação, portanto deve-se sempre usar a caixa de diálogo seletora, como ilustrado na figura 2.

    + + + + +

    Para exibir o seletor, crie uma {@link android.content.Intent} usando {@link +android.content.Intent#createChooser createChooser()} e passe-a para {@link +android.app.Activity#startActivity startActivity()}. Por exemplo:

    + +
    +Intent sendIntent = new Intent(Intent.ACTION_SEND);
    +...
    +
    +// Always use string resources for UI text.
    +// This says something like "Share this photo with"
    +String title = getResources().getString(R.string.chooser_title);
    +// Create intent to show the chooser dialog
    +Intent chooser = Intent.createChooser(sendIntent, title);
    +
    +// Verify the original intent will resolve to at least one activity
    +if (sendIntent.resolveActivity(getPackageManager()) != null) {
    +    startActivity(chooser);
    +}
    +
    + +

    Isso exibe uma caixa de diálogo com uma lista de aplicativos que respondem à intenção transmitida ao método {@link +android.content.Intent#createChooser createChooser()} e utiliza o texto fornecido como +título da caixa de diálogo.

    + + + + + + + + + +

    Recepção de uma intenção implícita

    + +

    Para anunciar quais intenções implícitas o aplicativo pode receber, declare um ou mais filtros de intenções +para cada um dos componentes do aplicativo com um elemento +{@code <intent-filter>} no arquivo de manifesto. +Cada filtro de intenções especifica o tipo de intenções aceito com base na ação +nos dados e na categoria da intenção. O sistema fornecerá uma intenção implícita ao componente do seu aplicativo somente se ela +puder passar por um dos filtros de intenções.

    + +

    Observação: a intenção explícita é sempre entregue ao alvo +independentemente dos filtros de intenções que o componente declare.

    + +

    Os componentes de um aplicativo devem declarar filtros separados para cada trabalho exclusivo que podem fazer. +Por exemplo, uma atividade em um aplicativo de galeria de imagens pode ter dois filtros: um filtro +para visualizar uma imagem e outro para editar uma imagem. Quando a atividade inicia, +ela inspeciona a {@link android.content.Intent} e decide como se comportar com base nas informações +na {@link android.content.Intent} (como para exibir ou não os controles do editor).

    + +

    Cada filtro de intenções é definido por um elemento {@code <intent-filter>} +no arquivo de manifesto do aplicativo, aninhado no componente correspondente do aplicativo (como +um elemento +{@code <activity>}). Dentro de {@code <intent-filter>}, +é possível especificar o tipo de intenções aceitas usando um ou mais +dos três elementos a seguir:

    + +
    +
    {@code <action>}
    +
    Declara a ação da intenção aceita, no atributo {@code name}. O valor + deve ser o valor literal da string de uma ação, e não a constante da classe.
    +
    {@code <data>}
    +
    Declara o tipo de dados aceitos usando um ou mais atributos que especificam diversos + aspectos da URI de dados (scheme, host, port, +path etc.) e do tipo MIME.
    +
    {@code <category>}
    +
    Declara a categoria da intenção aceita, no atributo {@code name}. O valor + deve ser o valor literal da string de uma ação, e não a constante da classe. + +

    Observação: para receber intenções implícitas, + é preciso incluir a + categoria {@link android.content.Intent#CATEGORY_DEFAULT} no filtro de intenções. Os métodos +{@link android.app.Activity#startActivity startActivity()} e +{@link android.app.Activity#startActivityForResult startActivityForResult()} tratam de todas as intenções + como se eles declarassem a categoria {@link android.content.Intent#CATEGORY_DEFAULT}. + Se você não a declarar no filtro de intenções, nenhuma intenção implícita retomará + a sua atividade.

    +
    +
    + +

    Por exemplo, abaixo há uma declaração de atividade com um filtro de intenções para receber +uma intenção {@link android.content.Intent#ACTION_SEND} quando o tipo de dados for texto:

    + +
    +<activity android:name="ShareActivity">
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="text/plain"/>
    +    </intent-filter>
    +</activity>
    +
    + +

    Não há problemas em criar um filtro que inclua mais de uma instância de +{@code <action>}, +{@code <data>} ou +{@code <category>}. +Se você o fizer, basta certificar-se de que o componente possa tratar todas e quaisquer combinações +daqueles elementos do filtro.

    + +

    Para tratar de diversos tipos de intenções, mas somente em combinações específicas +de ações, dados e tipos de categoria, será necessário criar diversos filtros de intenções.

    + + + + +

    As intenções implícitas são testadas em relação a um filtro por meio da comparação da intenção com cada um +dos três elementos. Para ser entregue ao componente, a intenção deve passar por todos os três testes. +Se ela falhar em algum deles, o sistema Android não entregará a intenção +ao componente. No entanto, como um componente poder ter diversos filtros de intenções, uma intenção que não +passe por um dos filtros de um componente pode passar por outro filtro. +Veja mais informações sobre como o sistema resolve intenções na seção abaixo +sobre Resolução de intenções.

    + +

    Atenção: para evitar a execução involuntária de um {@link android.app.Service} +diferente do aplicativo, sempre use uma intenção explícita para iniciar o próprio serviço +e não declare filtros de intenções para ele.

    + +

    Observação: +para todas as atividades, é necessário declarar os filtros de intenções no arquivo de manifesto. +Contudo, os filtros para receptores de transmissão podem ser registrados dinamicamente chamando +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}. Assim, será possível cancelar o registro do receptor com {@link +android.content.Context#unregisterReceiver unregisterReceiver()}. Isso permitirá que o aplicativo +receba transmissões específicas durante um período de tempo especificado apenas quando o aplicativo +estiver em execução.

    + + + + + + + +

    Exemplos de filtros

    + +

    Para compreender melhor alguns dos comportamentos do filtro de intenções, veja o fragmento a seguir +do arquivo de manifesto de um aplicativo de compartilhamento social.

    + +
    +<activity android:name="MainActivity">
    +    <!-- This activity is the main entry, should appear in app launcher -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +
    +<activity android:name="ShareActivity">
    +    <!-- This activity handles "SEND" actions with text data -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="text/plain"/>
    +    </intent-filter>
    +    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <action android:name="android.intent.action.SEND_MULTIPLE"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
    +        <data android:mimeType="image/*"/>
    +        <data android:mimeType="video/*"/>
    +    </intent-filter>
    +</activity>
    +
    + +

    A primeira atividade, {@code MainActivity}, é o ponto de entrada principal do aplicativo — a atividade +que abre quando o usuário inicializa o aplicativo pela primeira vez com o ícone de inicialização:

    +
      +
    • A ação {@link android.content.Intent#ACTION_MAIN} + indica que este é o ponto de entrada principal e não espera nenhum dado de intenção.
    • +
    • A categoria {@link android.content.Intent#CATEGORY_LAUNCHER} indica que esse ícone + da atividade deve ser colocado no inicializador de aplicativo do sistema. Se o elemento {@code <activity>} + não especificar um ícone com {@code icon}, o sistema usará o ícone do elemento + {@code <application>}.
    • +
    +

    Esses dois devem ser pareados juntos para que a atividade apareça no inicializador do aplicativo.

    + +

    A segunda atividade, {@code ShareActivity}, destina-se a facilitar o compartilhamento de conteúdo de texto +e mídia. Apesar de os usuários poderem acessar essa atividade pela {@code MainActivity}, +eles também podem acessar {@code ShareActivity} diretamente de outro aplicativo que emita uma intenção +implícita que corresponda a um dos dois filtros de intenções.

    + +

    Observação: o tipo MIME, +{@code +application/vnd.google.panorama360+jpg}, é um tipo de dados especial que especifica +fotos panorâmicas que podem ser tratadas com as APIs do Google +Panorama.

    + + + + + + + + + + + + + +

    Uso de uma intenção pendente

    + +

    Um objeto {@link android.app.PendingIntent} é um agrupador em torno de um objeto {@link +android.content.Intent}. A principal finalidade de uma {@link android.app.PendingIntent} + é conceder permissão a um aplicativo externo +para usar a {@link android.content.Intent} contida como se ela fosse executada a partir +do processo do próprio aplicativo.

    + +

    Os principais casos de uso de uma intenção pendente são:

    +
      +
    • Declarar uma intenção a ser executada quando o usuário realiza uma ação com a Notificação + (o {@link android.app.NotificationManager} do sistema Android + executa a {@link android.content.Intent}). +
    • Declarar uma intenção a ser executada quando o usuário realiza uma ação com o + Widget do aplicativo + (o aplicativo de tela inicial executa a {@link android.content.Intent}). +
    • Declarar uma intenção a ser executada em um momento específico no futuro (o {@link android.app.AlarmManager} + do sistema Android executa a {@link android.content.Intent}). +
    + +

    Como cada objeto {@link android.content.Intent} é projetado para ser tratado por um tipo +específico de componentes do aplicativo (uma {@link android.app.Activity}, um {@link android.app.Service} +ou um {@link android.content.BroadcastReceiver}), uma {@link android.app.PendingIntent} +deve ser criada com a mesma consideração. Ao usar uma intenção pendente, o aplicativo +não executará a intenção com uma chamada como de {@link android.content.Context#startActivity +startActivity()}. Em vez disso, deve-se declarar o tipo do componente pretendido ao criar +a {@link android.app.PendingIntent} chamando o respectivo método criador:

    + +
      +
    • {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} para uma + {@link android.content.Intent} que inicia uma {@link android.app.Activity}.
    • +
    • {@link android.app.PendingIntent#getService PendingIntent.getService()} para uma + {@link android.content.Intent} que inicia um {@link android.app.Service}.
    • +
    • {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} para uma + {@link android.content.Intent} que inicia um {@link android.content.BroadcastReceiver}.
    • +
    + +

    A menos que o aplicativo esteja recebendo intenções pendentes de outros aplicativos, +os métodos acima para criar uma {@link android.app.PendingIntent} são provavelmente +os únicos métodos de {@link android.app.PendingIntent} necessários.

    + +

    Cada método toma o {@link android.content.Context} do aplicativo atual, +a {@link android.content.Intent} que você deseja agrupar e um ou mais sinalizadores que especificam +como a intenção deve ser usada (como se a intenção pudesse ser usada mais de uma vez).

    + +

    Veja mais informações sobre o uso de intenções pendentes na documentação +dos respectivos casos de uso, como nos guias das APIs de Notificações +e Widget do aplicativo.

    + + + + + + + +

    Resolução de intenções

    + + +

    Quando o sistema recebe uma intenção implícita para iniciar uma atividade, ele busca +as melhores atividades para a intenção comparando-a com os filtros de intenções com base em três aspectos:

    + +
      +
    • A ação da intenção +
    • Os dados da intenção (URI e tipo de dados) +
    • A categoria da intenção +
    + +

    As seções a seguir descrevem como uma intenção é combinada com os componentes apropriados +em termos de como o filtro de intenções é declarado no arquivo de manifesto de um aplicativo.

    + + +

    Teste de ação

    + +

    Para especificar ações de intenções aceitas, um filtro de intenções pode declarar zero ou mais +elementos {@code +<action>}. Por exemplo:

    + +
    +<intent-filter>
    +    <action android:name="android.intent.action.EDIT" />
    +    <action android:name="android.intent.action.VIEW" />
    +    ...
    +</intent-filter>
    +
    + +

    Para passar por este filtro, a ação especificada na {@link android.content.Intent} + deve corresponder a uma das ações listadas no filtro.

    + +

    Se o filtro não listar nenhuma ação, não há nada a que +uma intenção corresponda, portanto todas as intenções falharão no teste. Contudo, se uma {@link android.content.Intent} +não especificar nenhuma ação, ela passará no teste (desde que o filtro +contenha pelo menos uma ação).

    + + + +

    Teste de categoria

    + +

    Para especificar as categorias de intenção aceitas, um filtro de intenções pode declarar zero ou mais +elementos {@code +<category>}. Por exemplo:

    + +
    +<intent-filter>
    +    <category android:name="android.intent.category.DEFAULT" />
    +    <category android:name="android.intent.category.BROWSABLE" />
    +    ...
    +</intent-filter>
    +
    + +

    Para que uma intenção passe no teste de categoria, cada categoria na {@link android.content.Intent} +deve corresponder a uma categoria no filtro. O inverso não é necessário — o filtro de intenções pode +declarar mais categorias das especificadas na {@link android.content.Intent} e +a {@link android.content.Intent} ainda passará no teste. Portanto, uma intenção sem categorias sempre +passará nesse teste independentemente das categorias declaradas no filtro.

    + +

    Observação: +O Android aplica automaticamente a categoria {@link android.content.Intent#CATEGORY_DEFAULT} +para todas as intenções implícitas passadas a {@link +android.content.Context#startActivity startActivity()} e {@link +android.app.Activity#startActivityForResult startActivityForResult()}. +Por isso, se você deseja que a atividade receba intenções implícitas, ela deve +conter uma categoria de {@code "android.intent.category.DEFAULT"} nos filtros de intenções (como +exibido no exemplo de {@code <intent-filter>} anterior).

    + + + +

    Teste de dados

    + +

    Para especificar dados de intenções aceitas, um filtro de intenções pode declarar zero ou mais +elementos {@code +<data>}. Por exemplo:

    + +
    +<intent-filter>
    +    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    +    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    +    ...
    +</intent-filter>
    +
    + +

    Cada elemento +<data> pode especificar uma estrutura de URI e um tipo de dados (tipo de mídia MIME). Há atributos +separados — {@code scheme}, {@code host}, {@code port} e +{@code path} — para cada parte da URI: +

    + +

    {@code <scheme>://<host>:<port>/<path>}

    + +

    +Por exemplo: +

    + +

    {@code content://com.example.project:200/folder/subfolder/etc}

    + +

    Nessa URI, o esquema é {@code content}, o host é {@code com.example.project}, +a porta é {@code 200} e o caminho é {@code folder/subfolder/etc}. +

    + +

    Cada um desses atributos é opcional em um elemento {@code <data>}, +mas há dependências lineares:

    +
      +
    • Se não houver esquema especificado, o host será ignorado.
    • +
    • Se não houver host especificado, a porta será ignorada.
    • +
    • Se não houver esquema nem host especificado, o caminho será ignorado.
    • +
    + +

    Quando a URI em uma intenção é comparada a uma especificação de URI em um filtro, +a comparação é feita somente com as partes da URI incluídas no filtro. Por exemplo:

    +
      +
    • Se um filtro especificar somente um esquema, todas as URIs com esse esquema atenderão +ao filtro.
    • +
    • Se um filtro especificar um esquema e uma autoridade, mas não um caminho, todas as URIs +com o mesmo esquema e autoridade passarão pelo filtro independentemente dos caminhos.
    • +
    • Se um filtro especificar um esquema, uma autoridade e um caminho, somente URIs com o mesmo esquema, +autoridade e caminho passarão pelo filtro.
    • +
    + +

    Observação: a especificação de caminho pode +conter um asterisco especial (*) para exigir somente uma correspondência parcial do nome do caminho.

    + +

    O teste de dados compara a URI e o tipo MIME da intenção com uma URI +e um tipo MIME especificados no filtro. As regras são as seguintes: +

    + +
      +
    1. A intenção que não contiver URI nem tipo MIME passará +no teste somente se o filtro não especificar nenhuma URI nem tipo MIME.
    2. + +
    3. A intenção que contiver URI mas nenhum tipo MIME (nem explícito, nem inferível a partir +da URI) passará pelo teste somente se a URI corresponder ao formato de URI do filtro +e se o filtro, igualmente, não especificar um tipo MIME.
    4. + +
    5. A intenção que contiver tipo MIME mas nenhuma URI passará pelo teste +somente se o filtro listar o mesmo tipo MIME e não especificar nenhum formato de URI.
    6. + +
    7. A intenção que contiver URI e tipo MIME (explícito ou inferível a partir +da URI) passará a parte do tipo MIME do teste somente se esse +tipo corresponder a um tipo listado no filtro. A parte da URI passará no teste +se corresponder a uma URI no filtro ou se tiver uma URI de {@code content:} +ou {@code file:} e se o filtro não especificar nenhuma URI. Em outras palavras, +presume-se que um componente seja compatível com dados de {@code content:} e de {@code file:} se +o filtro listar somente um tipo MIME.

    8. +
    + +

    +Essa última regra (d) reflete a expectativa +de que os componentes sejam capazes de obter dados de local de um arquivo ou provedor de conteúdo. +Portanto, os filtros podem listar somente um tipo de dados e não precisam nomear +explicitamente os esquemas {@code content:} e {@code file:}. +Este é um caso típico. Um elemento {@code <data>} +como o seguinte, por exemplo, informa ao Android que o componente pode obter dados de imagem de um provedor +de conteúdo e exibi-los: +

    + +
    +<intent-filter>
    +    <data android:mimeType="image/*" />
    +    ...
    +</intent-filter>
    + +

    +Como a maioria dos dados disponíveis é dispensada pelos provedores de conteúdo, os filtros +que especificam um tipo de dados mas não uma URI são, talvez, os mais comuns. +

    + +

    +Outra configuração comum é: filtros com um esquema e um tipo de dados. Por +exemplo, um elemento +{@code <data>}, como o seguinte, informa ao Android +que o componente pode recuperar dados de vídeo da rede para realizar a ação: +

    + +
    +<intent-filter>
    +    <data android:scheme="http" android:type="video/*" />
    +    ...
    +</intent-filter>
    + + + +

    Correspondência de intenções

    + +

    Intenções são correspondidas a filtros de intenções não somente para descobrir +um componente-alvo a ativar, mas também para descobrir algo sobre o conjunto +de componentes do dispositivo. Por exemplo: o aplicativo Home preenche o inicializador do aplicativo +encontrando todas as atividades com filtros de intenções que especifiquem +a ação {@link android.content.Intent#ACTION_MAIN} e +a categoria {@link android.content.Intent#CATEGORY_LAUNCHER}.

    + +

    O aplicativo pode usar a correspondência de intenções de modo similar. +O {@link android.content.pm.PackageManager} tem um conjunto de métodos +{@code query...()}, que retornam todos os componentes que podem aceitar uma determinada intenção +e uma série de métodos {@code resolve...()} similares, que determinam o melhor +componente para responder a uma intenção. Por exemplo: +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} retorna uma lista de todas as atividades que podem realizar +a intenção passada como um argumento e {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} retorna uma lista de serviços semelhantes. +Nenhum dos métodos ativa os componentes — eles apenas listam aqueles que +podem responder. Há um método semelhante +para receptores de transmissão — o {@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()}. +

    + + + + diff --git a/docs/html-intl/intl/pt-br/guide/components/loaders.jd b/docs/html-intl/intl/pt-br/guide/components/loaders.jd new file mode 100644 index 0000000000000000000000000000000000000000..f3c42094729db5224090075ffa53744402e7229b --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=Carregadores +parent.title=Atividades +parent.link=activities.html +@jd:body +
    +
    +

    Neste documento

    +
      +
    1. Resumo de API de carregador
    2. +
    3. Uso de carregadores em um aplicativo +
        +
      1. +
      2. Início de um carregador
      3. +
      4. Reinício de um carregador
      5. +
      6. Uso dos retornos de chamada de LoaderManager
      7. +
      +
    4. +
    5. Exemplo +
        +
      1. Mais exemplos
      2. +
      +
    6. +
    + +

    Classes principais

    +
      +
    1. {@link android.app.LoaderManager}
    2. +
    3. {@link android.content.Loader}
    4. + +
    + +

    Exemplos relacionados

    +
      +
    1. +LoaderCursor
    2. +
    3. +LoaderThrottle
    4. +
    +
    +
    + +

    Introduzidos no Android 3.0, os carregadores facilitam o carregamento assíncrono de dados +em uma atividade ou fragmento. Os carregadores têm as seguintes características:

    +
      +
    • Eles estão disponíveis para cada {@link android.app.Activity} e {@link +android.app.Fragment}.
    • +
    • Eles fornecem carregamento assíncrono de dados.
    • +
    • Eles monitoram a origem dos dados e fornecem novos resultados +quando o conteúdo é alterado.
    • +
    • Eles reconectam-se automaticamente ao cursor do último carregador +quando são recriados após uma alteração de configuração. Portanto, eles não precisam reconsultar +os dados.
    • +
    + +

    Resumo da API de carregador

    + +

    Há várias classes e interfaces que podem ser envolvidas no uso +de carregadores em um aplicativo. Elas são resumidas nesta tabela:

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Classe/InterfaceDescrição
    {@link android.app.LoaderManager}Uma classe abstrata associada a {@link android.app.Activity} ou +{@link android.app.Fragment} para gerenciar uma ou mais instâncias de {@link +android.content.Loader}. Isto ajuda um aplicativo a gerenciar +operações executadas por longos períodos juntamente com o ciclo de vida de {@link android.app.Activity} +ou {@link android.app.Fragment}; o uso mais comum disto é com +{@link android.content.CursorLoader}. No entanto, os aplicativos têm a liberdade de criar +os próprios carregadores para outros tipos de dados. +
    +
    + Há apenas um {@link android.app.LoaderManager} por atividade ou fragmento. No entanto, um {@link android.app.LoaderManager} pode ter +vários carregadores.
    {@link android.app.LoaderManager.LoaderCallbacks}Uma interface de retorno de chamada para um cliente interagir com {@link +android.app.LoaderManager}. Por exemplo, usa-se o método de retorno de chamada {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +para criar um novo carregador.
    {@link android.content.Loader}Uma classe abstrata que realiza carregamento assíncrono de dados. Esta é a classe de base +de um carregador. Geralmente, você usaria {@link +android.content.CursorLoader}, mas é possível implementar subclasse própria. Enquanto os carregadores +estiverem ativos, devem monitorar a origem dos dados e fornecer +novos resultados quando o conteúdo for alterado.
    {@link android.content.AsyncTaskLoader}Um carregador abstrato que fornece uma {@link android.os.AsyncTask} para realizar o trabalho.
    {@link android.content.CursorLoader}Uma subclasse de {@link android.content.AsyncTaskLoader} que consulta +{@link android.content.ContentResolver} e retorna um {@link +android.database.Cursor}. Esta classe implementa o protocolo {@link +android.content.Loader} de forma padrão para cursores de consulta, +compilando em {@link android.content.AsyncTaskLoader} para realizar a consulta de cursor +em um encadeamento de segundo plano para que a IU do aplicativo não seja bloqueada. Usar +este carregador é a melhor maneira de carregar dados de forma assíncrona a partir de um {@link +android.content.ContentProvider}, em vez de realizar uma consulta gerenciada pelas +APIs da atividade ou do fragmento.
    + +

    As classes e interfaces na tabela acima são os componentes essenciais +que você usará para implementar um carregador no aplicativo. Você não precisará de todos eles +para cada carregador criado, mas sempre precisará de uma referência a {@link +android.app.LoaderManager} para inicializar um carregador e uma implementação +de uma classe {@link android.content.Loader}, como {@link +android.content.CursorLoader}. As seguintes seções mostram como usar +essas classes e interfaces em um aplicativo.

    + +

    Uso de carregadores em um aplicativo

    +

    Esta seção descreve como usar os carregadores em um aplicativo do Android. Um aplicativo +que usa os carregadores, geralmente, inclui o seguinte:

    +
      +
    • Uma {@link android.app.Activity} ou um {@link android.app.Fragment}.
    • +
    • Uma instância de {@link android.app.LoaderManager}.
    • +
    • Um {@link android.content.CursorLoader} para carregar dados baseados em um {@link +android.content.ContentProvider}. Alternativamente, é possível implementar a própria subclasse +de {@link android.content.Loader} ou {@link android.content.AsyncTaskLoader} +para carregar dados de outra origem.
    • +
    • Uma implementação de {@link android.app.LoaderManager.LoaderCallbacks}. +É aqui que é possível criar novos carregadores e gerenciar as referências +a carregadores existentes.
    • +
    • Uma maneira de exibir os dados do carregador, como um {@link +android.widget.SimpleCursorAdapter}.
    • +
    • Uma origem de dados, como um {@link android.content.ContentProvider}, ao usar +{@link android.content.CursorLoader}.
    • +
    +

    Início de um carregador

    + +

    O {@link android.app.LoaderManager} gerencia uma ou mais instâncias de {@link +android.content.Loader} dentro de uma {@link android.app.Activity} +ou um {@link android.app.Fragment}. Há apenas um {@link +android.app.LoaderManager} por atividade ou fragmento.

    + +

    Geralmente, +inicializa-se um {@link android.content.Loader} dentro do método {@link +android.app.Activity#onCreate onCreate()} da atividade, ou dentro do método +{@link android.app.Fragment#onActivityCreated onActivityCreated()} do fragmento. Faça +isso da seguinte maneira:

    + +
    // Prepare the loader.  Either re-connect with an existing one,
    +// or start a new one.
    +getLoaderManager().initLoader(0, null, this);
    + +

    O método {@link android.app.LoaderManager#initLoader initLoader()} +recebe os seguintes parâmetros:

    +
      +
    • Um ID único que identifica o carregador. Neste exemplo, o ID é 0.
    • +
    • Argumentos opcionais para fornecer ao carregador +em construção (null neste exemplo).
    • + +
    • Uma implementação de {@link android.app.LoaderManager.LoaderCallbacks}, +que {@link android.app.LoaderManager} chama para relatar eventos do carregador. Nesse exemplo, + a classe local implementa a interface de {@link +android.app.LoaderManager.LoaderCallbacks}, para que ela passe uma referência +para si, {@code this}.
    • +
    +

    A chamada de {@link android.app.LoaderManager#initLoader initLoader()} garante que o carregador +foi inicializado e que está ativo. Ela possui dois possíveis resultados:

    +
      +
    • Se o carregador especificado pelo ID já existir, o último carregador +criado será usado novamente.
    • +
    • Se o carregador especificado pelo ID não existir, +{@link android.app.LoaderManager#initLoader initLoader()} ativará o método +{@link android.app.LoaderManager.LoaderCallbacks} {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. +É aqui que você implementa o código para instanciar e retornar um novo carregador. +Para obter mais informações, consulte a seção onCreateLoader.
    • +
    +

    Em qualquer um dos casos, a implementação de {@link android.app.LoaderManager.LoaderCallbacks} +fornecida é associada ao carregador e será chamada quando o estado +do carregador mudar. Se, no momento desta chamada, o autor dela +estiver no estado inicializado e o carregador solicitado já existir e tiver +gerado seus dados, o sistema chamará {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +imediatamente (durante{@link android.app.LoaderManager#initLoader initLoader()}), +então prepare-se para tais situações. Consulte +onLoadFinished para obter mais informações sobre este retorno de chamada

    + +

    Observe que o método {@link android.app.LoaderManager#initLoader initLoader()} +retorna o {@link android.content.Loader} que é criado, mas você não precisará +capturar uma referência para ele. O {@link android.app.LoaderManager} gerencia +a vida do carregador automaticamente. O {@link android.app.LoaderManager} +inicia e interrompe o carregamento quando necessário, além de manter o estado do carregador +e do conteúdo associado. À medida que isso ocorre, você raramente interage com os carregadores +diretamente (para ver um exemplo de métodos para aprimorar o comportamento +de um carregador, consulte o exemplo de LoaderThrottle). +Geralmente, usam-se os métodos {@link +android.app.LoaderManager.LoaderCallbacks} para intervir no processo de carregamento +quando determinados eventos ocorrem. Para obter mais informações sobre este assunto, consulte Uso dos retornos de chamada de LoaderManager.

    + +

    Reinício de um carregador

    + +

    Ao usar {@link android.app.LoaderManager#initLoader initLoader()}, +como mostrado acima, ele usará um carregador existente com o ID especificado, se houver um. +Caso contrário, um carregador será criado. No entanto, às vezes, você quer descartar os dados antigos +e começar do início.

    + +

    Para descartar os dados antigos, use {@link +android.app.LoaderManager#restartLoader restartLoader()}. Por exemplo, +esta implementação de {@link android.widget.SearchView.OnQueryTextListener} reinicia +o carregador quando a consulta do usuário é alterada. O carregador precisa ser reiniciado +para que possa usar o filtro de busca revisado para realizar uma nova consulta:

    + +
    +public boolean onQueryTextChanged(String newText) {
    +    // Called when the action bar search text has changed.  Update
    +    // the search filter, and restart the loader to do a new query
    +    // with this filter.
    +    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    +    getLoaderManager().restartLoader(0, null, this);
    +    return true;
    +}
    + +

    Uso dos retornos de chamada de LoaderManager

    + +

    {@link android.app.LoaderManager.LoaderCallbacks} é uma interface de retorno de chamada +que permite que um cliente interaja com o {@link android.app.LoaderManager}.

    +

    Carregadores, em determinado {@link android.content.CursorLoader}, devem +reter os dados após serem interrompidos. Isto permite que os aplicativos +mantenham os dados dos métodos {@link android.app.Activity#onStop +onStop()} e {@link android.app.Activity#onStart onStart()} do fragmento ou da atividade +para que, quando os usuários voltarem a um aplicativo, não tenham que esperar +o recarregamento dos dados. Você usa os métodos {@link android.app.LoaderManager.LoaderCallbacks} +para saber quando deve criar um novo carregador, e para dizer ao aplicativo quando + deve interromper o uso dos dados de um carregador.

    + +

    {@link android.app.LoaderManager.LoaderCallbacks} inclui +esses métodos:

    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — +instancia e retorna um novo {@link android.content.Loader} para o ID fornecido. +
    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +— chamado quando um carregador anteriormente criado termina o seu carregamento. +
    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} + — chamado quando um carregador anteriormente criado é reiniciado, +tornando os dados indisponíveis. +
    • +
    +

    Esses métodos são descritos com mais informações nas seguintes seções.

    + +

    onCreateLoader

    + +

    Ao tentar acessar um carregador (por exemplo, por meio de {@link +android.app.LoaderManager#initLoader initLoader()}), ele verifica +se o carregador especificado pelo ID existe. Se não existir, ele ativa o método {@link +android.app.LoaderManager.LoaderCallbacks} {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. É aqui +que você pode criar um novo carregador. Geralmente, será um {@link +android.content.CursorLoader}, mas é possível implementar a própria subclasse de {@link +android.content.Loader}.

    + +

    Nesse exemplo, o método de retorno de chamada de {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +cria um {@link android.content.CursorLoader}. Você deve compilar +{@link android.content.CursorLoader} usando o método construtor, +que exige um conjunto completo de informações necessárias para realizar uma consulta ao {@link +android.content.ContentProvider}. Especificamente, ele precisa de:

    +
      +
    • uri — a URI do conteúdo a ser recuperado.
    • +
    • projection — uma lista de quais colunas devem ser retornadas. Passar +null retornará todas as colunas, o que é ineficiente.
    • +
    • selection — um filtro que declara quais linhas devem retornar, +formatado por uma cláusula SQL WHERE (excluindo WHERE). Passar +null retornará todas as linhas da URI em questão.
    • +
    • selectionArgs — é possível incluir interrogações na seleção, +que serão substituídas pelos valores de selectionArgs, na ordem em que aparecem +na seleção. Os valores serão vinculados como Strings.
    • +
    • sortOrder — como ordenar as linhas, formatadas em uma cláusula SQL +ORDER BY (excluindo ORDER BY). Passar null +usará a ordem de classificação padrão, que pode ser desordenada.
    • +
    +

    Por exemplo:

    +
    + // If non-null, this is the current filter the user has provided.
    +String mCurFilter;
    +...
    +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +    // This is called when a new Loader needs to be created.  This
    +    // sample only has one Loader, so we don't care about the ID.
    +    // First, pick the base URI to use depending on whether we are
    +    // currently filtering.
    +    Uri baseUri;
    +    if (mCurFilter != null) {
    +        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
    +                  Uri.encode(mCurFilter));
    +    } else {
    +        baseUri = Contacts.CONTENT_URI;
    +    }
    +
    +    // Now create and return a CursorLoader that will take care of
    +    // creating a Cursor for the data being displayed.
    +    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
    +            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
    +            + Contacts.DISPLAY_NAME + " != '' ))";
    +    return new CursorLoader(getActivity(), baseUri,
    +            CONTACTS_SUMMARY_PROJECTION, select, null,
    +            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    +}
    +

    onLoadFinished

    + +

    Este método é chamado quando um carregador anteriormente criado terminar o seu carregamento. +Este método certamente será chamado antes da liberação dos últimos dados +que forem fornecidos por este carregador. Neste ponto, você deve remover todo o uso +dos dados antigos (já que serão liberados em breve), mas não deve fazer +a liberação dos dados, já que pertencem ao carregador e ele lidará com isso.

    + + +

    O carregador liberará os dados quando souber +que o aplicativo não está mais usando-os. Por exemplo, se os dados forem um cursor de um {@link +android.content.CursorLoader}, você não deve chamar {@link +android.database.Cursor#close close()} por conta própria. Se o cursor estiver +sendo colocado em um {@link android.widget.CursorAdapter}, você deve usar o método {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} para que o antigo +{@link android.database.Cursor} não seja fechado. Por exemplo:

    + +
    +// This is the Adapter being used to display the list's data.
    SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in.  (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}
    + +

    onLoaderReset

    + +

    Este método é chamado quando um carregador anteriormente criado é reiniciado, +tornando os dados indisponíveis. Este retorno de chamada permite que você descubra quando os dados +estão prestes a serem liberados para que seja possível remover a referência a eles.  

    +

    Esta implementação chama +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} +com um valor de null:

    + +
    +// This is the Adapter being used to display the list's data.
    +SimpleCursorAdapter mAdapter;
    +...
    +
    +public void onLoaderReset(Loader<Cursor> loader) {
    +    // This is called when the last Cursor provided to onLoadFinished()
    +    // above is about to be closed.  We need to make sure we are no
    +    // longer using it.
    +    mAdapter.swapCursor(null);
    +}
    + + +

    Exemplo

    + +

    Como exemplo, a seguir há uma implementação completa de um {@link +android.app.Fragment} que exibe uma {@link android.widget.ListView} contendo +os resultados de uma consulta aos provedores de conteúdo de contatos. Ela usa um {@link +android.content.CursorLoader} para gerenciar a consulta no provedor.

    + +

    Para um aplicativo acessar os contatos de um usuário, como neste exemplo, +o manifesto deverá incluir a permissão +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.

    + +
    +public static class CursorLoaderListFragment extends ListFragment
    +        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
    +
    +    // This is the Adapter being used to display the list's data.
    +    SimpleCursorAdapter mAdapter;
    +
    +    // If non-null, this is the current filter the user has provided.
    +    String mCurFilter;
    +
    +    @Override public void onActivityCreated(Bundle savedInstanceState) {
    +        super.onActivityCreated(savedInstanceState);
    +
    +        // Give some text to display if there is no data.  In a real
    +        // application this would come from a resource.
    +        setEmptyText("No phone numbers");
    +
    +        // We have a menu item to show in action bar.
    +        setHasOptionsMenu(true);
    +
    +        // Create an empty adapter we will use to display the loaded data.
    +        mAdapter = new SimpleCursorAdapter(getActivity(),
    +                android.R.layout.simple_list_item_2, null,
    +                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
    +                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
    +        setListAdapter(mAdapter);
    +
    +        // Prepare the loader.  Either re-connect with an existing one,
    +        // or start a new one.
    +        getLoaderManager().initLoader(0, null, this);
    +    }
    +
    +    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    +        // Place an action bar item for searching.
    +        MenuItem item = menu.add("Search");
    +        item.setIcon(android.R.drawable.ic_menu_search);
    +        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    +        SearchView sv = new SearchView(getActivity());
    +        sv.setOnQueryTextListener(this);
    +        item.setActionView(sv);
    +    }
    +
    +    public boolean onQueryTextChange(String newText) {
    +        // Called when the action bar search text has changed.  Update
    +        // the search filter, and restart the loader to do a new query
    +        // with this filter.
    +        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    +        getLoaderManager().restartLoader(0, null, this);
    +        return true;
    +    }
    +
    +    @Override public boolean onQueryTextSubmit(String query) {
    +        // Don't care about this.
    +        return true;
    +    }
    +
    +    @Override public void onListItemClick(ListView l, View v, int position, long id) {
    +        // Insert desired behavior here.
    +        Log.i("FragmentComplexList", "Item clicked: " + id);
    +    }
    +
    +    // These are the Contacts rows that we will retrieve.
    +    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
    +        Contacts._ID,
    +        Contacts.DISPLAY_NAME,
    +        Contacts.CONTACT_STATUS,
    +        Contacts.CONTACT_PRESENCE,
    +        Contacts.PHOTO_ID,
    +        Contacts.LOOKUP_KEY,
    +    };
    +    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +        // This is called when a new Loader needs to be created.  This
    +        // sample only has one Loader, so we don't care about the ID.
    +        // First, pick the base URI to use depending on whether we are
    +        // currently filtering.
    +        Uri baseUri;
    +        if (mCurFilter != null) {
    +            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
    +                    Uri.encode(mCurFilter));
    +        } else {
    +            baseUri = Contacts.CONTENT_URI;
    +        }
    +
    +        // Now create and return a CursorLoader that will take care of
    +        // creating a Cursor for the data being displayed.
    +        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
    +                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
    +                + Contacts.DISPLAY_NAME + " != '' ))";
    +        return new CursorLoader(getActivity(), baseUri,
    +                CONTACTS_SUMMARY_PROJECTION, select, null,
    +                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    +    }
    +
    +    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    +        // Swap the new cursor in.  (The framework will take care of closing the
    +        // old cursor once we return.)
    +        mAdapter.swapCursor(data);
    +    }
    +
    +    public void onLoaderReset(Loader<Cursor> loader) {
    +        // This is called when the last Cursor provided to onLoadFinished()
    +        // above is about to be closed.  We need to make sure we are no
    +        // longer using it.
    +        mAdapter.swapCursor(null);
    +    }
    +}
    +

    Mais exemplos

    + +

    Há alguns exemplos variados na ApiDemos +que ilustra o uso de carregadores:

    +
      +
    • +LoaderCursor — uma versão completa do +fragmento exibido acima.
    • +
    • LoaderThrottle — um exemplo de como usar o regulador +para reduzir o número de consultas que o provedor de conteúdo realiza quando os dados são alterados.
    • +
    + +

    Para obter mais informações sobre o download e a instalação de exemplos de SDK, consulte Obtenção +dos exemplos.

    + diff --git a/docs/html-intl/intl/pt-br/guide/components/processes-and-threads.jd b/docs/html-intl/intl/pt-br/guide/components/processes-and-threads.jd new file mode 100644 index 0000000000000000000000000000000000000000..c8e636dacce834f848b8435bc8d49b05adb8db3d --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=Processos e encadeamentos +page.tags=lifecycle,background + +@jd:body + + + +

    Quando um componente de aplicativo inicia e o aplicativo não tem nenhum outro componente em execução, +o sistema Android inicia um novo processo no Linux para o aplicativo com um único encadeamento +de execução. Por padrão, todos os componentes do mesmo aplicativo são executados no mesmo processo +e encadeamento (chamado de encadeamento "principal"). Se um componente de aplicativo iniciar e já existir um processo +para este aplicativo (pois outro componente do aplicativo já existe), ele será iniciado +dentro deste processo e usará o mesmo encadeamento de execução. No entanto, é possível organizar para que vários componentes +no aplicativo sejam executados em processos separados e criar encadeamentos +adicionais para qualquer processo.

    + +

    Este documento discute como os processos e os encadeamentos funcionam em um aplicativo do Android.

    + + +

    Processos

    + +

    Por padrão, todos os componentes do mesmo aplicativo são executados no mesmo processo +e a maioria dos aplicativos não deve alterar isso. No entanto, se achar que precisa de controle sobre o processo a que um certo +componente pertence, é possível fazer isto no arquivo de manifesto.

    + +

    A entrada do manifesto para cada tipo de elemento de componente — {@code +<activity>}, {@code +<service>}, {@code +<receiver>} e {@code +<provider>} — é compatível com um atributo {@code android:process} que pode especificar +um processo em que este componente deverá ser executado. É possível definir este atributo para que cada componente +seja executado em seu próprio processo ou para que alguns componentes compartilhem um processo, enquanto que outros não. Também é possível definir +{@code android:process} para que os componentes de aplicativos diferentes sejam executados no mesmo +processo — fornecido para que os aplicativos compartilhem o mesmo ID de usuário do Linux e sejam assinados +com os mesmos certificados.

    + +

    O elemento {@code +<application>} também suporta um atributo {@code android:process} +para definir um valor padrão que se aplique a todos os elementos.

    + +

    O Android pode decidir desativar um processo em certo ponto, quando a memória estiver baixa e for necessária +para outros processos que atendem o usuário de forma mais imediata. Os componentes +de aplicativo em execução no processo que é eliminado são consequentemente eliminados. Um processo é iniciado +novamente para aqueles componentes quando houver trabalho para eles.

    + +

    Ao decidir que processo eliminar, o sistema Android avalia a importância relativa dele +para o usuário. Por exemplo, ele fechará mais prontamente um processo que hospede atividades que não estejam mais +visíveis na tela, comparado a um processo que hospede atividades visíveis. A decisão +é exterminar processo ou não, portanto, depende do estado dos componentes em execução neste processo. As regras +usadas para decidir quais processos serão exterminados são discutidas abaixo.

    + + +

    Ciclo de vida dos processos

    + +

    O sistema Android tenta manter um processo do aplicativo pelo maior período possível, +mas eventualmente precisa remover processos antigos para recuperar memória para processos novos e mais importantes. Para determinar +quais processos manter +e quais exterminar, o sistema posiciona cada um em uma "hierarquia de importância" com base +nos componentes em execução e no estado deles. Processos com menos importância +serão eliminados primeiro e, em seguida, aqueles com a menor importância depois deles também serão, consecutivamente, até que os recursos +do sistema sejam recuperados.

    + +

    Há cinco níveis na hierarquia de importância. A lista a seguir mostra os tipos +de processo em ordem de importância (o primeiro processo é o mais importante +e o eliminado por último):

    + +
      +
    1. Processos em primeiro plano +

      Um processo necessário para o que o usuário está fazendo. Considera-se + um processo como em primeiro plano se qualquer uma das condições for verdadeira:

      + +
        +
      • Se ele hospedar uma {@link android.app.Activity} com a qual o usuário esteja interagindo (se o método {@link android.app.Activity#onResume onResume()} de {@link +android.app.Activity} +for chamado).
      • + +
      • Se ele hospedar um {@link android.app.Service} vinculado à atividade com a qual o usuário +esteja interagindo.
      • + +
      • Se ele hospedar um {@link android.app.Service} em execução "em primeiro plano"— se o serviço +tiver chamado {@link android.app.Service#startForeground startForeground()}. + +
      • Se ele hospedar um {@link android.app.Service} que esteja executando um de seus retornos de chamada +do ciclo de vida ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart +onStart()}, ou {@link android.app.Service#onDestroy onDestroy()}).
      • + +
      • Se ele hospedar um {@link android.content.BroadcastReceiver} que esteja executando seu método {@link + android.content.BroadcastReceiver#onReceive onReceive()}.
      • +
      + +

      Geralmente, somente poucos processos em primeiro plano existem em um determinado momento. Eles serão eliminados +somente como último recurso — se a memória estiver baixa demais para suportar a execução de todos. Geralmente, neste ponto, +o dispositivo atingiu o estado de paginação de memória. Portanto, eliminar alguns processos em primeiro plano +é necessário para que a interface permaneça responsiva.

    2. + +
    3. Processos visíveis +

      Um processo que não tenha nenhum componente em primeiro plano, + mas que ainda possa afetar o que o usuário vê na tela. Um processo é considerado visível + se uma das condições a seguir for verdadeira:

      + +
        +
      • Se ele hospedar um {@link android.app.Activity} que não esteja em primeiro plano, +mas ainda seja visível para o usuário (o seu método {@link android.app.Activity#onPause onPause()} tiver sido chamado). +Isto poderá ocorrer, por exemplo, se a atividade em primeiro plano iniciar um diálogo, o que permitirá +que a atividade anterior seja vista por trás dela.
      • + +
      • Se ele hospedar um {@link android.app.Service} que esteja vinculado a uma atividade visível +(ou em primeiro plano).
      • +
      + +

      Um processo visível é considerado extremamente importante e não será eliminado a não ser +que seja necessário para manter em execução os processos em primeiro plano.

      +
    4. + +
    5. Processos de serviço +

      Um processo que esteja executando um serviço que tenha sido iniciado com o método {@link +android.content.Context#startService startService()} e não +esteja em uma das duas categorias mais altas. Apesar de processos de serviço não estarem diretamente ligados a tudo o que os usuários veem, +eles estão geralmente fazendo coisas com que o usuário se importa (como reproduzindo música em segundo plano +ou baixando dados na rede), então o sistema os mantém em execução a não ser que haja memória suficiente +para retê-los juntamente com todos os processos visíveis e em primeiro plano.

      +
    6. + +
    7. Processos em segundo plano +

      Um processo que mantenha uma atividade que não esteja atualmente visível para o usuário (se o método +{@link android.app.Activity#onStop onStop()} da atividade tiver sido chamado). Esses processos não têm impacto direto +na experiência do usuário e o sistema poderá eliminá-los a qualquer momento para recuperar memória para processos +em primeiro plano, +visíveis ou de serviço. Geralmente há vários processos em segundo plano em execução e eles são mantidos +em uma lista LRU (least recently used - menos usados recentemente) para garantir que o processo com a atividade +que foi mais vista recentemente pelo usuário seja a última a ser eliminada. Se uma atividade implementar o seu método de ciclo de vida +corretamente e salvar o estado atual, eliminar o seu processo não terá efeito visível +na experiência do usuário, pois quando ele navegar de volta à atividade, ela restaurará +todo o estado visível. Consulte o documento Atividades +para obter informações sobre os estados de restauração e de gravação.

      +
    8. + +
    9. Processos vazios +

      Um processo que não possui nenhum componente de aplicativo ativo. O único motivo para manter este tipo de processo +ativo é para armazenamento em cache, para aprimorar o tempo de inicialização +na próxima vez em que um componente precisar executá-lo. O sistema frequentemente elimina esses processos para equilibrar os recursos do sistema em geral +entre os armazenamentos em cache do processo e os de núcleo subjacente.

      +
    10. +
    + + +

    O Android coloca o processo no maior nível possível, com base na importância dos componentes +atualmente ativos no processo. Por exemplo, se um processo hospedar um serviço e uma atividade visível +, ele terá a classificação de um processo visível, e não a de um processo de serviço.

    + +

    Além disso, a classificação de um processo pode ser elevada porque outros processos +dependem dele — um processo que está servindo a outro processo nunca pode ter classificação menor +do que a do processo que está sendo servido. Por exemplo, se um provedor de conteúdo no processo A estiver servindo um cliente no processo B, +ou se um serviço no processo A estiver vinculado a um componente no processo B, o processo A será sempre considerado +menos importante do que o processo B.

    + +

    Como um processo que executa um serviço tem classificação maior do que um processo com atividades em segundo plano, +uma atividade que iniciar uma operação de longo prazo poderá também iniciar um serviço para esta operação, em vez de simplesmente +criar um encadeamento de trabalho — especificamente se a operação exceder a atividade. +Por exemplo, uma atividade que esteja fazendo upload de uma imagem para um site deverá iniciar um serviço +para fazer o upload para que ele possa continuar em segundo plano mesmo se o usuário deixar a atividade. +Usar um serviço garante que a operação tenha pelo menos a prioridade "processo de serviço", +independente do que acontecer à atividade. Este é o mesmo motivo pelo qual os receptores de transmissão devem +empregar serviços em vez de simplesmente usar operações que consomem tempo em um encadeamento.

    + + + + +

    Encadeamentos

    + +

    Quando um aplicativo é executado, o sistema cria um encadeamento de execução para ele, +chamado de "principal". Este encadeamento é muito importante, pois está encarregado de despachar eventos +para os widgets adequados da interface do usuário, incluindo eventos de desenho. É também o encadeamento +em que o aplicativo interage com componentes do kit de ferramentas da IU do Android (componentes dos pacotes {@link +android.widget} e {@link android.view}). Portanto, o encadeamento principal, às vezes, +também chama o encadeamento da IU.

    + +

    O sistema não cria um encadeamento separado para cada instância de um componente. Todos os componentes +executados no mesmo processo são instanciados no encadeamento da IU, e as chamadas do sistema +para cada componente são despachadas deste encadeamento. Consequentemente, métodos que respondam aos retornos de chamada +(como {@link android.view.View#onKeyDown onKeyDown()} para informar ações de usuário +ou um método de retorno de chamada do ciclo de vida) sempre serão executados no encadeamento da IU do processo.

    + +

    Por exemplo, quando o usuário toca em um botão na tela, o encadeamento da IU do aplicativo despacha +o evento de toque para o widget que, em troca, ativa o seu estado pressionado e publica uma solicitação de invalidação +à fila do evento. O encadeamento da IU retira a solicitação da fila e notifica o widget de que ele deve +desenhar novamente por conta própria.

    + +

    Quando o aplicativo realiza trabalho intenso em resposta à interação do usuário, este modelo de único encadeamento +pode produzir desempenho inferior a não ser que você implemente o aplicativo adequadamente. Especificamente, se tudo estiver +acontecendo no encadeamento da IU, realizar operações longas, como acesso à rede +ou consultas a banco de dados, bloqueará toda a IU. Quando o encadeamento é bloqueado, nenhum evento pode ser despachado, +incluindo eventos de desenho. Da perspectiva do usuário, +o aplicativo parece travar. Pior ainda, se o encadeamento da IU for bloqueado por mais do que alguns segundos +(cerca de 5 segundos atualmente), o usuário receberá a vergonhosa mensagem "aplicativo +não respondendo" (ANR). O usuário poderá, então, decidir fechar o aplicativo e desinstalá-lo +se estiver descontente.

    + +

    Além disso, o kit de ferramentas de IU do Android não é seguro para encadeamentos. Portanto, você não deve manipular a IU +a partir de um encadeamento de trabalho — deve-se realizar toda a manipulação para a interface do usuário +a partir do encadeamento da IU. Com isso dito, há duas regras simples para o modelo de encadeamento único do Android:

    + +
      +
    1. Não bloquear o encadeamento da IU +
    2. Não acessar o kit de ferramentas de IU do Android fora do encadeamento da IU +
    + +

    Encadeamentos de trabalho

    + +

    Por causa do modelo de encadeamento único descrito acima, é essencial para a capacidade de resposta da IU do aplicativo +que você não bloqueie o encadeamento da IU. Caso tenha operações a realizar +que não sejam instantâneas, deverá certificar-se de fazê-las em encadeamentos separados (encadeamentos de "segundo plano" +ou "de trabalho").

    + +

    Por exemplo, abaixo apresenta-se o código de uma escuta de clique que baixa uma imagem de um encadeamento separado +e exibe-a em uma {@link android.widget.ImageView}:

    + +
    +public void onClick(View v) {
    +    new Thread(new Runnable() {
    +        public void run() {
    +            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
    +            mImageView.setImageBitmap(b);
    +        }
    +    }).start();
    +}
    +
    + +

    À primeira vista, parece não apresentar problemas, pois ele cria um novo encadeamento para lidar +com a operação de rede. No entanto, ele viola a segunda regra do modelo de encadeamento único: não acessar o kit de ferramentas de IU do Android +de fora do encadeamento da IU — este exemplo modifica o {@link +android.widget.ImageView} a partir do encadeamento de trabalho em vez de o encadeamento da IU. Isto pode resultar em comportamentos +inesperados e indefinidos, que podem ser difíceis de rastrear e consumir bastante tempo.

    + +

    Para resolver este problema, o Android oferece várias maneiras de acessar o encadeamento da IU a partir +de outros encadeamentos. Abaixo há uma lista dos métodos que podem ajudar:

    + +
      +
    • {@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}
    • +
    • {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}
    • +
    • {@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}
    • +
    + +

    Por exemplo, é possível resolver o código acima usando o método {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)}:

    + +
    +public void onClick(View v) {
    +    new Thread(new Runnable() {
    +        public void run() {
    +            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
    +            mImageView.post(new Runnable() {
    +                public void run() {
    +                    mImageView.setImageBitmap(bitmap);
    +                }
    +            });
    +        }
    +    }).start();
    +}
    +
    + +

    Agora esta implementação é segura para encadeamentos: a operação de rede é concluída a partir de um encadeamento separado +enquanto que a {@link android.widget.ImageView} é manipulada a partir do encadeamento da IU.

    + +

    No entanto, à medida que a complexidade da operação aumenta, este tipo de código pode tornar-se complexo +e difícil demais para ser mantido. Para lidar com interações mais complexas com um encadeamento de trabalho, considere +usar um {@link android.os.Handler} nele para processar as mensagens entregues +pelo encadeamento da IU. Apesar de que, talvez, a melhor solução seja estender a classe {@link android.os.AsyncTask}, +que simplifica a execução das tarefas do encadeamento de trabalho que precisam interagir com a IU.

    + + +

    Uso de AsyncTask

    + +

    {@link android.os.AsyncTask} permite que você trabalhe de forma assíncrona +na interface do usuário. Ela realiza operações de bloqueio em um encadeamento de trabalho e, em seguida, publica os resultados +no encadeamento da IU, sem que você precise lidar com os encadeamentos e/ou os manipuladores.

    + +

    Para usá-la, você deve atribuir a subclasse {@link android.os.AsyncTask} e implementar o método de retorno de chamada {@link +android.os.AsyncTask#doInBackground doInBackground()}, que executa uma série +de encadeamentos de segundo plano. Para atualizar a IU, deve-se implementar {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, que entrega o resultado de {@link +android.os.AsyncTask#doInBackground doInBackground()} e é executado no encadeamento da IU para que seja possível +atualizar a IU de forma segura. É possível, em seguida, executar a tarefa chamando {@link android.os.AsyncTask#execute execute()} +a partir do encadeamento da IU.

    + +

    Por exemplo, é possível implementar o exemplo anterior usando {@link android.os.AsyncTask} +da seguinte forma:

    + +
    +public void onClick(View v) {
    +    new DownloadImageTask().execute("http://example.com/image.png");
    +}
    +
    +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    +    /** The system calls this to perform work in a worker thread and
    +      * delivers it the parameters given to AsyncTask.execute() */
    +    protected Bitmap doInBackground(String... urls) {
    +        return loadImageFromNetwork(urls[0]);
    +    }
    +    
    +    /** The system calls this to perform work in the UI thread and delivers
    +      * the result from doInBackground() */
    +    protected void onPostExecute(Bitmap result) {
    +        mImageView.setImageBitmap(result);
    +    }
    +}
    +
    + +

    Agora a IU está segura e o código está mais simples, pois ele separa o trabalho +em uma parte que deve ser feita em um encadeamento de trabalho e outra parte que deve ser feita no encadeamento da IU.

    + +

    Leia a referência {@link android.os.AsyncTask} para compreender melhor +o uso desta classe, mas a seguir há uma visão geral rápida sobre como ela funciona:

    + +
      +
    • É possível especificar o tipo dos parâmetros, valores de progresso e valor final +da tarefa, usando genéricos
    • +
    • O método {@link android.os.AsyncTask#doInBackground doInBackground()} é executado automaticamente +em um encadeamento de trabalho
    • +
    • {@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link +android.os.AsyncTask#onPostExecute onPostExecute()} e {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} são invocados no encadeamento da IU
    • +
    • O valor retornado por {@link android.os.AsyncTask#doInBackground doInBackground()} +é enviado para {@link android.os.AsyncTask#onPostExecute onPostExecute()}
    • +
    • É possível chamar {@link android.os.AsyncTask#publishProgress publishProgress()} a qualquer momento em {@link +android.os.AsyncTask#doInBackground doInBackground()} para executar {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} no encadeamento da IU
    • +
    • É possível cancelar a tarefa a qualquer momento, a partir de qualquer encadeamento
    • +
    + +

    Atenção: outro problema que pode ocorrer ao usar +um encadeamento de trabalho são reinicializações inesperadas da atividade devido a alterações na configuração em tempo de execução +(como quando o usuário altera a orientação da tela), que podem eliminar o encadeamento de trabalho. Para ver como é possível +manter uma tarefa durante uma dessas reinicializações e como cancelar adequadamente a tarefa +quando a atividade for eliminada, consulte o código fonte do aplicativo Prateleiras de exemplo.

    + + +

    Métodos seguros de encadeamento

    + +

    Em algumas situações, os métodos implementados podem ser chamados a partir de mais de um encadeamento e, portanto, +devem ser programados para serem seguros para encadeamento.

    + +

    Isto é especialmente verdadeiro para métodos que podem ser cancelados remotamente — como métodos em um serviço vinculado. Quando uma chamada +de um método implementado em um {@link android.os.IBinder} tiver origem no mesmo processo +em que {@link android.os.IBinder IBinder} estiver em execução, o método será executado no encadeamento do autor da chamada. +No entanto, quando a chamada tiver origem em outro processo, o método será executado em um encadeamento +escolhido a partir de uma série de encadeamentos que o sistema mantém no mesmo processo que {@link android.os.IBinder +IBinder} (ele não será executado no encadeamento da IU do processo). Por exemplo, enquanto o método +{@link android.app.Service#onBind onBind()} de um serviço seria chamado a partir de um encadeamento da IU do processo +de um serviço, os métodos implementados no objeto que {@link android.app.Service#onBind +onBind()} retorna (por exemplo, uma subclasse que implementa métodos de RPC) seriam chamados +a partir dos encadeamentos no conjunto. Como um serviço pode ter mais de um cliente, mais de um encadeamento no conjunto +pode envolver o mesmo método {@link android.os.IBinder IBinder} ao mesmo tempo. Os métodos {@link android.os.IBinder +IBinder} devem, portanto, ser implementados para serem seguros para encadeamentos.

    + +

    De forma semelhante, um provedor de conteúdo pode receber solicitações de dados que tenham origem em outros processos. +Apesar de as classes {@link android.content.ContentResolver} e {@link android.content.ContentProvider} +ocultarem os detalhes de como a comunicação entre processos é gerenciada, os métodos {@link +android.content.ContentProvider} que respondem a essas solicitações — os métodos {@link +android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert +insert()}, {@link android.content.ContentProvider#delete delete()}, {@link +android.content.ContentProvider#update update()} e {@link android.content.ContentProvider#getType +getType()} — serão chamados de um conjunto de encadeamentos no processo do provedor de conteúdo, não no encadeamento da IU +para o processo. Como esses métodos podem ser chamados a partir de qualquer quantidade de encadeamentos +ao mesmo tempo, eles também devem ser implementados para serem seguros para encadeamento.

    + + +

    Comunicação entre processos

    + +

    O Android oferece um mecanismo para comunicação entre processos (IPC) usando chamadas de procedimento remoto (RPCs), +onde um método é chamado por uma atividade ou outro componente de aplicativo, mas é executado +remotamente (em outro processo), com qualquer resultado retornado +de volta ao autor da chamada. Isto acarreta na decomposição de uma chamada de método e de seus dados para um nível em que o sistema operacional +possa entender, transmitindo-a do processo local e do espaço de endereço ao processo remoto +e ao espaço de endereço e, em seguida, remontando e restabelecendo a chamada lá. Os valores de retorno +são transmitidos na direção oposta. O Android fornece todo o código para realizar essas operações +de IPC para que você possa concentrar-se em definir e implementar a interface de programação de RPC.

    + +

    Para realizar o IPC, o aplicativo deve vincular-se a um serviço usando {@link +android.content.Context#bindService bindService()}. Para obter mais informações, consulte o guia do desenvolvedor Serviços.

    + + + diff --git a/docs/html-intl/intl/pt-br/guide/components/recents.jd b/docs/html-intl/intl/pt-br/guide/components/recents.jd new file mode 100644 index 0000000000000000000000000000000000000000..467f62067a546d5286ffa8ae5ce5180bfd918d7a --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=Tela de visão geral +page.tags="recents","overview" + +@jd:body + +
    +
    + +

    Neste documento

    +
      +
    1. Adição de tarefas à tela de visão geral +
        +
      1. Uso do sinalizador Intent para adicionar uma tarefa
      2. +
      3. Uso do atributo Activity para adicionar uma tarefa
      4. +
      +
    2. +
    3. Remoção de tarefas +
        +
      1. Uso da classe AppTask para remover tarefas
      2. +
      3. Retenção de tarefas terminadas
      4. +
      +
    4. +
    + +

    Classes principais

    +
      +
    1. {@link android.app.ActivityManager.AppTask}
    2. +
    3. {@link android.content.Intent}
    4. +
    + +

    Exemplo de código

    +
      +
    1. Aplicativos centralizados em documentos
    2. +
    + +
    +
    + +

    A tela de visão geral (também chamada de tela de recentes, lista de tarefas recentes ou aplicativos recentes) +é uma IU de nível de sistema que lista +atividades e tarefas acessadas recentemente. O +usuário pode navegar pela lista e selecionar uma tarefa a retomar ou remover uma tarefa da +lista deslizando-a para fora. Com a versão 5.0 do Android (API de nível 21), várias instâncias da +mesma atividade contendo diferentes documentos podem aparecer como tarefas na tela de visão geral. Por exemplo, o +Google Drive pode ter uma tarefa para cada um dos vários documentos do Google. Cada documento aparece como uma +tarefa na tela de visão geral.

    + + +

    Figura 1. A tela de visão geral mostrando três documentos do Google Drive, +cada um representado como uma tarefa separada.

    + +

    Normalmente, você deve permitir que o sistema defina como as tarefas e as +atividades são representadas na tela de visão geral e não precisa modificar esse comportamento. +No entanto, o seu aplicativo pode determinar como e quando as atividades aparecem na tela de visão geral. A +classe {@link android.app.ActivityManager.AppTask} permite gerenciar tarefas e os sinalizadores de atividade da classe +{@link android.content.Intent} permitem especificar quando uma atividade é adicionada ou removida da +tela de visão geral. Além disso, os atributos +<activity> permitem definir o comportamento no manifesto.

    + +

    Adição de tarefas à tela de visão geral

    + +

    Usar os sinalizadores da classe {@link android.content.Intent} para adicionar uma tarefa permite maior controle sobre +quando e como um documento é aberto ou reaberto na tela de visão geral. Ao usar os atributos +<activity>, +é possível escolher entre sempre abrir o documento em uma nova tarefa ou reutilizar uma +tarefa existente para o documento.

    + +

    Uso do sinalizador Intent para adicionar uma tarefa

    + +

    Ao criar um novo documento para a atividade, você chama o método +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +da classe {@link android.app.ActivityManager.AppTask}, passando a ele a intenção que +inicia a atividade. Para inserir uma quebra lógica para que o sistema trate a atividade como uma nova +tarefa na tela de visão geral, passe o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +no método {@link android.content.Intent#addFlags(int) addFlags()} da {@link android.content.Intent} +que inicia a atividade.

    + +

    Observação: o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +substitui o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, +obsoleto a partir do Android 5.0 (API de nível 21).

    + +

    Se você usar o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} ao criar +o novo documento, o sistema sempre criará uma nova tarefa com a atividade-alvo como raiz. +Essa configuração permite que o mesmo documento seja aberto em mais de uma tarefa. O código a seguir demonstra +como a atividade principal faz isso:

    + +

    +DocumentCentricActivity.java

    +
    +public void createNewDocument(View view) {
    +      final Intent newDocumentIntent = newDocumentIntent();
    +      if (useMultipleTasks) {
    +          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    +      }
    +      startActivity(newDocumentIntent);
    +  }
    +
    +  private Intent newDocumentIntent() {
    +      boolean useMultipleTasks = mCheckbox.isChecked();
    +      final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
    +      newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
    +      newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
    +      return newDocumentIntent;
    +  }
    +
    +  private static int incrementAndGet() {
    +      Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
    +      return mDocumentCounter++;
    +  }
    +}
    +
    + +

    Observação: Atividades iniciadas com o sinalizador {@code FLAG_ACTIVITY_NEW_DOCUMENT} +devem ter o valor do atributo {@code android:launchMode="standard"} (o padrão) definido no +manifesto.

    + +

    Quando a atividade principal inicia uma nova atividade, o sistema procura nas tarefas existentes uma +cuja intenção corresponda ao nome do componente da intenção e aos dados de Intent para a atividade. Se a tarefa +não for encontrada ou se a intenção continha o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, +uma nova tarefa será criada com a atividade como raiz. Se o sistema encontrar uma tarefa, ele a trará +para a frente e passará a nova intenção para {@link android.app.Activity#onNewIntent onNewIntent()}. +A nova atividade receberá a intenção e criará um novo documento na tela de visão geral, como no +exemplo a seguir:

    + +

    +NewDocumentActivity.java

    +
    +@Override
    +protected void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.activity_new_document);
    +    mDocumentCount = getIntent()
    +            .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
    +    mDocumentCounterTextView = (TextView) findViewById(
    +            R.id.hello_new_document_text_view);
    +    setDocumentCounterText(R.string.hello_new_document_counter);
    +}
    +
    +@Override
    +protected void onNewIntent(Intent intent) {
    +    super.onNewIntent(intent);
    +    /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
    +    is reused to create a new document.
    +     */
    +    setDocumentCounterText(R.string.reusing_document_counter);
    +}
    +
    + + +

    Uso do atributo Activity para adicionar uma tarefa

    + +

    Uma atividade também pode especificar em seu manifesto que sempre iniciará uma nova tarefa usando +o atributo <activity>, + +{@code android:documentLaunchMode}. Esse atributo tem quatro valores que produzem os seguintes +efeitos quando o usuário abre um documento com o aplicativo:

    + +
    +
    "{@code intoExisting}"
    +
    A atividade reutiliza uma tarefa existente para o documento. Isso é o mesmo que configurar o + sinalizador {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} sem configurar + o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, como descrito em + Uso do sinalizador Intent para adicionar uma tarefa acima.
    + +
    "{@code always}"
    +
    A atividade cria uma nova tarefa para o documento, mesmo se o mesmo já estiver aberto. Usar + esse valor é o mesmo que configurar os sinalizadores {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + e {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.
    + +
    "{@code none”}"
    +
    A atividade não cria uma nova tarefa para o documento. A tela de visão geral trata a + atividade como aconteceria por padrão: ela exibe uma tarefa para o aplicativo, que + retoma a atividade invocada por último pelo usuário.
    + +
    "{@code never}"
    +
    A atividade não cria uma nova tarefa para o documento. Definir esse valor substitui o + comportamento dos sinalizadores {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + e {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, caso um deles esteja definido + na intenção, e a tela de visão geral exibe uma tarefa para o aplicativo, que retoma a + atividade invocada por último pelo usuário.
    +
    + +

    Observação: para valores diferentes de {@code none} e {@code never}, a +atividade deve ser definida com {@code launchMode="standard"}. Se esse atributo não for especificado, +{@code documentLaunchMode="none"} será usado.

    + +

    Remoção de tarefas

    + +

    Por padrão, uma tarefa de documento é automaticamente removida da tela de visão geral quando a atividade +termina. Esse comportamento pode ser substituído com a classe {@link android.app.ActivityManager.AppTask}, +com um sinalizador {@link android.content.Intent} ou com um atributo +<activity>.

    + +

    É possível excluir inteiramente uma tarefa da tela de visão geral definindo o +atributo <activity>, + +{@code android:excludeFromRecents} como {@code true}.

    + +

    É possível definir o número máximo de tarefas que o aplicativo pode incluir na tela de visão geral definindo +o atributo <activity> +{@code android:maxRecents} + como um valor inteiro. O padrão é 16. Quando o número máximo de tarefas é atingido, a tarefa usada menos +recentemente é removida da tela de visão geral. O valor máximo de {@code android:maxRecents} +é 50 (25 em dispositivos com pouca memória); valores menores que 1 não são válidos.

    + +

    Uso da classe AppTask para remover tarefas

    + +

    Na atividade que cria uma nova tarefa na tela de visão geral, é possível +especificar quando remover a tarefa e terminar todas as atividades associadas a ela chamando +o método {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}.

    + +

    +NewDocumentActivity.java

    +
    +public void onRemoveFromRecents(View view) {
    +    // The document is no longer needed; remove its task.
    +    finishAndRemoveTask();
    +}
    +
    + +

    Observação: o uso +do método {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} +substitui o uso do sinalizador {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} +discutido abaixo.

    + +

    Retenção de tarefas terminadas

    + +

    Se você deseja reter uma tarefa na tela de visão geral, mesmo que a atividade tenha terminado, passe +o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} no +método {@link android.content.Intent#addFlags(int) addFlags()} da Intent que inicia a atividade.

    + +

    +DocumentCentricActivity.java

    +
    +private Intent newDocumentIntent() {
    +    final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
    +    newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
    +      android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
    +    newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
    +    return newDocumentIntent;
    +}
    +
    + +

    Para obter o mesmo efeito, defina o +atributo <activity> + +{@code android:autoRemoveFromRecents} como {@code false}. O valor padrão é {@code true} +para atividades de documentos e {@code false} para atividades comuns. Usar esse atributo substitui +o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} discutido anteriormente.

    + + + + + + + diff --git a/docs/html-intl/intl/pt-br/guide/components/services.jd b/docs/html-intl/intl/pt-br/guide/components/services.jd new file mode 100644 index 0000000000000000000000000000000000000000..123d90ac9cb32554292672cfa0bb870b43475b42 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=Serviços +@jd:body + + + + +

    Um {@link android.app.Service} é um componente do aplicativo que pode realizar +operações longas e não fornece uma interface do usuário. Outro componente do aplicativo +pode iniciar um serviço e ele continuará em execução em segundo plano mesmo que o usuário +alterne para outro aplicativo. Além disso, um componente poderá vincular-se a um serviço +para interagir com ele e até estabelecer comunicação entre processos (IPC). Por exemplo, um serviço pode lidar +com operações de rede, reproduzir música, executar E/S de arquivos, ou interagir com um provedor de conteúdo, +tudo a partir do segundo plano.

    + +

    Um serviço pode essencialmente ter duas formas:

    + +
    +
    Iniciado
    +
    Um serviço é "iniciado" quando um componente do aplicativo (como um atividade) +inicia-o chamando {@link android.content.Context#startService startService()}. Quando iniciado, um serviço +pode ficar em execução em segundo plano indefinidamente, mesmo que o componente que o iniciou seja destruído. Geralmente, +um serviço iniciado realiza uma única operação e não retorna um resultado para o autor da chamada. +Por exemplo, ele pode fazer download ou upload de um arquivo pela rede. Quando a operação for concluída, +o serviço deverá ser interrompido.
    +
    Vinculado
    +
    Um serviço é "vinculado" quando um componente do aplicativo o vincula chamando {@link +android.content.Context#bindService bindService()}. Um serviço vinculado oferece uma interface +servidor-cliente que permite que os componentes interajam com a interface, enviem solicitações, obtenham resultados, +mesmo em processos com comunicação interprocessual (IPC). Um serviço vinculado permanece em execução somente enquanto +outro componente do aplicativo estiver vinculado a ele. Vários componentes podem ser vinculados ao serviço de uma só vez, +mas quando todos desfizerem o vínculo, o serviço será destruído.
    +
    + +

    Apesar de esta documentação discutir os dois tipos de serviços separadamente, +um serviço pode funcionar das duas maneiras — ele pode ser iniciado (para permanecer em execução indefinidamente) e também permitir a vinculação. +Basta você implementar alguns métodos de retorno de chamada: {@link +android.app.Service#onStartCommand onStartCommand()} para permitir que os componentes iniciem o serviço e {@link +android.app.Service#onBind onBind()} para permitir a vinculação.

    + +

    Se o aplicativo for iniciado, vinculado ou ambos, qualquer componente do aplicativo +poderá usar o serviço (mesmo a partir de um aplicativo separado), da mesma forma que qualquer componente poderá usar +uma atividade — iniciando com uma {@link android.content.Intent}. No entanto, é possível declarar +o serviço como privado, no arquivo do manifesto, e bloquear o acesso de outros aplicativos. Isto é discutido com mais detalhes +na seção Declaração do serviço +no manifesto.

    + +

    Atenção: um serviço é executado +no encadeamento principal em seu processo de hospedagem — o serviço não cria seu próprio encadeamento +e não é executado em um processo separado (salvo se especificado). Isso significa que, + se o serviço for realizar qualquer trabalho intensivo de CPU ou operações de bloqueio (como reprodução +de MP3 ou rede), você deve criar um novo encadeamento dentro do serviço. Usando um encadeamento separado, +você reduzirá o risco da ocorrência de erros de Aplicativo não respondendo (ANR) +e o encadeamento principal do aplicativo poderá permanecer dedicado à interação do usuário com as atividades.

    + + +

    Conceitos básicos

    + + + +

    Para criar um serviço, você deve criar uma subclasse de {@link android.app.Service} +(ou uma das subclasses existentes). Na implementação, será necessário substituir alguns métodos de retorno de chamada +que lidem com aspectos essenciais do ciclo de vida do serviço e forneçam um mecanismo para vincular +componentes ao serviço, se apropriado. Os dois métodos mais importantes de retorno de chamada que você deve substituir são:

    + +
    +
    {@link android.app.Service#onStartCommand onStartCommand()}
    +
    O sistema chama este método quando outro componente, como uma atividade, +solicita que o serviço seja iniciado, chamando {@link android.content.Context#startService +startService()}. Quando este método é executado, o serviço é iniciado e pode ser executado +em segundo plano indefinidamente. Se implementar isto, é de sua responsabilidade interromper o serviço +quando o trabalho for concluído, chamando {@link android.app.Service#stopSelf stopSelf()} ou {@link +android.content.Context#stopService stopService()} (caso queira somente fornecer a vinculação, +não é necessário implementar este método).
    +
    {@link android.app.Service#onBind onBind()}
    +
    O sistema chama este método quando outro componente quer vincular-se +ao serviço (como para realizações de RPC) chamando {@link android.content.Context#bindService +bindService()}. Na implementação deste método, você deve fornecer uma interface que os clientes +usem para comunicar-se com o serviço, retornando um {@link android.os.IBinder}. Você sempre deve implementar este método, +mas, se não quiser permitir a vinculação, retorne nulo.
    +
    {@link android.app.Service#onCreate()}
    +
    O sistema chama este método quando o serviço é criado pela primeira vez para realizar procedimentos únicos +de configuração (antes de chamar {@link android.app.Service#onStartCommand onStartCommand()} ou +{@link android.app.Service#onBind onBind()}). Se o serviço já estiver em execução, este método +não é chamado.
    +
    {@link android.app.Service#onDestroy()}
    +
    O sistema chama este método quando o serviço não é mais usado e está sendo destruído. +O serviço deve implementar isto para limpar quaisquer recursos, como encadeamentos, escutas +registradas, receptores etc. Esta é a última chamada que o serviço recebe.
    +
    + +

    Se um componente iniciar o serviço chamando {@link +android.content.Context#startService startService()} (o que resulta em uma chamada para {@link +android.app.Service#onStartCommand onStartCommand()}), +o serviço permanecerá em execução até ser interrompido por conta própria com {@link android.app.Service#stopSelf()} ou por outro componente +chamando {@link android.content.Context#stopService stopService()}.

    + +

    Se um componente +chamar {@link android.content.Context#bindService bindService()} para criar o serviço (e {@link +android.app.Service#onStartCommand onStartCommand()} não for chamado), o serviço será executado +somente enquanto o componente estiver vinculado a ele. Quando o serviço for desvinculado de todos os clientes, +o sistema o destruirá.

    + +

    O sistema Android forçará a interrupção de um serviço somente quando a memória estiver baixa e precisar +recuperar os recursos do sistema para a atividade com que o usuário estiver interagindo. Se o serviço estiver vinculado a uma atividade que o usuário +tenha atribuído foco, é menos provável que ele seja eliminado. E, se o serviço for declarado para ser executado em primeiro plano (discutido posteriormente), então ele quase nunca será eliminado. +Caso contrário, se o serviço for iniciado e estiver em uma longa execução, o sistema reduzirá sua posição +na lista de tarefas de segundo plano no decorrer do tempo e o serviço se tornará altamente suscetível +à eliminação — se o serviço for iniciado, então você deve programá-lo para lidar +com reinicializações pelo sistema. Se o sistema eliminar o seu serviço, ele reiniciará assim que os recursos +estiverem disponíveis novamente (apesar de isto também depender do valor que você retornar de {@link +android.app.Service#onStartCommand onStartCommand()}, como discutido a seguir). Para obter mais informações sobre +quando o sistema deve destruir um serviço, consulte o documento Processos e encadeamentos +.

    + +

    Nas seguintes seções, você verá como é possível criar cada tipo de serviço e como +usá-los a partir de outros componentes do aplicativo.

    + + + +

    Declaração de serviço no manifesto

    + +

    Como atividades (e outros componentes), você deve declarar todos os serviços no arquivo de manifesto +do aplicativo.

    + +

    Para declarar o serviço, adicione um elemento {@code <service>} +como filho do elemento {@code <application>} +. Por exemplo:

    + +
    +<manifest ... >
    +  ...
    +  <application ... >
    +      <service android:name=".ExampleService" />
    +      ...
    +  </application>
    +</manifest>
    +
    + +

    Consulte a referência de elemento {@code <service>} +para obter mais informações sobre como declarar o serviço no manifesto.

    + +

    É possível incluir outros atributos no elemento {@code <service>} +para definir propriedades como permissões necessárias para iniciar o serviço e o processo +em que o serviço deve ser executado. O atributo {@code android:name} +é o único necessário — ele especifica o nome da classe do serviço. Ao publicar o aplicativo, + não deve-se alterar-lhe o nome porque, se isso acontecer, há riscos de ocorrência de erros +de código devido à dependência de intenções explícitas para iniciar ou vincular o serviço (leia a publicação do blogue, Coisas +que não podem mudar). + +

    Para garantir a segurança do aplicativo, sempre use uma intenção explícita ao iniciar ou vincular +{@link android.app.Service} e não declare filtros de intenção para o serviço. Caso seja essencial permitir alguma ambiguidade, como qual serviço deve ser usado para iniciar, +é possível fornecer filtros de intenções +para os serviços e excluir o nome do componente de {@link +android.content.Intent}, mas é preciso definir o pacote para a intenção com {@link +android.content.Intent#setPackage setPackage()}, que fornece desambiguidade suficiente para +o serviço alvo.

    + +

    Além disso, é possível garantir que o serviço esteja disponível somente para o aplicativo +incluindo o atributo {@code android:exported} +e definindo-o como {@code "false"}. Isto impede efetivamente que outros aplicativos iniciem o serviço, + mesmo usando uma intenção explícita.

    + + + + +

    Criação de um serviço iniciado

    + +

    Um serviço iniciado é o que outro componente inicia chamando {@link +android.content.Context#startService startService()}, resultando em uma chamada para o método +{@link android.app.Service#onStartCommand onStartCommand()} do serviço.

    + +

    Quando um serviço é iniciado, ele tem um ciclo de vida que não depende do componente +que o iniciou. Além disso, o serviço pode ser executado em segundo plano indefinidamente, +mesmo se o componente que o iniciou for eliminado. Além disso, o serviço deve ser interrompido quando o seu trabalho +for realizado, chamando {@link android.app.Service#stopSelf stopSelf()}. Outros componentes podem interrompê-lo +chamando {@link android.content.Context#stopService stopService()}.

    + +

    Um componente do aplicativo, como uma atividade, pode iniciar o serviço chamando {@link +android.content.Context#startService startService()} e passando {@link android.content.Intent}, +que especifica o serviço e inclui os dados para o serviço usar. O serviço +recebe esta {@link android.content.Intent} no método {@link android.app.Service#onStartCommand +onStartCommand()}.

    + +

    Por exemplo, presuma que uma atividade precise salvar alguns dados em um banco de dados on-line. A atividade pode iniciar +um serviço de acompanhamento e entregar a ele os dados a salvar passando uma intenção para {@link +android.content.Context#startService startService()}. O serviço recebe a intenção em {@link +android.app.Service#onStartCommand onStartCommand()}, conecta-se à Internet e realiza +a transação no banco de dados. Quando a operação é concluída, o serviço é interrompido +e destruído.

    + +

    Atenção: um serviço é executado no mesmo processo que o aplicativo +em que ele é declarado e no encadeamento principal do aplicativo, por padrão. Portanto, se o serviço +realizar operações intensivas ou de bloqueio quando o usuário interagir com uma atividade do mesmo aplicativo, +diminuirá o desempenho da atividade. Para evitar impacto no desempenho do aplicativo, +deve-se iniciar um novo encadeamento dentro do serviço.

    + +

    Geralmente, há duas classes que podem ser estendidas para criar um serviço iniciado:

    +
    +
    {@link android.app.Service}
    +
    Esta é a classe de base para todos os serviços. Ao estender esta classe, é importante +criar um novo encadeamento para fazer todo o trabalho do serviço, pois o serviço usa +o encadeamento principal do aplicativo, por padrão, o que deve diminuir o desempenho de qualquer atividade +que o aplicativo estiver executando.
    +
    {@link android.app.IntentService}
    +
    Esta é uma subclasse de {@link android.app.Service} que usa um encadeamento de trabalho para lidar +com todas as solicitações de inicialização, uma por vez. Esta é a melhor opção se não quiser que o serviço +lide com várias solicitações simultaneamente. Tudo que precisa fazer é implementar {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, que recebe a intenção para cada solicitação +de início para que você possa realizar o trabalho de segundo plano.
    +
    + +

    As seguintes seções descrevem como é possível implementar o serviço usando +uma dessas classes.

    + + +

    Extensão da classe IntentService

    + +

    Como a maioria dos serviços não precisam lidar com várias solicitações simultaneamente +(o que pode ser perigoso para situações de vários encadeamentos), é melhor +se o serviço for implementado usando a classe {@link android.app.IntentService}.

    + +

    O {@link android.app.IntentService} faz o seguinte:

    + +
      +
    • Cria um encadeamento de trabalho padrão que executa todas as intenções entregues a {@link +android.app.Service#onStartCommand onStartCommand()} separado do encadeamento principal +do aplicativo.
    • +
    • Cria uma fila de trabalho que passa uma intenção por vez à implementação {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, para que nunca seja necessário +preocupar-se com vários encadeamentos.
    • +
    • Interrompe o serviço depois que todas as solicitações forem resolvidas para que não seja necessário chamar +{@link android.app.Service#stopSelf}.
    • +
    • Fornece implementações padrão de {@link android.app.IntentService#onBind onBind()} +que retornam como nulo.
    • +
    • Fornece uma implementação padrão de {@link android.app.IntentService#onStartCommand +onStartCommand()} que envia a intenção para a fila de trabalho e, em seguida, para a implementação de {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.
    • +
    + +

    Tudo isso adiciona-se ao fato de que basta implementar {@link +android.app.IntentService#onHandleIntent onHandleIntent()} para fazer o trabalho fornecido +pelo cliente (embora também seja necessário fornecer um pequeno construtor para o serviço).

    + +

    A seguir há um exemplo de implementação de {@link android.app.IntentService}:

    + +
    +public class HelloIntentService extends IntentService {
    +
    +  /**
    +   * A constructor is required, and must call the super {@link android.app.IntentService#IntentService}
    +   * constructor with a name for the worker thread.
    +   */
    +  public HelloIntentService() {
    +      super("HelloIntentService");
    +  }
    +
    +  /**
    +   * The IntentService calls this method from the default worker thread with
    +   * the intent that started the service. When this method returns, IntentService
    +   * stops the service, as appropriate.
    +   */
    +  @Override
    +  protected void onHandleIntent(Intent intent) {
    +      // Normally we would do some work here, like download a file.
    +      // For our sample, we just sleep for 5 seconds.
    +      long endTime = System.currentTimeMillis() + 5*1000;
    +      while (System.currentTimeMillis() < endTime) {
    +          synchronized (this) {
    +              try {
    +                  wait(endTime - System.currentTimeMillis());
    +              } catch (Exception e) {
    +              }
    +          }
    +      }
    +  }
    +}
    +
    + +

    É tudo que você precisa: um construtor e uma implementação de {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.

    + +

    Caso decida substituir outros métodos de retorno de chamada, como {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()} ou {@link +android.app.IntentService#onDestroy onDestroy()}, certifique-se de chamar a super-implementação +para que o {@link android.app.IntentService} possa lidar adequadamente com a vida do encadeamento de trabalho.

    + +

    Por exemplo, {@link android.app.IntentService#onStartCommand onStartCommand()} deve retornar +a implementação padrão (que é como a intenção é entregue para {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):

    + +
    +@Override
    +public int onStartCommand(Intent intent, int flags, int startId) {
    +    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    +    return super.onStartCommand(intent,flags,startId);
    +}
    +
    + +

    Além de {@link android.app.IntentService#onHandleIntent onHandleIntent()}, o único método +que não precisa ser usado para chamar a superclasse é {@link android.app.IntentService#onBind +onBind()} (mas é necessário implementá-lo se o serviço permitir vinculações).

    + +

    Na próxima seção, você verá como o mesmo tipo de serviço é implementado ao estender +a classe de base {@link android.app.Service}, que possui muito mais códigos, mas que pode +ser apropriada para lidar com solicitações de inicialização simultâneas.

    + + +

    Extensão da classe Service

    + +

    Como você viu na seção anterior, o uso de {@link android.app.IntentService} +torna a implementação de um serviço iniciado muito simples. Se, no entanto, você precisa do serviço +para realizar vários encadeamentos (em vez de processar as solicitações de inicialização por meio de uma fila de trabalho), +é possível estender a classe {@link android.app.Service} para lidar com cada intenção.

    + +

    Para efeito de comparação, o seguinte exemplo de código é uma implementação da classe {@link +android.app.Service} que realiza exatamente o mesmo trabalho que o exemplo acima usando {@link +android.app.IntentService}. Ou seja, para cada solicitação de inicialização, ela usa um encadeamento +de trabalho para realizar o trabalho e processa apenas uma solicitação por vez.

    + +
    +public class HelloService extends Service {
    +  private Looper mServiceLooper;
    +  private ServiceHandler mServiceHandler;
    +
    +  // Handler that receives messages from the thread
    +  private final class ServiceHandler extends Handler {
    +      public ServiceHandler(Looper looper) {
    +          super(looper);
    +      }
    +      @Override
    +      public void handleMessage(Message msg) {
    +          // Normally we would do some work here, like download a file.
    +          // For our sample, we just sleep for 5 seconds.
    +          long endTime = System.currentTimeMillis() + 5*1000;
    +          while (System.currentTimeMillis() < endTime) {
    +              synchronized (this) {
    +                  try {
    +                      wait(endTime - System.currentTimeMillis());
    +                  } catch (Exception e) {
    +                  }
    +              }
    +          }
    +          // Stop the service using the startId, so that we don't stop
    +          // the service in the middle of handling another job
    +          stopSelf(msg.arg1);
    +      }
    +  }
    +
    +  @Override
    +  public void onCreate() {
    +    // Start up the thread running the service.  Note that we create a
    +    // separate thread because the service normally runs in the process's
    +    // main thread, which we don't want to block.  We also make it
    +    // background priority so CPU-intensive work will not disrupt our UI.
    +    HandlerThread thread = new HandlerThread("ServiceStartArguments",
    +            Process.THREAD_PRIORITY_BACKGROUND);
    +    thread.start();
    +
    +    // Get the HandlerThread's Looper and use it for our Handler
    +    mServiceLooper = thread.getLooper();
    +    mServiceHandler = new ServiceHandler(mServiceLooper);
    +  }
    +
    +  @Override
    +  public int onStartCommand(Intent intent, int flags, int startId) {
    +      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    +
    +      // For each start request, send a message to start a job and deliver the
    +      // start ID so we know which request we're stopping when we finish the job
    +      Message msg = mServiceHandler.obtainMessage();
    +      msg.arg1 = startId;
    +      mServiceHandler.sendMessage(msg);
    +
    +      // If we get killed, after returning from here, restart
    +      return START_STICKY;
    +  }
    +
    +  @Override
    +  public IBinder onBind(Intent intent) {
    +      // We don't provide binding, so return null
    +      return null;
    +  }
    +
    +  @Override
    +  public void onDestroy() {
    +    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    +  }
    +}
    +
    + +

    Como pode ver, é muito mais trabalhoso do que usar {@link android.app.IntentService}.

    + +

    No entanto, como você lida com cada chamada de {@link android.app.Service#onStartCommand +onStartCommand()}, é possível realizar várias solicitações simultaneamente. Isso não é o que este exemplo faz, +mas, se for o que quer, é possível criar um novo encadeamento +para cada solicitação e executá-las na hora (em vez de esperar que a solicitação anterior seja concluída).

    + +

    Observe que o método {@link android.app.Service#onStartCommand onStartCommand()} deve retornar +um número inteiro. O número inteiro é um valor que descreve como o sistema deve continuar o serviço +no evento em que o sistema o eliminar (como discutido acima, a implementação padrão de {@link +android.app.IntentService} lida com isto, apesar de ser possível modificá-lo). O valor de retorno +de {@link android.app.Service#onStartCommand onStartCommand()} deve ser uma das seguintes +constantes:

    + +
    +
    {@link android.app.Service#START_NOT_STICKY}
    +
    Se o sistema eliminar o serviço após o retorno de {@link android.app.Service#onStartCommand +onStartCommand()}, não recrie o serviço, a não ser que tenha +intenções pendentes para entregar. Esta é a opção mais segura para evitar executar o serviço quando desnecessário +e quando o aplicativo puder simplesmente reiniciar qualquer trabalho incompleto.
    +
    {@link android.app.Service#START_STICKY}
    +
    Se o sistema eliminar o serviço após o retorno de {@link android.app.Service#onStartCommand +onStartCommand()}, recrie o serviço e chame {@link +android.app.Service#onStartCommand onStartCommand()}, mas não entregue novamente a última intenção. +Em vez disso, o sistema chama {@link android.app.Service#onStartCommand onStartCommand()} com intenção nula, +a não ser que tenha intenções pendentes para iniciar o serviço e, +nesse caso, essas intenções são entregues. Isto é adequado para reprodutores de mídia (ou serviços semelhantes) +que não estejam executando comandos, mas estejam em execução indefinidamente e aguardando um trabalho.
    +
    {@link android.app.Service#START_REDELIVER_INTENT}
    +
    Se o sistema eliminar o serviço após o retorno de {@link android.app.Service#onStartCommand +onStartCommand()}, recrie o serviço e chame {@link +android.app.Service#onStartCommand onStartCommand()} com a última intenção que foi entregue +ao serviço. Quaisquer intenções pendentes são entregues, por sua vez. Isto é adequado para serviços que estejam realizando +um trabalho ativamente que deva ser retomado imediatamente, como o download de um arquivo.
    +
    +

    Para obter mais informações sobre esses valores de retorno, veja a documentação de referência vinculada +a cada constante.

    + + + +

    Início de um serviço

    + +

    É possível iniciar um dispositivo de uma atividade ou outro componente do aplicativo passando uma +{@link android.content.Intent} a {@link +android.content.Context#startService startService()}. O sistema Android chama o método {@link +android.app.Service#onStartCommand onStartCommand()} do serviço e passa a ele a {@link +android.content.Intent} (nunca deve-se chamar {@link android.app.Service#onStartCommand +onStartCommand()} diretamente).

    + +

    Por exemplo, uma atividade pode iniciar o serviço de exemplo na seção anterior ({@code +HelloSevice}) usando uma intenção explícita com {@link android.content.Context#startService +startService()}:

    + +
    +Intent intent = new Intent(this, HelloService.class);
    +startService(intent);
    +
    + +

    O método {@link android.content.Context#startService startService()} retorna imediatamente +e o sistema Android chama o método {@link android.app.Service#onStartCommand +onStartCommand()} do serviço. Se o serviço não estiver em execução, o sistemo primeiro chama {@link +android.app.Service#onCreate onCreate()} e, em seguida, chama {@link android.app.Service#onStartCommand +onStartCommand()}.

    + +

    Se o serviço não fornecer vinculação, a intenção entregue com {@link +android.content.Context#startService startService()} é o único modo de comunicação entre +o componente do aplicativo e o serviço. No entanto, se quiser que o serviço envie um resultado de volta, +o cliente que iniciar o serviço poderá criar um {@link android.app.PendingIntent} para uma transmissão +(com {@link android.app.PendingIntent#getBroadcast getBroadcast()}) e entregá-la ao serviço +na {@link android.content.Intent} que iniciar o serviço. O serviço pode então +usar a transmissão para entregar um resultado.

    + +

    Várias solicitações para iniciar o serviço resultam em diversas chamadas correspondentes +ao {@link android.app.Service#onStartCommand onStartCommand()} do serviço. No entanto, somente uma solicitação para interromper +o serviço (com {@link android.app.Service#stopSelf stopSelf()} ou {@link +android.content.Context#stopService stopService()}) é necessária para interrompê-lo.

    + + +

    Interrupção de um serviço

    + +

    Um serviço iniciado deve gerenciar seu próprio ciclo de vida. Ou seja, o sistema não interrompe +ou elimina o serviço, a não ser que tenha que recuperar a memória do sistema e o serviço +continuar em execução depois que {@link android.app.Service#onStartCommand onStartCommand()} retornar. Portanto, +o serviço deve ser interrompido chamando {@link android.app.Service#stopSelf stopSelf()} ou outro +componente pode interrompê-lo chamando {@link android.content.Context#stopService stopService()}.

    + +

    Ao solicitar o interrompimento com {@link android.app.Service#stopSelf stopSelf()} ou {@link +android.content.Context#stopService stopService()}, o sistema elimina o serviço +assim que possível.

    + +

    No entanto, se o serviço lida com várias solicitações para {@link +android.app.Service#onStartCommand onStartCommand()} ao mesmo tempo, não se deve interromper +o serviço ao terminar o processamento de uma solicitação de inicialização, pois é possível +que uma nova solicitação de inicialização tenha ocorrido (interromper no final da primeira solicitação eliminaria a segunda). Para evitar este problema, +é possível usar {@link android.app.Service#stopSelf(int)} para garantir que a solicitação +que interrompe o serviço sempre se baseie na solicitação de inicialização mais recente. Ou seja, ao chamar {@link +android.app.Service#stopSelf(int)}, você passa o ID da solicitação de inicialização (o startId +entregue a {@link android.app.Service#onStartCommand onStartCommand()}) para aquele que solicitação de interrompimento +corresponde. Em seguida, se o serviço receber uma nova solicitação de inicialização antes de ser possível chamar {@link +android.app.Service#stopSelf(int)}, o ID não corresponderá e o serviço não será interrompido.

    + +

    Atenção: é importante que um aplicativo interrompa seus serviços +quando terminar os trabalhos para evitar o desperdício dos recursos do sistema e consumo da bateria. Se necessário, +outros componentes podem interromper o serviço chamando {@link +android.content.Context#stopService stopService()}. Mesmo se for possível vincular-se ao serviço, +deve-se sempre interrompê-lo por conta própria se ele tiver recebido uma chamada de {@link +android.app.Service#onStartCommand onStartCommand()}.

    + +

    Para obter mais informações sobre o ciclo de vida de um serviço, consulte a seção Gerenciamento do ciclo de vida de um serviço abaixo.

    + + + +

    Criação de um serviço vinculado

    + +

    Um serviço vinculado permite que componentes de aplicativo sejam vinculados chamando {@link +android.content.Context#bindService bindService()} para criar uma conexão de longo prazo +(e, geralmente, não permite que os componentes o iniciem chamando {@link +android.content.Context#startService startService()}).

    + +

    Deve-se criar um serviço vinculado quando se deseja interagir com o serviço a partir de atividades +e outros componentes no aplicativo ou para expor algumas das funcionalidades do aplicativo +para outros aplicativos, por meio de comunicação entre processos (IPC).

    + +

    Para criar um serviço vinculado, você deve implementar o método de retorno de chamada {@link +android.app.Service#onBind onBind()} para retornar um {@link android.os.IBinder} +que define a interface para a comunicação com o serviço. Outros componentes de aplicativo podem chamar +{@link android.content.Context#bindService bindService()} para recuperar a interface +e começar a chamar métodos no serviço. O serviço vive somente para servir o componente do aplicativo +ao qual ele está vinculado. Portanto, quando não houver componentes vinculados ao serviço, o sistema o eliminará +(não é necessário interromper um serviço vinculado da mesma maneira que quando o serviço é iniciado +por meio de {@link android.app.Service#onStartCommand onStartCommand()}).

    + +

    Para criar um serviço vinculado, a primeira coisa a se fazer é definir a interface que especifica +como um cliente pode comunicar-se com o servidor. Esta interface entre o serviço +e um cliente deve ser uma implementação de {@link android.os.IBinder} e é o que o serviço deve retornar +a partir do método de retorno de chamada {@link android.app.Service#onBind +onBind()}. Quando o cliente receber {@link android.os.IBinder}, ele poderá começar +a interagir com o serviço por meio da interface.

    + +

    Vários clientes podem vincular-se ao serviço por vez. Quando um cliente terminar de interagir com o serviço, + ele chamará {@link android.content.Context#unbindService unbindService()} para desvincular-se. Quando não houver +clientes vinculados ao serviço, o sistema o eliminará.

    + +

    Há várias maneiras de implementar um serviço vinculado e a implementação é mais complicada +que um serviço iniciado. Logo, a discussão sobre serviços vinculados aparece em um documento separado +sobre Serviços vinculados.

    + + + +

    Enviar notificações ao usuário

    + +

    Quando em execução, um serviço pode notificar o usuário sobre eventos usando Notificações de aviso ou Notificações da barra de status.

    + +

    Uma notificação de aviso é uma mensagem que aparece na superfície da janela atual +por um breve momento antes de desaparecer, enquanto que uma notificação da barra de status fornece um ícone na barra de status +com uma mensagem que o usuário pode selecionar para realizar uma ação (como iniciar uma atividade).

    + +

    Geralmente, uma notificação da barra de status é a melhor técnica quando um trabalho de segundo plano é concluído +(como download +de arquivo completo) e o usuário pode agir a partir dele. Quando o usuário seleciona a notificação a partir +da vista expandida, ela pode iniciar uma atividade (como a vista do arquivo baixado).

    + +

    Consulte os guias de desenvolvedor Notificações de aviso ou Notificações da barra de status +para obter mais informações.

    + + + +

    Execução de serviço em primeiro plano

    + +

    Um serviço de primeiro plano é aquele +com que o usuário está ativamente interagindo e não é uma opção para o sistema eliminá-lo quando a memória estiver baixa. Um serviço de primeiro plano +deve fornecer uma notificação para a barra de status, que é colocada sob +"o cabeçalho "Em andamento", o que significa que a notificação não pode ser dispensada a não ser que o serviço +seja interrompido ou removido do primeiro plano.

    + +

    Por exemplo, um reprodutor de música que reproduz a partir de um serviço deve ser configurado +para permanecer em execução em primeiro plano, pois o usuário está +prestando atenção em sua operação explicitamente. A notificação na barra de status pode indicar a música atual +e permitir que o usuário inicie uma atividade para interagir com o reprodutor de música.

    + +

    Para solicitar que o serviço seja executado em primeiro plano, chame {@link +android.app.Service#startForeground startForeground()}. Este método usa dois parâmetros: um número inteiro +que identifica unicamente a notificação e {@link +android.app.Notification} para a barra de status. Por exemplo:

    + +
    +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
    +        System.currentTimeMillis());
    +Intent notificationIntent = new Intent(this, ExampleActivity.class);
    +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    +notification.setLatestEventInfo(this, getText(R.string.notification_title),
    +        getText(R.string.notification_message), pendingIntent);
    +startForeground(ONGOING_NOTIFICATION_ID, notification);
    +
    + +

    Atenção: O ID do número inteiro fornecido para {@link +android.app.Service#startForeground startForeground()} não deve ser zero.

    + + +

    Para remover o serviço do primeiro plano, chame {@link +android.app.Service#stopForeground stopForeground()}. Este método usa um booleano, +indicando se deve remover também a notificação da barra de status. Este método não interrompe +o serviço. No entanto, se você interromper o serviço enquanto estiver em execução em primeiro plano, +a notificação também será removida.

    + +

    Para obter mais informações sobre notificações, consulte Criação de notificações +da barra de status.

    + + + +

    Gerenciamento do ciclo de vida de um serviço

    + +

    O ciclo de vida de um serviço é muito mais simples do que o de uma atividade. No entanto, é ainda mais importante +que você preste muita atenção em como o serviço é criado e eliminado, pois ele +pode ser executado em primeiro plano sem que o usuário esteja ciente.

    + +

    O ciclo de vida do serviço — desde quando é criado até ser eliminado — pode seguir +dois caminhos:

    + +
      +
    • Um serviço iniciado +

      O serviço é criado quando outro componente chama {@link +android.content.Context#startService startService()}. Depois, o serviço permanece em execução indefinidamente e deve +interromper-se chamando {@link +android.app.Service#stopSelf() stopSelf()}. Outro componente também pode interromper +o serviço chamando {@link +android.content.Context#stopService stopService()}. Quando o serviço é interrompido, o sistema o elimina.

    • + +
    • Um serviço vinculado +

      O serviço é criado quando outro componente (um cliente) chama {@link +android.content.Context#bindService bindService()}. O cliente comunica-se com o serviço +pela interface {@link android.os.IBinder}. O cliente pode escolher encerrar a conexão chamando +{@link android.content.Context#unbindService unbindService()}. Vários clientes podem ser vinculados +ao mesmo serviço e, quando todos os vínculos terminam, o sistema destrói o serviço (o serviço +não precisa ser interrompido).

    • +
    + +

    Esses dois caminhos não são inteiramente separados. Ou seja, é possível vincular um serviço +que já foi iniciado com {@link android.content.Context#startService startService()}. Por exemplo, um serviço de música +de segundo plano pode ser iniciado chamando {@link android.content.Context#startService +startService()} com uma {@link android.content.Intent} que identifica a música reproduzida. Depois, +possivelmente quando o usuário quiser exercer mais controle sobre o reprodutor ou obter informações +sobre a música em reprodução, uma atividade pode ser vinculada ao serviço chamando {@link +android.content.Context#bindService bindService()}. Em casos como esse, {@link +android.content.Context#stopService stopService()} ou {@link android.app.Service#stopSelf +stopSelf()} não interrompe o serviço até que todos os clientes sejam desvinculados.

    + + +

    Implementação de retornos de chamada do ciclo de vida

    + +

    Como uma atividade, um serviço tem um método de ciclo de vida que é possível implementar +para monitorar as alterações no estado do serviço e realizar trabalhos em momentos adequados. O seguinte serviço de esqueleto +demonstra cada um dos métodos de ciclo de vida:

    + +
    +public class ExampleService extends Service {
    +    int mStartMode;       // indicates how to behave if the service is killed
    +    IBinder mBinder;      // interface for clients that bind
    +    boolean mAllowRebind; // indicates whether onRebind should be used
    +
    +    @Override
    +    public void {@link android.app.Service#onCreate onCreate}() {
    +        // The service is being created
    +    }
    +    @Override
    +    public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) {
    +        // The service is starting, due to a call to {@link android.content.Context#startService startService()}
    +        return mStartMode;
    +    }
    +    @Override
    +    public IBinder {@link android.app.Service#onBind onBind}(Intent intent) {
    +        // A client is binding to the service with {@link android.content.Context#bindService bindService()}
    +        return mBinder;
    +    }
    +    @Override
    +    public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) {
    +        // All clients have unbound with {@link android.content.Context#unbindService unbindService()}
    +        return mAllowRebind;
    +    }
    +    @Override
    +    public void {@link android.app.Service#onRebind onRebind}(Intent intent) {
    +        // A client is binding to the service with {@link android.content.Context#bindService bindService()},
    +        // after onUnbind() has already been called
    +    }
    +    @Override
    +    public void {@link android.app.Service#onDestroy onDestroy}() {
    +        // The service is no longer used and is being destroyed
    +    }
    +}
    +
    + +

    Observação: diferentemente dos métodos de retorno de chamada do ciclo de vida da atividade, +você não precisa chamar a implementação da superclasse.

    + + +

    Figura 2. Ciclo de vida do serviço. O diagrama à esquerda +mostra o ciclo de vida quando o serviço é criado com {@link android.content.Context#startService +startService()} e o diagrama à direita mostra o ciclo de vida quando o serviço +é criado com {@link android.content.Context#bindService bindService()}.

    + +

    Ao implementar esses métodos, é possível monitorar dois retornos (loops) aninhados no ciclo de vida do serviço:

    + +
      +
    • Todo o ciclo de vida de um serviço acontece entre o momento em que {@link +android.app.Service#onCreate onCreate()} é chamado e em que {@link +android.app.Service#onDestroy} retorna. Como uma atividade, um serviço faz a sua configuração inicial +em {@link android.app.Service#onCreate onCreate()} e libera todos os recursos restantes em {@link +android.app.Service#onDestroy onDestroy()}. Por exemplo, +um serviço de reprodução de música pode criar um encadeamento onde a música será reproduzida em {@link +android.app.Service#onCreate onCreate()}, e interromperá o encadeamento em {@link +android.app.Service#onDestroy onDestroy()}. + +

      Os métodos {@link android.app.Service#onCreate onCreate()} e {@link android.app.Service#onDestroy +onDestroy()} são chamados para todos os serviços, +se tiverem sido criados por {@link android.content.Context#startService startService()} ou {@link +android.content.Context#bindService bindService()}.

    • + +
    • O ciclo de vida ativo de um serviço começa com uma chamada de {@link +android.app.Service#onStartCommand onStartCommand()} ou {@link android.app.Service#onBind onBind()}. +Cada método entrega a {@link +android.content.Intent} que foi passada para {@link android.content.Context#startService +startService()} ou {@link android.content.Context#bindService bindService()}, respectivamente. +

      Se o serviço for iniciado, o ciclo de vida ativo terminará no mesmo momento +em que o ciclo de vida inteiro terminar (o serviço permanece ativo mesmo após o retorno de {@link android.app.Service#onStartCommand +onStartCommand()}). Se o serviço estiver vinculado, o ciclo de ida ativo acabará quando {@link +android.app.Service#onUnbind onUnbind()} retornar.

      +
    • +
    + +

    Observação: apesar de um serviço iniciado ser interrompido com uma chamada +de {@link android.app.Service#stopSelf stopSelf()} ou {@link +android.content.Context#stopService stopService()}, não há um retorno de chamada respectivo +para o serviço (não há retorno de chamada de {@code onStop()}). Portanto, a não ser que o serviço esteja vinculado a um cliente, +o sistema o eliminará quando for interrompido — {@link +android.app.Service#onDestroy onDestroy()} será o único retorno de chamada recebido.

    + +

    A figura 2 ilustra os métodos de retorno de chamada tradicionais para um serviço. Apesar de a figura separar +os serviços que são criados por {@link android.content.Context#startService startService()} +daqueles que são criados por {@link android.content.Context#bindService bindService()}, +observe que qualquer serviço, não importa como foi iniciado, pode permitir a vinculação de clientes. +Portanto, um serviço que já foi iniciado com {@link android.app.Service#onStartCommand +onStartCommand()} (por um cliente chamando {@link android.content.Context#startService startService()}) +ainda pode receber uma chamada de {@link android.app.Service#onBind onBind()} (quando um cliente chama +{@link android.content.Context#bindService bindService()}).

    + +

    Para obter mais informações sobre como criar um serviço que forneça vinculação, consulte o documento Serviços vinculados, +que aborda mais profundamente o método de retorno de chamada {@link android.app.Service#onRebind onRebind()} +na seção sobre Gerenciamento do ciclo de vida +de um serviço vinculado.

    + + + diff --git a/docs/html-intl/intl/pt-br/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/pt-br/guide/components/tasks-and-back-stack.jd new file mode 100644 index 0000000000000000000000000000000000000000..d309c6704f3765f19332f60021d1e5c20f08ffbe --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=Tarefas e pilhas de retorno +parent.title=Atividades +parent.link=activities.html +@jd:body + + + + +

    Os aplicativos normalmente contêm diversas atividades. Cada atividade +deve ser projetada com relação a tipos específicos de ações que o usuário pode realizar e que podem iniciar outras +atividades. Por exemplo: um aplicativo de e-mail pode ter uma atividade para exibir uma lista de novas mensagens. +Quando o usuário seleciona uma mensagem, uma nova atividade abre para exibir essa mensagem.

    + +

    As atividades podem também iniciar atividades existentes em outros aplicativos no dispositivo. Por +exemplo: se o seu aplicativo deseja enviar um e-mail, é possível definir uma intenção para realizar +uma ação de "enviar" e incluir alguns dados, como um endereço de e-mail e uma mensagem. Uma atividade de outro +aplicativo que se declara para tratar deste tipo de intenções, então, abre-se. Nesse caso, a intenção +destina-se ao envio de e-mails, portanto inicia-se uma atividade de “composição" do aplicativo de e-mail (se diversas atividades +forem compatíveis com a mesma intenção, o sistema permitirá que o usuário selecione qual usar). Quando o e-mail é +enviado, sua atividade reinicia, parecendo que a atividade de e-mail faz parte do seu aplicativo. Apesar +de as atividades serem de aplicativos diferentes, o Android mantém essa experiência +do usuário retilínea mantendo ambas as atividades na mesma tarefa.

    + +

    Tarefas são coleções de atividades com as quais os usuários interagem +ao realizar determinado trabalho. As atividades são organizadas em uma pilha (a pilha de retorno) +na ordem em que cada atividade é aberta.

    + + + +

    A tela inicial do dispositivo é o ponto de partida para a maioria das tarefas. Quando o usuário toca em um ícone no inicializador do +aplicativo + (ou em um atalho na tela inicial), essa tarefa do aplicativo acontece em primeiro plano. Se não +existir nenhuma tarefa para o aplicativo (se o aplicativo não tiver sido usado recentemente), uma nova tarefa +será criada e a atividade "principal" daquele aplicativo abrirá como a atividade raiz na pilha.

    + +

    Quando a atividade atual inicia outra, a nova atividade é colocada no topo da pilha +e recebe foco. A atividade anterior permanece na pilha, mas é interrompida. Quando uma atividade +para, o sistema retém o estado atual da interface do usuário. Quando o usuário pressiona o botão +Voltar +, a atividade atual é retirada do topo da pilha (a atividade é destruída) +e a atividade anterior reinicia (o estado anterior da IU é restaurado). Atividades na pilha nunca +são reorganizadas, somente colocadas e retiradas da pilha — colocadas na pilha quando iniciadas +pela atividade atual e retiradas quando o usuário se retira dela usando o botão Voltar. Desse modo, a pilha +de retorno +opera como uma estrutura de objeto UEPS (último que entra, primeiro que sai). A figura 1 +ilustra esse comportamento com uma linha cronológica exibindo o progresso entre atividades junto com a pilha de retorno +atual em cada ponto no tempo.

    + + +

    Figura 1. Representação de como cada nova atividade +em uma tarefa adiciona um item à pilha de retorno. Quando o usuário pressiona o botão Voltar, +a atividade atual é +destruída e a atividade anterior reinicia.

    + + +

    Se o usuário continua pressionando Voltar, cada atividade na pilha é retirada para +revelar +a anterior até que o usuário retorne à tela inicial (ou a qualquer atividade que estivesse em execução +no começo da tarefa). Quando todas as atividades forem removidas da pilha, a tarefa não existirá mais.

    + +
    +

    Figura 2. Duas tarefas: a tarefa B recebe a interação do usuário +em primeiro plano enquanto a tarefa A está em segundo plano aguardando para ser retomada.

    +
    +
    +

    Figura 3. Uma única atividade é instanciada diversas vezes.

    +
    + +

    As tarefas são unidades coesas que podem mover-se para "segundo plano" quando usuário inicia uma nova tarefa +ou ir para a tela inicial por meio do botão Página inicial. Quando em segundo plano, todas as atividades +da tarefa são +interrompidas, mas a pilha de retorno das tarefas continua intacta — a tarefa simplesmente perdeu o foco, +que foi para outra tarefa, como ilustrado na figura 2. Uma tarefa pode, então, retornar ao "primeiro plano" para que os usuários +continuem de onde pararam. Suponha, por exemplo, que a tarefa atual (tarefa A) tenha três +atividades na sua pilha — duas sob a atividade atual. O usuário pressiona o botão Página inicial e, +em seguida, +inicia um novo aplicativo no inicializador do aplicativo. Quando a tela inicial aparece, a tarefa A +vai para segundo plano. Quando o novo aplicativo inicia, o sistema inicia uma tarefa para este aplicativo +(tarefa B) com sua própria pilha de atividades. Após interagir +com este aplicativo, o usuário retorna à Página inicial novamente e seleciona o aplicativo que originalmente +iniciou a tarefa A. Agora, a tarefa A fica +em primeiro plano — todas as três atividades nas pilhas estão intactas e a atividade no topo +da pilha reinicia. Nesse +momento, o usuário pode alternar para a tarefa B acessando a Página inicial e selecionando o ícone do aplicativo +que iniciou essa tarefa (ou selecionando a tarefa do aplicativo +na tela de visão geral). +Esse é um exemplo de multitarefas no Android.

    + +

    Observação: várias tarefas podem ser mantidas em segundo plano simultaneamente. +Contudo, se o usuário estiver executando diversas tarefas em segundo plano ao mesmo tempo, o sistema pode começar +a destruir atividades de segundo plano para recuperar memória, fazendo com que o estado das atividades seja perdido. +Consulte a seção a seguir sobre Estado de atividades.

    + +

    Como as atividades na pilha de retorno nunca são reorganizadas, se o seu aplicativo permitir que +usuários iniciem uma determinada atividade a partir de mais de uma atividade, uma nova instância +dela será criada e colocada na pilha (em vez de trazer qualquer instância anterior +dela para o topo). Assim, uma atividade no aplicativo pode ser instanciada diversas +vezes (mesmo a partir de diferentes tarefas), como ilustrado na figura 3. Assim, se o usuário navegar inversamente +usando o botão Voltar, as instâncias da atividade serão revelada na ordem em que foram +abertas (cada uma +com o próprio estado da IU). No entanto, é possível modificar esse comportamento se você não deseja que uma atividade seja +instanciada mais de uma vez. Esse assunto é abordado na próxima seção sobre Gerenciamento de tarefas.

    + + +

    Para resumir o comportamento padrão de atividades e tarefas:

    + +
      +
    • Quando a atividade A inicia a atividade B, a atividade A é interrompida, mas o sistema retém seu estado +(como posição de rolagem e texto inserido em formulários). +Se o usuário pressionar o botão Voltar na atividade B, a atividade A reiniciará com seu estado +restaurado.
    • +
    • Quando o usuário se retira de uma tarefa pressionando o botão Página inicial, a atividade atual é +interrompida +e sua tarefa fica em segundo plano. O sistema retém o estado de cada atividade na tarefa. Se, +mais tarde, o usuário reiniciar a tarefa selecionando o ícone de inicialização que a inicia, ela ficará +em primeiro plano e reiniciará a atividade no topo da pilha.
    • +
    • Se o usuário pressionar o botão Voltar, a atividade atual será retirada da pilha +e +destruída. A atividade anterior na pilha é retomada. Quando uma atividade é destruída, o sistema +não retém seu estado.
    • +
    • As atividades podem ser instanciadas diversas vezes mesmo a partir de outras tarefas.
    • +
    + + +
    +

    Projeto de navegação

    +

    Para saber mais sobre o funcionamento da navegação de aplicativos no Android, leia o guia Navegação do Projeto do Android.

    +
    + + +

    Gravação do estado da atividade

    + +

    Como discutido acima, o comportamento padrão do sistema preserva o estado de uma atividade quando ela é +interrompida. Desse modo, quando usuários navegam inversamente a uma atividade anterior, a interface do usuário aparece +na forma em que foi deixada. Entretanto, é possível — e recomendável — reter proativamente +o estado das atividades usando métodos de retorno de chamada nos casos em que a atividade é destruída e deve +ser recriada.

    + +

    Quando o sistema interrompe uma das atividades (como quando uma nova atividade inicia ou a tarefa +se move para segundo plano), o sistema pode destruir esta atividade completamente se precisar recuperar +a memória do sistema. Quando isso ocorre, as informações sobre o estado da atividade são perdidas. Se isso acontecer, +o sistema ainda +saberá que a atividade tem um lugar na pilha de retorno, mas quando a atividade for levada +ao topo da pilha, o sistema precisará recriá-la (em vez de reiniciá-la). Para +evitar perder o trabalho do usuário, deve-se retê-la proativamente implementando +métodos de retorno de chamada {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +na atividade.

    + +

    Para obter mais informações sobre a gravação do estado da atividade, consulte o documento +Atividades.

    + + + +

    Gerenciamento de tarefas

    + +

    O modo com que o Android gerencia tarefas e a pilha de retorno, como descrito abaixo — posicionando todas +as atividades iniciadas em sucessão na mesma tarefa em uma pilha UEPS (último que entra, primeiro que sai) — funciona +muito bem para a maioria dos aplicativo e não é preciso preocupar-se com a associação das atividades +a tarefas ou como elas estão organizadas na pilha de retorno. No entanto, pode-se decidir interromper +o comportamento normal. Talvez você deseje que uma atividade inicie uma nova tarefa no aplicativo ao ser +iniciada (em vez de ser colocada dentro da tarefa atual); ou, quando você inicia uma atividade, deseja +apresentar uma instância dela existente (em vez de criar uma nova +instância no topo da pilha de retorno); ou talvez você deseje que a pilha apague +todas as atividades, exceto a atividade raiz, quando o usuário sai da tarefa.

    + +

    É possível fazer tudo isso e muito mais com atributos +no elemento {@code <activity>} do manifesto + e com sinalizadores na intenção passada +a {@link android.app.Activity#startActivity startActivity()}.

    + +

    Com relação a isso, os principais atributos +{@code <activity>} que podem ser usados são:

    + + + +

    E os principais sinalizadores de intenção que podem ser usados são:

    + +
      +
    • {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
    • +
    • {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
    • +
    • {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
    • +
    + +

    Nas seções a seguir, você verá como usar esses atributos de manifesto e sinalizadores +de intenção para definir como as atividades são associadas a tarefas e como elas se comportam na pilha de retorno.

    + +

    Além disso, são discutidas separadamente as considerações sobre como tarefas e atividades podem ser representadas +e gerenciadas na tela de visão geral. Consulte Tela de visão geral +para obter mais informações. Normalmente, é preciso permitir que o sistema defina como as tarefas +e as atividades são representadas na tela de visão geral e não é necessário modificar esse comportamento.

    + +

    Atenção: a maioria dos aplicativos não deve interromper o comportamento +padrão de atividades e tarefas. Se você determinar que é necessário modificar os comportamentos padrão +da atividade, seja prudente e certifique-se de testar a capacidade de uso da atividade durante +a inicialização e ao navegar de volta para ela de outras atividades e tarefas com o botão Voltar. +Certifique-se de testar os comportamentos de navegação que podem entrar em conflito com o comportamento esperado pelo usuário.

    + + +

    Definição de modos de inicialização

    + +

    Os modos de inicialização permitem definir a forma com que uma nova instância de uma atividade é associada +à tarefa atual. Pode-se definir diferentes modos de inicialização de duas maneiras:

    +
      +
    • Usando o arquivo de manifesto +

      Ao declarar uma atividade em um arquivo de manifesto, pode-se especificar como a atividade +deve associar-se a tarefas quando ela inicia.

    • +
    • Usando sinalizadores de intenção +

      Ao chamar {@link android.app.Activity#startActivity startActivity()}, +é possível incluir um sinalizador na {@link android.content.Intent} que declara como +(ou se) a nova atividade deve associar-se à tarefa atual.

    • +
    + +

    Assim, se a atividade A iniciar a atividade B, a atividade B poderá definir no manifesto como +deve associar-se à tarefa atual (se ocorrer) e a atividade A poderá solicitar o modo pelo qual a atividade +B deverá associar-se à tarefa atual. Se ambas as atividades definem como a atividade B +deve associar-se a uma tarefa, a solicitação da atividade A (como definido na intenção) sobrepõe +a solicitação da atividade B (como definido no seu manifesto).

    + +

    Observação: Alguns modos de inicialização disponíveis para o arquivo de manifesto +não estão disponíveis como sinalizadores para uma intenção e, do mesmo modo, alguns modos de inicialização disponíveis como sinalizadores +de uma intenção não podem ser definidos no manifesto.

    + + +

    Uso do arquivo de manifesto

    + +

    Ao declarar uma atividade no arquivo de manifesto, pode-se especificar como a atividade +deve associar-se a tarefas usando o atributo {@code +launchMode} do elemento +{@code <activity>}.

    + +

    O atributo {@code +launchMode} especifica uma instrução sobre como a atividade deve ser inicializada +em uma tarefa. Há quatro modos diferentes de inicialização que podem ser designados ao atributo +launchMode: +

    + +
    +
    {@code "standard"} (o modo padrão)
    +
    Padrão. O sistema cria uma nova instância da atividade na tarefa +pela qual foi iniciada e encaminha-lhe a intenção. A atividade pode ser instanciada diversas vezes; +cada instância pode pertencer a diferentes tarefas e uma tarefa pode ter diversas instâncias.
    +
    {@code "singleTop"}
    +
    Se uma instância da atividade já existir no topo da tarefa atual, o sistema +encaminhará a intenção àquela instância por meio de uma chamada do método {@link +android.app.Activity#onNewIntent onNewIntent()} em vez de criar uma nova instância +da atividade. A atividade pode ser instanciada diversas vezes; cada instância pode +pertencer a diferentes tarefas e uma tarefa pode ter diversas instâncias (mas somente se +a atividade no topo da pilha de retorno não for uma instância existente da atividade). +

    Por exemplo: suponhamos que uma pilha de retorno da tarefa consista na atividade raiz A com as atividades B, C e +D no topo (a pilha é A-B-C-D, com D no topo). Uma intenção chega de uma atividade de tipo D. +Se D for o modo de inicialização {@code "standard"} padrão, uma nova instância da classe será inicializada +e a tarefa se tornará A-B-C-D-D. Entretanto, se o modo de inicialização de D for {@code "singleTop"}, a instância existente +de D receberá a intenção por {@link +android.app.Activity#onNewIntent onNewIntent()} porque ela está no topo da pilha — +a pilha permanece A-B-C-D. Contudo, se uma intenção chegar de uma atividade de tipo B, uma nova +instância de B será adicionada à pilha, mesmo que o modo de inicialização seja {@code "singleTop"}.

    +

    Observação: quando uma nova instância de uma atividade é criada, +o usuário pode pressionar o botão Voltar para retornar à atividade anterior. Porém, quando uma +instância +existente de uma atividade trata de uma nova intenção, o usuário não pode pressionar o botão Voltar para retornar +ao estado +da atividade antes que a nova intenção chegue em {@link android.app.Activity#onNewIntent +onNewIntent()}.

    +
    + +
    {@code "singleTask"}
    +
    O sistema cria uma nova tarefa e instancia a atividade em sua raiz. +Entretanto, se uma instância da atividade já existir em uma tarefa separada, o sistema encaminhará +a intenção àquela instância por meio de uma chamada do método {@link +android.app.Activity#onNewIntent onNewIntent()} em vez de criar uma nova instância. Somente +uma instância da atividade pode existir por vez. +

    Observação: embora a atividade inicie em uma nova tarefa, o botão +Voltar ainda direciona o usuário à atividade anterior.

    +
    {@code "singleInstance"}.
    +
    Igual à {@code "singleTask"}, exceto que o sistema não inicializa nenhuma outra atividade +na tarefa que contém a instância. A atividade é sempre o único e exclusivo membro de sua tarefa; +toda atividade iniciada por ela abre em uma tarefa separada.
    +
    + + +

    Outro exemplo: o aplicativo Navegador do Android declara que a atividade do navegador da web deve +sempre abrir na própria tarefa — especificando o modo de inicialização {@code singleTask} no elemento {@code <activity>}. +Isso significa que, se o aplicativo emite uma +intenção para abrir o navegador do Android, sua atividade não é colocada na mesma +tarefa do aplicativo. Em vez disso, uma nova tarefa inicia para o navegador ou, se o navegador +já possui uma tarefa em execução em segundo plano, essa tarefa é colocada em primeiro plano para tratar a nova +intenção.

    + +

    Se uma atividade inicia em uma nova tarefa ou na mesma tarefa que a atividade que +a iniciou, o botão Voltar sempre direciona o usuário à atividade anterior. Porém, se você +iniciar uma atividade que especifica o modo de inicialização {@code singleTask} e se uma instância +dessa atividade existir em uma tarefa de segundo plano, toda a tarefa será colocada em primeiro plano. Nesse +momento, a pilha de retorno conterá todas as atividades da tarefa colocada em primeiro plano +no topo. A figura 4 ilustra essa situação.

    + + +

    Figura 4. Representação de como uma atividade +com modo de inicialização "singleTask" é adicionada à pilha de retorno. Se a atividade já for parte de uma tarefa +de segundo plano com a própria pilha de retorno, toda a pilha também virá +para primeiro plano, no topo da tarefa atual.

    + +

    Para obter mais informações sobre o uso de modos de inicialização no arquivo de manifesto, consulte a documentação do elemento +<activity> +, onde o atributo {@code launchMode} e os valores aceitos +são discutidos mais aprofundadamente.

    + +

    Observação: os comportamentos a especificar para a atividade com o atributo {@code launchMode} + podem ser neutralizados por sinalizadores incluídos na intenção que inicia a atividade, conforme abordado +na próxima seção.

    + + + +

    Uso de sinalizadores de intenção

    + +

    Ao iniciar uma atividade, é possível modificar a associação padrão de uma atividade à tarefa +incluindo sinalizadores na intenção fornecida a {@link +android.app.Activity#startActivity startActivity()}. Os sinalizadores que podem ser usados para modificar +o comportamento padrão são:

    + +

    +

    {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
    +
    Inicia a atividade em uma nova tarefa. Se uma tarefa já estiver em execução para a atividade que você está +iniciando agora, ela será colocada em primeiro plano com o último estado restaurado e a atividade +receberá a nova intenção em {@link android.app.Activity#onNewIntent onNewIntent()}. +

    Isso produz o mesmo comportamento que o valor {@code "singleTask"} do {@code launchMode}, +abordado na seção anterior.

    +
    {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
    +
    Se a atividade iniciada for a atual (no topo da pilha de retorno), +a instância existente receberá uma chamada de {@link android.app.Activity#onNewIntent onNewIntent()} +em vez de criar uma nova instância da atividade. +

    Isso produz o mesmo comportamento que o valor {@code "singleTop"} do {@code launchMode} +abordado na seção anterior.

    +
    {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
    +
    Se a atividade iniciada já estiver em execução na tarefa atual, em vez +de lançar uma nova instância daquela atividade, todas as outras atividades no topo dela serão +destruídas e essa intenção será entregue à instância reiniciada da atividade (agora no topo) +por {@link android.app.Activity#onNewIntent onNewIntent()}. +

    Não há nenhum valor para o atributo {@code launchMode} + que produza esse comportamento.

    +

    {@code FLAG_ACTIVITY_CLEAR_TOP} é mais usado em conjunto com + {@code FLAG_ACTIVITY_NEW_TASK}. +Quando usados juntos, estes sinalizadores são o modo de localizar uma atividade existente +em outra tarefa e colocá-la em uma posição em que possa responder à intenção.

    +

    Observação: se o modo de inicialização da atividade designada for +{@code "standard"}, +ela também será removida da pilha e uma nova instância será iniciada em seu lugar para tratar +a intenção recebida. Isso se deve ao fato de que uma nova instância é sempre criada para uma nova intenção quando o +modo de inicialização é {@code "standard"}.

    +
    + + + + + + +

    Tratamento de afinidades

    + +

    A afinidade indica a que tarefa uma atividade prefere pertencer. Por padrão, todas +as atividades do mesmo aplicativo têm afinidade entre si. Assim, por padrão, todas +as atividades no mesmo aplicativo preferem estar na mesma tarefa. Contudo, é possível modificar +a afinidade padrão de uma atividade. Atividades definidas +em aplicativos diferentes podem compartilhar uma afinidade, ou atividades definidas no mesmo aplicativo podem ter +diferentes afinidades de tarefa atribuídas.

    + +

    É possível modificar a afinidade de qualquer atividade com o atributo {@code taskAffinity} + do elemento +{@code <activity>}.

    + +

    O atributo {@code taskAffinity} + recebe um valor de string que deve ser exclusivo do nome do pacote padrão +declarado no elemento +{@code <manifest>} + porque o sistema usa esse nome para identificar a afinidade +de tarefa padrão do aplicativo.

    + +

    A afinidade tem relevância em duas circunstâncias:

    +
      +
    • Quando a intenção que inicializa uma atividade contém + o sinalizador + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}. + +

      Uma nova atividade é, por padrão, inicializada na tarefa da atividade +que chamou {@link android.app.Activity#startActivity startActivity()}. Ela é colocada na mesma +pilha de retorno do autor da chamada. Contudo, se a intenção passada a +{@link android.app.Activity#startActivity startActivity()} +contiver o sinalizador {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +, o sistema procurará uma tarefa diferente para comportar a nova atividade. Na maioria das vezes, é uma nova tarefa. +Porém, isso não é obrigatório. Se já houver uma tarefa com a mesma afinidade +da nova atividade, ela será inicializada naquela tarefa. Caso contrário, ela iniciará uma nova tarefa.

      + +

      Se esse sinalizador fizer com que uma atividade inicie uma nova tarefa e se o usuário pressionar o botão Página inicial +para sair dela, + será necessário ter um modo de o usuário navegar de volta à tarefa. Algumas entidades (como +o gerenciador de notificação) sempre iniciam atividades em tarefas externas, nunca como parte de si mesmas, por isso +elas sempre colocam {@code FLAG_ACTIVITY_NEW_TASK} nas intenções que passam +a {@link android.app.Activity#startActivity startActivity()}. +Se você tiver uma atividade que possa ser chamada +por uma entidade externa que possa usar este sinalizador, certifique-se de que o usuário tenha um modo independente +de voltar à tarefa iniciada, como com um ícone de inicialização (a atividade raiz da tarefa +tem um filtro de intenções {@link android.content.Intent#CATEGORY_LAUNCHER}; consulte a seção Início de uma tarefa abaixo).

      +
    • + +
    • Quando uma atividade tem o atributo +{@code allowTaskReparenting} definido como {@code "true"}. +

      Nesse caso, a atividade pode mover-se da tarefa que iniciou para a tarefa afim +quando for colocada em primeiro plano.

      +

      Por exemplo: suponhamos que uma atividade que relate condições do clima em cidades selecionadas seja +definida como parte de um aplicativo de viagens. Ela tem a mesma afinidade que outras atividades no mesmo +aplicativo (a afinidade padrão do aplicativo) e permite a redefinição da hierarquia com esse atributo. +Quando uma das atividades inicia a atividade de notificação de clima, ela inicialmente pertence à mesma +tarefa de sua atividade. Porém, quando a tarefa do aplicativo de viagens é colocada em primeiro plano, +a atividade de notificação de clima é reatribuída a essa tarefa e exibida dentro dela.

      +
    • +
    + +

    Dica: se um arquivo {@code .apk} contiver mais de um "aplicativo" +do ponto de vista do usuário, você provavelmente desejará usar o atributo {@code taskAffinity} + para designar diferentes afinidades às atividades associadas a cada "aplicativo".

    + + + +

    Apagar a pilha de retorno

    + +

    Se o usuário sair de uma tarefa por muito tempo, o sistema apagará a tarefa de todas as atividades exceto +a da atividade raiz. Quando o usuário retornar à tarefa, somente a atividade raiz será restaurada. +O sistema comporta-se dessa maneira porque, após longo tempo de uso, os usuários provavelmente abandonaram +o que estavam fazendo antes e são direcionados de volta à tarefa para começar algo novo.

    + +

    Há alguns atributos de atividade que podem ser usados para modificar esse comportamento:

    + +
    +
    alwaysRetainTaskState +
    +
    Se esse atributo for definido como {@code "true"} na atividade raiz de uma tarefa, +o comportamento padrão descrito não acontecerá. +A tarefa reterá todas as atividades em sua pilha mesmo após um longo período.
    + +
    clearTaskOnLaunch
    +
    Se esse atributo for definido como {@code "true"} na atividade raiz de uma tarefa, +a pilha será apagada da atividade raiz sempre que o usuário sair da tarefa +e retornar a ela. Em outras palavras, é o oposto de + +{@code alwaysRetainTaskState}. O usuário sempre retorna à tarefa +no estado inicial, mesmo ao retirar-se da tarefa somente por um momento.
    + +
    finishOnTaskLaunch +
    +
    Esse atributo é como {@code clearTaskOnLaunch}, +mas opera +em uma única atividade, e não em uma tarefa inteira. Ele também apaga todas as atividades, +inclusive a atividade raiz. Quando definido como {@code "true"}, +a atividade permanece parte da tarefa somente para a sessão atual. Se o usuário +retirar-se e, em seguida, retornar à tarefa, ela não estará mais presente.
    +
    + + + + +

    Início de uma tarefa

    + +

    É possível configurar uma atividade como ponto de entrada de uma tarefa fornecendo-lhe um filtro de intenções +com {@code "android.intent.action.MAIN"} como a ação especificada +e {@code "android.intent.category.LAUNCHER"} +como a categoria especificada. Por exemplo:

    + +
    +<activity ... >
    +    <intent-filter ... >
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +    ...
    +</activity>
    +
    + +

    Um filtro de intenções desse tipo faz com que um ícone e o rótulo +da atividade sejam exibidos no inicializador do aplicativo, fornecendo aos usuários um modo de inicializar a atividade +e de retornar à tarefa criada a qualquer tempo após sua inicialização. +

    + +

    Este segundo recurso é importante: é preciso que os usuários possam sair de uma tarefa e voltar a ela +mais tarde usando esse inicializador de atividades. Por isso, os dois modos +de inicialização que marcam atividades como sempre iniciando uma tarefa ({@code "singleTask"} e +{@code "singleInstance"}) devem ser usados somente quando a atividade tiver um filtro +{@link android.content.Intent#ACTION_MAIN} +e um {@link android.content.Intent#CATEGORY_LAUNCHER}. Imagine, por exemplo, o que +aconteceria se o filtro não estivesse presente: uma intenção inicializaria uma atividade {@code "singleTask"}, iniciando uma +nova tarefa, e o usuário perderia algum tempo trabalhando nessa tarefa. O usuário, então, pressiona o botão +Página inicial. A tarefa é enviada para segundo plano e não fica mais visível. O usuário não tem como voltar +à tarefa porque ela não é representada no inicializador do aplicativo.

    + +

    Para esses casos em que se deseja que o usuário não seja capaz de retornar a uma atividade, defina +{@code finishOnTaskLaunch} + do elemento +<activity> +como {@code "true"} (consulte Apagar a pilha).

    + +

    Veja mais informações sobre a representação e o gerenciamento de atividades +na tela de visão geral em +Tela de visão geral.

    + + diff --git a/docs/html-intl/intl/pt-br/guide/index.jd b/docs/html-intl/intl/pt-br/guide/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..ab396477e2506007b41254f3dd50403a56439c30 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Introdução ao Android + +@jd:body + + + + +

    O Android fornece uma estrutura de aplicativo rica que permite criar aplicativos e jogos inovadores +para dispositivos móveis em um ambiente de linguagem Java. Os documentos listados na navegação +à esquerda fornecem detalhes da criação de aplicativos usando as várias APIs do Android.

    + +

    Se você é novo no desenvolvimento para Android, é importante que entenda +os seguintes conceitos fundamentais sobre a estrutura de aplicativos do Android:

    + + +
    + +
    + +

    Aplicativos oferecem vários pontos de entrada

    + +

    Aplicativos para Android são criados como uma combinação de componentes distintos que podem ser invocados +individualmente. Por exemplo, uma atividade individual fornece uma única +tela para a interface de usuário e um serviço realiza trabalho +em segundo plano de forma independente.

    + +

    De um componente, é possível executar outro componente usando uma intenção. É possível até mesmo +iniciar um componente em um aplicativo diferente, como uma atividade em um aplicativo de mapas para mostrar um endereço. Esse modelo +fornece vários pontos de entrada para um único aplicativo e permite que qualquer aplicativo se comporte como o "padrão" de um usuário +para uma ação que outros aplicativos podem invocar.

    + + +

    Saiba mais:

    + + +
    + + +
    + +

    Os aplicativos se adaptam a diferentes dispositivos

    + +

    O Android fornece uma estrutura de aplicativo adaptativa que permite fornecer recursos exclusivos para +diferentes configurações de dispositivos. Por exemplo, é possível criar diferentes arquivos XML +de layout para diversos tamanhos de tela e o sistema +determina qual layout deverá aplicar com base no tamanho da tela do dispositivo atual.

    + +

    Você pode consultar a disponibilidade dos recursos do dispositivo em tempo de execução se qualquer recurso do +aplicativo exigir hardware específico, como uma câmera. Se necessário, também é possível declarar recursos que o aplicativo exige, +para que mercados como a Google Play Store não permitam a instalação em dispositivos que não sejam compatíveis +com aquele recurso.

    + + +

    Saiba mais:

    + + +
    + +
    + + + diff --git a/docs/html-intl/intl/pt-br/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/pt-br/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 0000000000000000000000000000000000000000..e33779684a5c0a5e577f325234540821ae73bee6 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=Manifesto do aplicativo +@jd:body + + + +

    + Todo aplicativo tem que ter um arquivo AndroidManifest.xml (precisamente +com esse nome) no seu diretório raiz. O arquivo de manifesto +apresenta informações essenciais sobre o aplicativo ao sistema Android, +necessárias para o sistema antes que ele possa executar o código +do aplicativo. Entre outras coisas, o manifesto serve para: +

    + +
      +
    • Nomear o pacote Java para o aplicativo. +O nome do pacote serve como identificador exclusivo para o aplicativo.
    • + +
    • Descrever os componentes do aplicativo — as atividades, +os serviços, os receptores de transmissão e os provedores de conteúdo que compõem +o aplicativo. Nomear as classes que implementam os componentes +e publicam seus recursos (por exemplo, que mensagens {@link android.content.Intent +Intent} eles podem tratar). Essas declarações permitem ao sistema Android +saber quais são os componentes e em que condições eles podem ser iniciados.
    • + +
    • Determinar que processos hospedarão componentes de aplicativo.
    • + +
    • Declarar as permissões que o aplicativo deve ter para acessar +partes protegidas da API e interagir com outros aplicativos.
    • + +
    • Declarar também as permissões que outros devem ter +para interagir com os componentes do aplicativo.
    • + +
    • Listar as classes {@link android.app.Instrumentation} que fornecem +geração de perfil e outras informações durante a execução do aplicativo. Essas declarações +estão presentes no manifesto somente enquanto o aplicativo está em desenvolvimento +e teste — elas são removidas antes da publicação do aplicativo.
    • + +
    • Declarar o nível mínimo da API do Android que o aplicativo +exige.
    • + +
    • Listar as bibliotecas às quais o aplicativo deve se vincular.
    • +
    + + +

    Estrutura do arquivo de manifesto

    + +

    +O diagrama ilustra a estrutura geral do arquivo de manifesto e cada +elemento que ele pode conter. Cada elemento e seus atributos +são documentados na totalidade em um arquivo separado. Para exibir informações detalhadas +sobre cada elemento, clique no nome do elemento no diagrama, +na lista de elementos em ordem alfabética que acompanha o diagrama +ou em qualquer outra menção ao nome do elemento. +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +
    +<manifest>
    +
    +    <uses-permission />
    +    <permission />
    +    <permission-tree />
    +    <permission-group />
    +    <instrumentation />
    +    <uses-sdk />
    +    <uses-configuration />  
    +    <uses-feature />  
    +    <supports-screens />  
    +    <compatible-screens />  
    +    <supports-gl-texture />  
    +
    +    <application>
    +
    +        <activity>
    +            <intent-filter>
    +                <action />
    +                <category />
    +                <data />
    +            </intent-filter>
    +            <meta-data />
    +        </activity>
    +
    +        <activity-alias>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data />
    +        </activity-alias>
    +
    +        <service>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data/>
    +        </service>
    +
    +        <receiver>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data />
    +        </receiver>
    +
    +        <provider>
    +            <grant-uri-permission />
    +            <meta-data />
    +            <path-permission />
    +        </provider>
    +
    +        <uses-library />
    +
    +    </application>
    +
    +</manifest>
    +
    + +

    +Todos os elementos que podem aparecer no arquivo de manifesto estão +relacionados abaixo em ordem alfabética. Estes são os únicos elementos legais. Não é possível +adicionar elementos ou atributos próprios. +

    + +

    +<action> +
    <activity> +
    <activity-alias> +
    <application> +
    <category> +
    <data> +
    <grant-uri-permission> +
    <instrumentation> +
    <intent-filter> +
    <manifest> +
    <meta-data> +
    <permission> +
    <permission-group> +
    <permission-tree> +
    <provider> +
    <receiver> +
    <service> +
    <supports-screens> +
    <uses-configuration> +
    <uses-feature> +
    <uses-library> +
    <uses-permission> +
    <uses-sdk> +

    + + + + +

    Convenções de arquivos

    + +

    +Algumas convenções e regras se aplicam geralmente a todos os elementos e atributos +no manifesto: +

    + +
    +
    Elementos
    +
    Somente os elementos +<manifest> +e <application> +são necessários — eles devem estar presentes e ocorrer somente uma vez. +A maioria dos outros pode ocorrer diversas vezes ou nunca — embora +pelo menos alguns deles devam estar presentes para que o manifesto +realize algo significativo. + +

    +Se um elemento contiver qualquer coisa, ele conterá outros elementos. +Todos os valores são definidos por meio de atributos, e não como dados de caracteres dentro de um elemento. +

    + +

    +Elementos de mesmo nível geralmente não são ordenados. Por exemplo: os elementos +<activity>, +<provider> +e <service> +podem ser combinados entre si em qualquer sequência. (O elemento +<activity-alias> +é uma exceção a essa regra: ele deve seguir o +<activity> +para o qual é alias.) +

    + +
    Atributos
    +
    Formalmente, todos os atributos são opcionais. No entanto, alguns deles +devem ser especificados para um elemento para cumprir a sua finalidade. Use +a documentação como guia. Para atributos verdadeiramente opcionais, ele menciona +um valor padrão ou declara o que acontece na ausência de uma especificação. + +

    Exceto por alguns atributos do elemento +<manifest> +raiz, todos os nomes de atributo têm um prefixo {@code android:} — +por exemplo, {@code android:alwaysRetainTaskState}. Como o prefixo é universal, +a documentação geralmente o omite ao referir-se a atributos +pelo nome.

    + +
    Declaração de nomes de classe
    +
    Muitos elementos correspondem a objetos Java, inclusive elementos do próprio +aplicativo (o elemento +<application> +) e seus componentes principais — atividades +(<activity>), +serviços +(<service>), +receptores de transmissão +(<receiver>) +e provedores de conteúdo +(<provider>). + +

    +Se uma subclasse for definida, como quase sempre acontece para classes de componentes +({@link android.app.Activity}, {@link android.app.Service}, +{@link android.content.BroadcastReceiver} e {@link android.content.ContentProvider}), +a subclasse será declarada por meio de um atributo {@code name}. O nome deve conter +toda a designação do pacote. +Por exemplo: uma subclasse {@link android.app.Service} pode ser declarada assim: +

    + +
    <manifest . . . >
    +    <application . . . >
    +        <service android:name="com.example.project.SecretService" . . . >
    +            . . .
    +        </service>
    +        . . .
    +    </application>
    +</manifest>
    + +

    +No entanto, para encurtar, se o primeiro caractere da string for um ponto, +a string será acrescentada ao nome do pacote do aplicativo (conforme especificado pelo atributo +package + do elemento +<manifest> +). A seguinte atribuição é igual à atribuição acima: +

    + +
    <manifest package="com.example.project" . . . >
    +    <application . . . >
    +        <service android:name=".SecretService" . . . >
    +            . . .
    +        </service>
    +        . . .
    +    </application>
    +</manifest>
    + +

    +Ao iniciar um componente, o Android cria uma instância da subclasse nomeada. +Se nenhuma subclasse for especificada, ele criará uma instância da classe base. +

    + +
    Vários valores
    +
    Se for especificado mais de um valor, o elemento sempre será repetido +em vez de listar os vários valores dentro de um único elemento. +Por exemplo, um filtro de intenção pode listar algumas ações: + +
    <intent-filter . . . >
    +    <action android:name="android.intent.action.EDIT" />
    +    <action android:name="android.intent.action.INSERT" />
    +    <action android:name="android.intent.action.DELETE" />
    +    . . .
    +</intent-filter>
    + +
    Valores de recurso
    +
    Alguns atributos têm valores que podem ser exibidos aos usuários — por exemplo, +uma etiqueta e um ícone de uma atividade. Os valores desses atributos +devem ser localizados e, por tanto, definidos a partir de um recurso ou tema. Os valores +de recurso são expressos no formato a seguir:

    + +

    {@code @[pacote:]tipo:nome}

    + +

    +em que o nome do pacote pode ser omitido se o recurso estiver no mesmo pacote +que o aplicativo tipo é um tipo de recurso — como uma "string" ou +"drawable" (desenhável) — e nome é o nome que identifica o recurso específico. +Por exemplo: +

    + +
    <activity android:icon="@drawable/smallPic" . . . >
    + +

    +Os valores de um tema são expressos de forma semelhante, mas, com um '{@code ?}' +em vez de '{@code @}': +

    + +

    {@code ?[pacote:]tipo:nome} +

    + +
    Valores de string
    +
    Quando o valor de um atributo é uma string, devem-se usar duas barras invertidas ('{@code \\}') +para caracteres de escape — por exemplo, '{@code \\n}' para +uma nova linha ou '{@code \\uxxxx}' para um caractere Unicode.
    +
    + + +

    Características do arquivo

    + +

    +As seções a seguir descrevem como alguns recursos do Android se refletem +no arquivo de manifesto. +

    + + +

    Filtros de intenções

    + +

    +Os componentes fundamentais de um aplicativo (suas atividades, serviços e receptores +de transmissão) são ativados por intenções. Intenções são +pacotes de informações (objetos {@link android.content.Intent}) que descrevem +uma ação desejada — inclusive os dados usados em ações, a categoria +do componente que deve executar a ação e outras instruções pertinentes. +O Android localiza um componente adequado para responder à intenção, inicia +uma nova instância do componente se necessário e passa-o +ao objeto da intenção. +

    + +

    +Os componentes anunciam seus recursos — os tipos de intenção aos quais eles podem +responder — por meio de filtros de intenções. Como o sistema Android +precisa saber que intenções um componente pode tratar antes de iniciá-lo, os filtros +de intenções são especificados no manifesto como elementos +<intent-filter> +. Os componentes podem ter qualquer quantidade de filtros, em que cada um descreve +um recurso diferente. +

    + +

    +A intenção que nomeia explicitamente um componente alvo ativará +aquele componente — o filtro não desempenha nenhuma função. Porém, uma intenção que não especifica nenhum alvo +pelo nome pode ativar um componente somente se ele puder passar por um dos filtros +do componente. +

    + +

    +Para ver como os objetos de intenção são testados em relação aos filtros de intenções, +consulte o documento +Intenções +e filtros de intenções em separado. +

    + + +

    Ícones e etiquetas

    + +

    +Alguns elementos têm atributos {@code icon} e {@code label} de um pequeno ícone +e uma etiqueta de texto que pode ficar visível para os usuários. Alguns deles também têm um atributo +{@code description} para um texto explicativo mais longo que também pode ser +exibido na tela. Por exemplo: o elemento +<permission> +tem todos os três atributos; assim, quando o usuário é consultado para dar +permissão a um aplicativo que a solicitou, serão apresentados ao usuário um ícone +que representa a permissão, o nome da permissão e uma descrição +de tudo o que está envolvido. +

    + +

    +Em todo caso, o ícone e a etiqueta definidos em um elemento recipiente se tornam as configurações +{@code icon} e {@code label} padrão de todos os subelementos do contêiner. +Assim, o ícone e a etiqueta definidos no elemento +<application> +são o ícone e a etiqueta padrão para cada um dos componentes do aplicativo. +Da mesma forma, o ícone e a etiqueta definidos para um componente — por exemplo, um elemento +<activity> + — são as configurações padrão para cada um dos elementos +<intent-filter> +do componente. Se um elemento +<application> +define uma etiqueta, mas uma atividade e seu filtro de intenção não definem, +a etiqueta do aplicativo é tratada como a etiqueta de atividade +e do filtro de intenção. +

    + +

    +O ícone e a etiqueta definidos para um filtro de intenção são usados para representar um componente +apresentado para o usuário para preencher a função +anunciada pelo filtro. Por exemplo: um filtro com as configurações +"{@code android.intent.action.MAIN}" e +"{@code android.intent.category.LAUNCHER}" anuncia uma atividade +como uma que inicia um aplicativo — ou seja, +que deve ser exibida no inicializador do aplicativo. O ícone e a etiqueta +definidos no filtro são, portanto, as exibidas no inicializador. +

    + + +

    Permissões

    + +

    +As permissões são restrições que limitam o acesso a parte do código +ou aos dados de um dispositivo. A limitação é imposta para proteger dados +essenciais que podem sofrer mau uso e distorções ou prejudicar a experiência do usuário. +

    + +

    +Cada permissão é identificada por uma etiqueta exclusiva. Geralmente a etiqueta indica +a ação que foi restringida. A seguir há alguns exemplos de permissões definidas +pelo Android: +

    + +

    {@code android.permission.CALL_EMERGENCY_NUMBERS} +
    {@code android.permission.READ_OWNER_DATA} +
    {@code android.permission.SET_WALLPAPER} +
    {@code android.permission.DEVICE_POWER}

    + +

    +Um recurso pode ser protegido por, no máximo, uma permissão. +

    + +

    +Se um aplicativo precisar de acesso a um recurso protegido por uma permissão, +ele deve declarar que precisa da permissão com um elemento +<uses-permission> +no manifesto. Assim, quando o aplicativo é instalado +no dispositivo, o instalador determina se concederá ou não a permissão +solicitada, marcando as autoridades que assinaram os certificados +do aplicativo e, em alguns casos, perguntando ao usuário. +Se a permissão for concedida, o aplicativo será capaz de usar os recursos +protegidos. Caso contrário, sua tentativa de acessar esses recursos simplesmente falhará +sem nenhuma notificação ao usuário. +

    + +

    +Um aplicativo também pode proteger seus componentes (atividades, serviços, +receptores de transmissão e provedores de conteúdo) com permissões. Ele pode empregar +qualquer uma das permissões definidas pelo Android (listadas em +{@link android.Manifest.permission android.Manifest.permission}) ou declaradas +por outros aplicativos. Ou então, ele pode definir as suas próprias. As novas permissões são declaradas +com o elemento +<permission>. + Por exemplo: uma atividade pode ser protegida da seguinte forma: +

    + +
    +<manifest . . . >
    +    <permission android:name="com.example.project.DEBIT_ACCT" . . . />
    +    <uses-permission android:name="com.example.project.DEBIT_ACCT" />
    +    . . .
    +    <application . . .>
    +        <activity android:name="com.example.project.FreneticActivity"
    +                  android:permission="com.example.project.DEBIT_ACCT"
    +                  . . . >
    +            . . .
    +        </activity>
    +    </application>
    +</manifest>
    +
    + +

    +Observe que, nesse exemplo, a permissão {@code DEBIT_ACCT}, além de declarada +com o elemento +<permission> +, tem seu uso solicitado com o elemento +<uses-permission>. + Ela deve ser solicitada para que outros componentes do aplicativo +iniciem a atividade protegida, mesmo que a proteção +seja imposta pelo próprio aplicativo. +

    + +

    +Se, no mesmo exemplo, o atributo {@code permission} fosse definido +como uma permissão declarada em outro lugar +(como {@code android.permission.CALL_EMERGENCY_NUMBERS}), não seria +necessário declará-la novamente com um elemento +<permission>. + No entanto, ainda seria necessário solicitar seu uso com +<uses-permission>. +

    + +

    +O elemento +<permission-tree> +declara um espaço de nome de um grupo de permissões que será definido +no código. E +<permission-group> +define um etiqueta de um conjunto de permissões (os dois declarados no manifesto com elementos +<permission> +e as declaradas em outro lugar). Ele afeta somente a forma com que as permissões estão +agrupadas quando apresentadas ao usuário. O elemento +<permission-group> +não especifica que permissões pertencem ao grupo; +ele só dá um nome ao grupo. Para incluir uma permissão no grupo, +designa-se o nome do grupo ao atributo +permissionGroup + do elemento +<permission>. + +

    + + +

    Bibliotecas

    + +

    +Todo aplicativo está vinculado à biblioteca Android padrão, que +contém os pacotes básicos para programar aplicativos (com classes comuns +tais como Activity, Service, Intent, View, Button, Application, ContentProvider +etc.). +

    + +

    +No entanto, alguns pacotes residem em suas próprias bibliotecas. Se o aplicativo +usar código de algum desses pacotes, ele deve receber solicitação explícita para ser +vinculado a eles. O manifesto deve conter um elemento +<uses-library> + separado para nomear cada uma das bibliotecas (o nome da biblioteca se encontra +na documentação do pacote). +

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/calendar-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..ce72b7db5cff09ac92303b605426f7efd2e7f692 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=Provedor de agenda +@jd:body + +
    +
    +

    Neste documento

    +
      +
    1. Conceitos básicos
    2. +
    3. Permissões do usuário
    4. +
    5. Tabela de agenda +
        +
      1. Consulta em uma agenda
      2. +
      3. Modificação de uma agenda
      4. +
      5. Inserção de uma agenda
      6. +
      +
    6. +
    7. Tabelas de eventos +
        +
      1. Adição de eventos
      2. +
      3. Atualização de eventos
      4. +
      5. Exclusão de eventos
      6. +
      +
    8. +
    9. Tabela de participantes +
        +
      1. Adição de participantes
      2. +
      +
    10. +
    11. Tabela de lembretes +
        +
      1. Adição de lembretes
      2. +
      +
    12. +
    13. Tabela de instâncias +
        +
      1. Consulta na tabela de instâncias
      2. +
    14. +
    15. Intenções do Agenda +
        +
      1. Uso de uma intenção para inserir um evento
      2. +
      3. Uso de uma intenção para editar um evento
      4. +
      5. Uso de intenções para exibir dados de agenda
      6. +
      +
    16. + +
    17. Adaptadores de sincronização
    18. +
    + +

    Classes principais

    +
      +
    1. {@link android.provider.CalendarContract.Calendars}
    2. +
    3. {@link android.provider.CalendarContract.Events}
    4. +
    5. {@link android.provider.CalendarContract.Attendees}
    6. +
    7. {@link android.provider.CalendarContract.Reminders}
    8. +
    +
    +
    + +

    O Provedor de agenda é um repositório para eventos da agenda do usuário. A +API do Provedor de Agenda permite consultar, inserir, atualizar e excluir +operações em agendas, eventos, participantes, lembretes etc.

    + + +

    A API do Provedor de Agenda pode ser usada por aplicativos e adaptadores de sincronização. As regras +variam conforme o tipo de programa que realiza as chamadas. Este documento +se concentra principalmente no uso da API do Provedor de Agenda como um aplicativo. Veja +uma discussão sobre as diferenças entre adaptadores de sincronização em +Adaptadores de sincronização.

    + + +

    Normalmente, para ler ou programar dados da agenda, o manifesto de um aplicativo deve +conter as permissões adequadas descritas em Permissões +do usuário. Para facilitar a realização de operações comuns, o Provedor +de Agenda fornece um conjunto de intenções conforme descrito em Intenções +do Agenda. Essas intenções levam os usuários ao aplicativo Agenda para inserir, exibir +e editar eventos. O usuário interage com o aplicativo Agenda e, em seguida, +retorna ao aplicativo original. Assim, o aplicativo não precisa solicitar permissões +nem fornecer uma interface gráfica para exibir ou criar eventos.

    + +

    Conceitos básicos

    + +

    Os provedores de conteúdo armazenam dados e disponibilizam-nos +para aplicativos. Os provedores de conteúdo oferecidos pela plataforma do Android (inclusive o Provedor de Agenda) normalmente expõem dados como um conjunto de tabelas baseadas em +um modelo de banco de dados relacional, em que cada linha é um registro e cada coluna são dados +de um tipo e significado específico. Por meio da API do Provedor de Agenda, aplicativos +e adaptadores de sincronização podem obter acesso de leitura/gravação às tabelas do banco de dados que armazenam +dados da agenda do usuário.

    + +

    Cada provedor de conteúdo expõe uma URI pública (agrupada como +um objeto {@link android.net.Uri} +) que identifica exclusivamente seu conjunto de dados. Um provedor de conteúdo que controla +diversos conjuntos de dados (diversas tabelas) expõe uma URI separada para cada um. Todas as +URIs de provedores começam com a string "content://". Ela +identifica os dados como controlados por um provedor de conteúdo. O Provedor +de Agenda define constantes para as URIs de cada uma das classes (tabelas). Essas +URIs têm o formato <class>.CONTENT_URI. Por exemplo: + {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}.

    + +

    A figura 1 exibe uma representação gráfica do modelo de dados do Provedor de Agenda. Ela ilustra +as tabelas e os campos principais que as vinculam entre si.

    + +Calendar Provider Data Model +

    Figura 1. Modelo de dados do Provedor de Agenda.

    + +

    Cada usuário pode ter diversas agendas e diferentes agendas podem ser associadas a diferentes tipos de conta (Google Agenda, Exchange etc.).

    + +

    O {@link android.provider.CalendarContract} define o modelo de dados de informações relacionadas a eventos e agendas. Esses dados são armazenados em diversas tabelas, que são listadas a seguir.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Tabela (classe)Descrição

    {@link android.provider.CalendarContract.Calendars}

    Essa tabela contém +as informações específicas da agenda. Cada linha nessa tabela contém os detalhes +de uma única agenda, como nome, cor, informações de sincronização etc.
    {@link android.provider.CalendarContract.Events}Essa tabela contém +as informações específicas do evento. Cada linha nessa tabela tem as informações de um único +evento — por exemplo: título do evento, local, horário de início, horário +de término etc. O evento pode ocorrer uma vez ou diversas vezes. Os participantes, +lembretes e propriedades estendidas são armazenados em tabelas separadas. +Cada um deles tem um {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +que referencia o {@link android.provider.BaseColumns#_ID} na tabela de eventos.
    {@link android.provider.CalendarContract.Instances}Essa tabela contém os +horários de início e término para cada ocorrência em um evento. Cada linha nessa tabela +representa uma única ocorrência do evento. Para eventos de ocorrência única, há um mapeamento 1:1 +de instâncias para eventos. Para eventos recorrentes, diversas linhas correspondentes +a diversas ocorrências daquele evento são geradas automaticamente.
    {@link android.provider.CalendarContract.Attendees}Essa tabela contém +as informações dos participantes (convidados) do evento. Cada linha representa um único convidado +de um evento. Ela especifica o tipo de convidado e a resposta quanto à participação do convidado +no evento.
    {@link android.provider.CalendarContract.Reminders}Essa tabela contém os +dados de alerta/notificação. Cada linha representa um único alerta de um evento. Um evento +pode ter vários lembretes. O número máximo de lembretes por evento +é especificado em +{@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS}, +que é definido pelo adaptador de sincronização +que possui a agenda fornecida. Os lembretes são especificados em minutos antes do evento +e têm um método que determina a forma de alertar o usuário.
    + +

    A API do Provedor de Agenda é projetada para ser flexível e poderosa. Ao mesmo tempo, +é importante fornecer uma boa experiência ao usuário final +e proteger a integridade da agenda e de seus dados. Para isso, a seguir apresentam-se +alguns pontos a considerar ao usar a API:

    + +
      + +
    • Inserção, atualização e exibição de eventos da agenda. Para inserir, modificar e ler eventos diretamente do provedor de agenda, é necessário ter as permissões apropriadas. Contudo, se o aplicativo em criação não for um aplicativo de agenda totalmente desenvolvido nem um adaptador de sincronização, não será necessário solicitar essas permissões. Em vez disso, podem-se usar intenções compatíveis com o aplicativo Agenda do Android para entregar operações de leitura e gravação a esse aplicativo. Ao usar as intenções, o aplicativo envia usuários ao aplicativo Agenda para realizar a operação desejada +em um formulário pré-preenchido. Após finalizarem a operação, eles serão direcionados de volta ao aplicativo. +Ao projetar seu aplicativo para realizar operações comuns através do Agenda, +os usuários têm uma experiência em uma interface robusta e consistente. Essa é +a abordagem recomendada. Para obter mais informações, consulte Intenções +do Agenda.

      + + +
    • Adaptadores de sincronização. Os adaptadores de sincronização sincronizam os dados da agenda +em um dispositivo do usuário com outro servidor ou fonte de dados. Nas tabelas +{@link android.provider.CalendarContract.Calendars} +e {@link android.provider.CalendarContract.Events}, +há colunas reservadas para o uso dos adaptadores de sincronização. +O provedor e os aplicativos não devem modificá-las. De fato, elas não são +visíveis a menos que sejam acessadas como um adaptador de sincronização. Para obter mais informações sobre +adaptadores de sincronização, consulte Adaptadores de sincronização.
    • + +
    + + +

    Permissões do usuário

    + +

    Para ler dados da agenda, o aplicativo deve conter a permissão {@link +android.Manifest.permission#READ_CALENDAR} no arquivo de manifesto. Ele +deve conter a permissão {@link android.Manifest.permission#WRITE_CALENDAR} +para excluir, inserir ou atualizar dados da agenda:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    +    <uses-sdk android:minSdkVersion="14" />
    +    <uses-permission android:name="android.permission.READ_CALENDAR" />
    +    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    +    ...
    +</manifest>
    +
    + + +

    Tabela de agendas

    + +

    A tabela {@link android.provider.CalendarContract.Calendars} contém detalhes +de agendas individuais. As colunas +Agendas a seguir são graváveis tanto por aplicativos quanto por adaptadores de sincronização. +Para obter uma lista completa de campos compatíveis, consulte +a referência {@link android.provider.CalendarContract.Calendars}

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    ConstanteDescrição
    {@link android.provider.CalendarContract.Calendars#NAME}O nome da agenda.
    {@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}O nome desta agenda que é exibido ao usuário.
    {@link android.provider.CalendarContract.Calendars#VISIBLE}Um booleano indicando se a agenda foi selecionada para ser exibida. Um valor +de 0 indica que eventos associados a essa agenda não devem ser +exibidos. Um valor de 1 indica que eventos associados a essa agenda devem +ser exibidos. Esse valor afeta a geração de linhas na tabela {@link +android.provider.CalendarContract.Instances}.
    {@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}Um booleano que indica se a agenda deve ser sincronizada e ter +os eventos armazenados no dispositivo. Um valor de 0 indica a não sincronização dessa agenda +e o não armazenamento dos eventos no dispositivo. Um valor de 1 indica a sincronização dos eventos dessa agenda +e o armazenamento dos eventos no dispositivo.
    + +

    Consulta em uma agenda

    + +

    A seguir há um exemplo que mostra como obter as agendas de propriedade de determinado +usuário. Para simplificar o exemplo, a operação de consulta é exibida no +encadeamento da interface do usuário ("encadeamento principal"). Na prática, isso deve ser feito em um encadeamento +assíncrono em vez de no encadeamento principal. Para ver mais discussões, consulte +Carregadores. Se você não estiver somente +lendo dados, mas modificando-os, consulte {@link android.content.AsyncQueryHandler}. +

    + + +
    +// Projection array. Creating indices for this array instead of doing
    +// dynamic lookups improves performance.
    +public static final String[] EVENT_PROJECTION = new String[] {
    +    Calendars._ID,                           // 0
    +    Calendars.ACCOUNT_NAME,                  // 1
    +    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    +    Calendars.OWNER_ACCOUNT                  // 3
    +};
    +  
    +// The indices for the projection array above.
    +private static final int PROJECTION_ID_INDEX = 0;
    +private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
    +private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
    +private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
    + + + + + +

    Na próxima parte do exemplo, você construirá a consulta. A seleção +especifica os critérios da consulta. Neste exemplo, a consulta busca +agendas que tenham o ACCOUNT_NAME +"sampleuser@google.com", o ACCOUNT_TYPE +"com.google" e o OWNER_ACCOUNT +"sampleuser@google.com". Para ver todas as agendas que um usuário +tenha exibido, não somente as que ele possui, omita o OWNER_ACCOUNT. +A consulta retorna um objeto {@link android.database.Cursor} +que pode ser usado para cruzar o conjunto de resultados retornado pela consulta +do banco de dados. Para ver mais informações sobre o uso de consultas em provedores de conteúdo, +consulte Provedores de conteúdo.

    + + +
    // Run query
    +Cursor cur = null;
    +ContentResolver cr = getContentResolver();
    +Uri uri = Calendars.CONTENT_URI;   
    +String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" 
    +                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
    +                        + Calendars.OWNER_ACCOUNT + " = ?))";
    +String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
    +        "sampleuser@gmail.com"}; 
    +// Submit the query and get a Cursor object back. 
    +cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
    + +

    Essa próxima seção usa o cursor para avançar pelo conjunto de resultados. Ele usa +as constantes definidas no início do exemplo para retornar os valores +de cada campo.

    + +
    // Use the cursor to step through the returned records
    +while (cur.moveToNext()) {
    +    long calID = 0;
    +    String displayName = null;
    +    String accountName = null;
    +    String ownerName = null;
    +      
    +    // Get the field values
    +    calID = cur.getLong(PROJECTION_ID_INDEX);
    +    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    +    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    +    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
    +              
    +    // Do something with the values...
    +
    +   ...
    +}
    +
    + +

    Modificação de uma agenda

    + +

    Para realizar uma atualização de uma agenda, é possível fornecer o {@link +android.provider.BaseColumns#_ID} da agenda como um ID anexado à +URI + +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) +ou como o primeiro item de seleção. A seleção +deve iniciar com "_id=?" e o primeiro +selectionArg deve ser o {@link +android.provider.BaseColumns#_ID} da agenda. +Também é possível realizar atualizações com codificações do ID na URI. Este exemplo altera +o nome de exibição de uma agenda usando a +abordagem +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}):

    + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long calID = 2;
    +ContentValues values = new ContentValues();
    +// The new display name for the calendar
    +values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
    +Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
    +int rows = getContentResolver().update(updateUri, values, null, null);
    +Log.i(DEBUG_TAG, "Rows updated: " + rows);
    + +

    Inserção de uma agenda

    + +

    Agendas são projetadas para serem gerenciadas principalmente por um adaptador de sincronização, por isso +deve-se inserir somente novas agendas como um adaptador de sincronização. Para a maior parte, +aplicativos só podem efetuar mudanças superficiais em agendas, como mudar o nome de exibição. Se +um aplicativo precisa criar uma agenda local, pode fazê-lo realizando +a inserção da agenda como um adaptador de sincronização usando um {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} de {@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL}. +{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} +é um tipo de conta especial para agendas +não associado a nenhuma conta do dispositivo. Agendas desse tipo não são sincronizadas com um servidor. Para +ver discussões sobre adaptadores de sincronização, consulte Adaptadores de sincronização.

    + +

    Tabela de eventos

    + +

    A tabela {@link android.provider.CalendarContract.Events} contém detalhes +de eventos individuais. Para adicionar, atualizar ou excluir eventos, um aplicativo deve +conter a permissão {@link android.Manifest.permission#WRITE_CALENDAR} +no arquivo de manifesto.

    + +

    As colunas de Eventos a seguir são graváveis tanto por um aplicativo quanto por um adaptador +de sincronização. Para obter uma lista completa de campos compatíveis, consulte a referência de {@link +android.provider.CalendarContract.Events}.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ConstanteDescrição
    {@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}O {@link android.provider.BaseColumns#_ID} da agenda à qual o evento pertence.
    {@link android.provider.CalendarContract.EventsColumns#ORGANIZER}E-mail do organizador (dono) do evento.
    {@link android.provider.CalendarContract.EventsColumns#TITLE}O título do evento.
    {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}Onde o evento acontece.
    {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}A descrição do evento.
    {@link android.provider.CalendarContract.EventsColumns#DTSTART}O horário de início do evento em milissegundos em UTC desde a época.
    {@link android.provider.CalendarContract.EventsColumns#DTEND}O horário de término do evento em milissegundos em UTC desde a época.
    {@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}O fuso horário do evento.
    {@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}O fuso horário do horário de término do evento.
    {@link android.provider.CalendarContract.EventsColumns#DURATION}A duração do evento em formato RCF5545. +Por exemplo, um valor de "PT1H" indica que o evento +deve durar uma hora, e um valor de "P2W" indica +uma duração de 2 semanas.
    {@link android.provider.CalendarContract.EventsColumns#ALL_DAY}Um valor de 1 indica que esse evento ocupa o dia inteiro, como definido +pelo fuso horário local. Um valor de 0 indica que é um evento comum que pode iniciar +e terminar a qualquer momento durante um dia.
    {@link android.provider.CalendarContract.EventsColumns#RRULE}A regra de recorrência do formato do evento. Por +exemplo, "FREQ=WEEKLY;COUNT=10;WKST=SU". Veja +mais exemplos aqui.
    {@link android.provider.CalendarContract.EventsColumns#RDATE}As datas de recorrência do evento. + Normalmente, usa-se {@link android.provider.CalendarContract.EventsColumns#RDATE} + em conjunto com {@link android.provider.CalendarContract.EventsColumns#RRULE} + para definir um conjunto agregado +de ocorrências repetidas. Para ver mais discussões, consulte Especificação RFC5545.
    {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}Se esse evento considera tempo ocupado ou se há tempo livre que pode ser +reagendado.
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}Se convidados podem modificar o evento.
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}Se convidados podem convidar outros.
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}Se convidados podem ver a lista de participantes.
    + +

    Adição de eventos

    + +

    Quando o aplicativo insere um novo evento, recomenda-se usar +uma intenção {@link android.content.Intent#ACTION_INSERT INSERT}, como descrito em Uso de uma intenção para inserir um evento. Contudo, se for +necessário, é possível inserir eventos diretamente. Esta seção descreve como +fazê-lo.

    + + +

    Abaixo apresentam-se as regras para inserção de um novo evento:

    +
      + +
    • É necessário incluir {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} e {@link +android.provider.CalendarContract.EventsColumns#DTSTART}.
    • + +
    • É necessário incluir um {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}. Para obter uma lista +dos IDs de fuso horário instalados do sistema, use {@link +java.util.TimeZone#getAvailableIDs()}. Observe que essa regra não se aplica +a inserções de evento pela intenção {@link +android.content.Intent#ACTION_INSERT INSERT} descrita em Uso de uma intenção para inserir um evento — nesta +situação, é fornecido um fuso horário padrão.
    • + +
    • Para eventos não recorrentes, é preciso incluir {@link +android.provider.CalendarContract.EventsColumns#DTEND}.
    • + + +
    • Para eventos recorrentes, é necessário incluir uma {@link +android.provider.CalendarContract.EventsColumns#DURATION} além de uma {@link +android.provider.CalendarContract.EventsColumns#RRULE} ou {@link +android.provider.CalendarContract.EventsColumns#RDATE}. Observe que essa regra não se aplica +a inserções de evento pela intenção {@link +android.content.Intent#ACTION_INSERT INSERT} descrita em Uso de uma intenção para inserir um evento — nessa situação, +é possível usar uma {@link +android.provider.CalendarContract.EventsColumns#RRULE} em conjunto com {@link android.provider.CalendarContract.EventsColumns#DTSTART} e {@link android.provider.CalendarContract.EventsColumns#DTEND}. Desta forma, o aplicativo Agenda +a converte em uma duração automaticamente.
    • + +
    + +

    A seguir há um exemplo de inserção de um evento: para simplificar, isso está sendo realizado +no encadeamento da IU. Na prática, inserções e atualizações devem ser feitas +em um encadeamento assíncrono para mover a ação para um encadeamento de segundo plano. Para obter +mais informações, consulte {@link android.content.AsyncQueryHandler}.

    + + +
    +long calID = 3;
    +long startMillis = 0; 
    +long endMillis = 0;     
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2012, 9, 14, 7, 30);
    +startMillis = beginTime.getTimeInMillis();
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2012, 9, 14, 8, 45);
    +endMillis = endTime.getTimeInMillis();
    +...
    +
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Events.DTSTART, startMillis);
    +values.put(Events.DTEND, endMillis);
    +values.put(Events.TITLE, "Jazzercise");
    +values.put(Events.DESCRIPTION, "Group workout");
    +values.put(Events.CALENDAR_ID, calID);
    +values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
    +Uri uri = cr.insert(Events.CONTENT_URI, values);
    +
    +// get the event ID that is the last element in the Uri
    +long eventID = Long.parseLong(uri.getLastPathSegment());
    +// 
    +// ... do something with event ID
    +//
    +//
    + +

    Observação: veja como este exemplo captura o ID +do evento depois que o evento é criado. Este é o modo mais fácil de obter um ID de evento. Muitas vezes o ID do evento +é necessário para realizar outras operações de agenda — por exemplo, adicionar +participantes ou lembretes a um evento.

    + + +

    Atualização de eventos

    + +

    Quando o aplicativo deseja permitir que o usuário edite um evento, é recomendável +usar uma intenção {@link android.content.Intent#ACTION_EDIT EDIT}, como +descrito em Uso de uma intenção para editar um evento. +Contudo, se for necessário, é possível editar eventos diretamente. Para realizar uma atualização +de um evento, é possível fornecer o _ID +do evento como um ID anexado à URI ({@link +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) +ou como o primeiro item de seleção. +A seleção deve iniciar com "_id=?" e o primeiro +selectionArg deve ser o _ID do evento. Você também +pode realizar atualizações usando uma seleção sem ID. A seguir há um exemplo de como atualizar +um evento. É possível mudar o título do evento usando +a abordagem +{@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}:

    + + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long eventID = 188;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +Uri updateUri = null;
    +// The new title for the event
    +values.put(Events.TITLE, "Kickboxing"); 
    +updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +int rows = getContentResolver().update(updateUri, values, null, null);
    +Log.i(DEBUG_TAG, "Rows updated: " + rows);  
    + +

    Exclusão de eventos

    + +

    Pode-se excluir um evento tanto pelo {@link +android.provider.BaseColumns#_ID} como um ID anexado na URI quanto usando-se +a seleção padrão. Ao usar um ID anexado, não é possível fazer seleções. +Há duas versões de exclusão: como aplicativo e como adaptador de sincronização. +A exclusão por um aplicativo define as colunas excluídas como 1. Esse sinalizador é que diz +ao adaptador de sincronização que a linha foi excluída e que essa exclusão deve ser +propagada para o servidor. A exclusão por um adaptador de sincronização remove o evento +do banco de dados junto com todos os dados associados. A seguir há um exemplo de um aplicativo +excluindo um evento pelo seu {@link android.provider.BaseColumns#_ID}:

    + + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long eventID = 201;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +Uri deleteUri = null;
    +deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +int rows = getContentResolver().delete(deleteUri, null, null);
    +Log.i(DEBUG_TAG, "Rows deleted: " + rows);  
    +
    + +

    Tabela de participantes

    + +

    Cada linha da tabela {@link android.provider.CalendarContract.Attendees} +representa um único participante ou convidado de um evento. Chamar +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} +retorna uma lista de participantes para +o evento com o {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} dado. +Esse {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +deve corresponder ao {@link +android.provider.BaseColumns#_ID} de determinado evento.

    + +

    A tabela a seguir lista +os campos graváveis. Ao inserir um novo participante, é necessário incluir todos eles +exceto ATTENDEE_NAME. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ConstanteDescrição
    {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}O ID do evento.
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}O nome do participante.
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}O endereço de e-mail do participante.
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}

    A relação do participante com o evento. Uma das seguintes:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}
    • +
    +
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}

    O tipo de participante. Uma das seguintes:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}
    • +
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}

    O status de participação do participante. Uma das seguintes:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}
    • +
    + +

    Adição de participantes

    + +

    A seguir há um exemplo que adiciona um único participante a um evento. Observe que +o {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +é obrigatório:

    + +
    +long eventID = 202;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Attendees.ATTENDEE_NAME, "Trevor");
    +values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
    +values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
    +values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
    +values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
    +values.put(Attendees.EVENT_ID, eventID);
    +Uri uri = cr.insert(Attendees.CONTENT_URI, values);
    +
    + +

    Tabela de lembretes

    + +

    Cada linha da tabela {@link android.provider.CalendarContract.Reminders} +representa um único lembrete de um evento. Chamar +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} retorna uma lista de lembretes para +o evento com o dado +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}.

    + + +

    A tabela a seguir relaciona os campos graváveis de lembretes. Todos eles devem +ser incluídos ao inserir um novo lembrete. Observe que adaptadores de sincronização especificam +os tipos de lembretes compatíveis na tabela {@link +android.provider.CalendarContract.Calendars}. Consulte +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS} +para obter detalhes.

    + + + + + + + + + + + + + + + + + + + +
    ConstanteDescrição
    {@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}O ID do evento.
    {@link android.provider.CalendarContract.RemindersColumns#MINUTES}Os minutos antes do evento em que o lembrete deve disparar.
    {@link android.provider.CalendarContract.RemindersColumns#METHOD}

    O método de alarme, como definido no servidor. Uma das seguintes:

    +
      +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}
    • +
    + +

    Adição de lembretes

    + +

    Este exemplo adiciona um lembrete para um evento. O lembrete dispara +15 minutos antes do evento.

    +
    +long eventID = 221;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Reminders.MINUTES, 15);
    +values.put(Reminders.EVENT_ID, eventID);
    +values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
    +Uri uri = cr.insert(Reminders.CONTENT_URI, values);
    + +

    Tabela de instâncias

    + +

    A tabela +{@link android.provider.CalendarContract.Instances} contém +os horários de início e término das ocorrência de um evento. Cada linha nessa tabela +representa uma única ocorrência do evento. A tabela de instâncias não é gravável e fornece +somente um modo de consultar ocorrências de eventos.

    + +

    A tabela a seguir relaciona alguns dos campos passíveis de consulta de uma instância. Observe +que o fuso horário é definido por +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} +e +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES}.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ConstanteDescrição
    {@link android.provider.CalendarContract.Instances#BEGIN}O horário de início da instância, em milissegundos UTC.
    {@link android.provider.CalendarContract.Instances#END}O horário de término da instância, em milissegundos UTC.
    {@link android.provider.CalendarContract.Instances#END_DAY}O dia final juliano da instância relativo ao fuso horário +do Agenda. + +
    {@link android.provider.CalendarContract.Instances#END_MINUTE}O minuto final da instância calculado a partir de meia-noite +no fuso horário do Agenda.
    {@link android.provider.CalendarContract.Instances#EVENT_ID}O _ID do evento para essa instância.
    {@link android.provider.CalendarContract.Instances#START_DAY}O dia inicial juliano da instância relativo ao fuso horário do Agenda. +
    {@link android.provider.CalendarContract.Instances#START_MINUTE}O minuto inicial da instância calculado a partir de meia-noite, relativo +ao fuso horário do Agenda. +
    + +

    Consulta na tabela de instâncias

    + +

    Para consultar a Tabela de instâncias, é necessário especificar um intervalo de tempo para a consulta +na URI. Neste exemplo, {@link android.provider.CalendarContract.Instances} +obtém acesso ao campo {@link +android.provider.CalendarContract.EventsColumns#TITLE} por meio +da sua implementação da interface {@link android.provider.CalendarContract.EventsColumns}. +Em outras palavras, {@link +android.provider.CalendarContract.EventsColumns#TITLE} é retornado por uma +vista do banco de dados, não pela consulta da tabela {@link +android.provider.CalendarContract.Instances} bruta.

    + +
    +private static final String DEBUG_TAG = "MyActivity";
    +public static final String[] INSTANCE_PROJECTION = new String[] {
    +    Instances.EVENT_ID,      // 0
    +    Instances.BEGIN,         // 1
    +    Instances.TITLE          // 2
    +  };
    +  
    +// The indices for the projection array above.
    +private static final int PROJECTION_ID_INDEX = 0;
    +private static final int PROJECTION_BEGIN_INDEX = 1;
    +private static final int PROJECTION_TITLE_INDEX = 2;
    +...
    +
    +// Specify the date range you want to search for recurring
    +// event instances
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2011, 9, 23, 8, 0);
    +long startMillis = beginTime.getTimeInMillis();
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2011, 10, 24, 8, 0);
    +long endMillis = endTime.getTimeInMillis();
    +  
    +Cursor cur = null;
    +ContentResolver cr = getContentResolver();
    +
    +// The ID of the recurring event whose instances you are searching
    +// for in the Instances table
    +String selection = Instances.EVENT_ID + " = ?";
    +String[] selectionArgs = new String[] {"207"};
    +
    +// Construct the query with the desired date range.
    +Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
    +ContentUris.appendId(builder, startMillis);
    +ContentUris.appendId(builder, endMillis);
    +
    +// Submit the query
    +cur =  cr.query(builder.build(), 
    +    INSTANCE_PROJECTION, 
    +    selection, 
    +    selectionArgs, 
    +    null);
    +   
    +while (cur.moveToNext()) {
    +    String title = null;
    +    long eventID = 0;
    +    long beginVal = 0;    
    +    
    +    // Get the field values
    +    eventID = cur.getLong(PROJECTION_ID_INDEX);
    +    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    +    title = cur.getString(PROJECTION_TITLE_INDEX);
    +              
    +    // Do something with the values. 
    +    Log.i(DEBUG_TAG, "Event:  " + title); 
    +    Calendar calendar = Calendar.getInstance();
    +    calendar.setTimeInMillis(beginVal);  
    +    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    +    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));    
    +    }
    + }
    + +

    Intenções do Agenda

    +

    O aplicativo não precisa de permissões para ler e gravar dados de agenda. Em vez disso, ele pode usar intenções compatíveis com o aplicativo Agenda do Android para entregar operações de leitura e gravação. A tabela a seguir lista as intenções compatíveis com o Provedor de Agenda.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    AçãoURIDescriçãoExtras

    + {@link android.content.Intent#ACTION_VIEW VIEW}

    content://com.android.calendar/time/<ms_since_epoch>

    + Também pode-se consultar a URI com +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI}. +Para ver um exemplo do uso dessa intenção, consulte Uso de intenções para exibir dados de calendários. + +
    Abre a agenda no horário especificado por <ms_since_epoch>.Nenhum.

    {@link android.content.Intent#ACTION_VIEW VIEW}

    + +

    content://com.android.calendar/events/<event_id>

    + + Também é possível consultar a URI com +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Para ver um exemplo do uso dessa intenção, consulte Uso de intenções para exibir dados de calendários. + +
    Exibe o evento especificado por <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
    +
    +
    + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
    {@link android.content.Intent#ACTION_EDIT EDIT}

    content://com.android.calendar/events/<event_id>

    + + Também é possível consultar a URI com +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Para ver um exemplo do uso dessa intenção, consulte Uso de uma intenção para editar um evento. + + +
    Edita o evento especificado por <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
    +
    +
    + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
    {@link android.content.Intent#ACTION_EDIT EDIT}
    +
    + {@link android.content.Intent#ACTION_INSERT INSERT}

    content://com.android.calendar/events

    + + Também é possível consultar a URI com +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Para ver um exemplo do uso dessa intenção, consulte Uso de uma intenção para inserir um evento. + +
    Cria um evento.Qualquer um dos extras listados na tabela abaixo.
    + +

    A tabela a seguir lista os extras de intenção compatíveis com o Provedor de Agenda: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Extra de intençãoDescrição
    {@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}Nome do evento.
    {@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}Horário de início do evento em milissegundos a partir da época.
    {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}Horário de término do evento em milissegundos a partir da época.
    {@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}Um booleano que indica que um evento acontece o dia inteiro. O valor pode ser +true ou false.
    {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}Local do evento.
    {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}Descrição do evento.
    + {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}Endereços de e-mail daqueles a convidar em forma de lista com termos separados por vírgula.
    + {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}A regra de recorrência do evento.
    + {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}Se o evento é privado ou público.
    {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}Se esse evento considera tempo ocupado na contagem ou se há tempo livre que pode ser reagendado.
    +

    As seções a seguir descrevem como usar estas intenções.

    + + +

    Uso de uma intenção para inserir um evento

    + +

    A intenção {@link android.content.Intent#ACTION_INSERT INSERT} +permite que o aplicativo entregue a tarefa de inserção de eventos ao próprio aplicativo Agenda. +Com essa abordagem, o aplicativo não precisará ter a permissão {@link +android.Manifest.permission#WRITE_CALENDAR} contida no arquivo de manifesto.

    + + +

    Quando usuários executam um aplicativo que usa essa abordagem, ele os direciona +ao Agenda para finalizar a adição do evento. A intenção {@link +android.content.Intent#ACTION_INSERT INSERT} usa campos extras para +pré-preencher um formulário com os detalhes do evento na Agenda. Os usuários podem, +então, cancelar o evento, editar o formulário conforme o necessário ou salvar o evento nas suas +agendas.

    + + + +

    A seguir há um fragmento de código que agenda um evento em 19 de janeiro de 2012, que acontece +das 7h30 às 8h30. Observe o exposto a seguir sobre esse fragmento de código:

    + +
      +
    • Ele especifica {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} + como a URI.
    • + +
    • Ele usa os campos extras {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} e {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} para pré-preencher o formulário +com o horário do evento. Os valores desses horários devem estar em milissegundos UTC +da época.
    • + +
    • Ele usa o campo extra {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} + para fornecer uma lista de termos separados por vírgula de convidados, especificados por endereço de e-mail.
    • + +
    +
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2012, 0, 19, 7, 30);
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2012, 0, 19, 8, 30);
    +Intent intent = new Intent(Intent.ACTION_INSERT)
    +        .setData(Events.CONTENT_URI)
    +        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
    +        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
    +        .putExtra(Events.TITLE, "Yoga")
    +        .putExtra(Events.DESCRIPTION, "Group class")
    +        .putExtra(Events.EVENT_LOCATION, "The gym")
    +        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
    +        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
    +startActivity(intent);
    +
    + +

    Uso de uma intenção para editar um evento

    + +

    É possível atualizar um evento diretamente, como descrito em Atualização de eventos. Porém, o uso da intenção {@link +android.content.Intent#ACTION_EDIT EDIT} permite que um aplicativo +sem permissão forneça a edição de eventos ao aplicativo Agenda. +Quando usuários finalizam a edição do evento no Agenda, retornam +ao aplicativo original.

    A seguir há um exemplo de uma intenção que define um novo +título para o evento especificado e permite aos usuários editar o evento no Agenda.

    + + +
    long eventID = 208;
    +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +Intent intent = new Intent(Intent.ACTION_EDIT)
    +    .setData(uri)
    +    .putExtra(Events.TITLE, "My New Title");
    +startActivity(intent);
    + +

    Uso de intenções para exibir dados de agenda

    +

    O Provedor de Agenda oferece dois modos diferentes de usar a intenção {@link android.content.Intent#ACTION_VIEW VIEW}:

    +
      +
    • Para abrir o Agenda em uma data específica.
    • +
    • Para exibir um evento.
    • + +
    +

    A seguir há um exemplo que mostra como abrir o Agenda em uma data específica:

    +
    // A date-time specified in milliseconds since the epoch.
    +long startMillis;
    +...
    +Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
    +builder.appendPath("time");
    +ContentUris.appendId(builder, startMillis);
    +Intent intent = new Intent(Intent.ACTION_VIEW)
    +    .setData(builder.build());
    +startActivity(intent);
    + +

    Abaixo há um exemplo que mostra como abrir um evento para exibição:

    +
    long eventID = 208;
    +...
    +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +Intent intent = new Intent(Intent.ACTION_VIEW)
    +   .setData(uri);
    +startActivity(intent);
    +
    + + +

    Adaptadores de sincronização

    + + +

    Há pequenas diferenças apenas nos modos de acesso ao Provedor de Agenda +via aplicativo e via adaptador de sincronização:

    + +
      +
    • Um adaptador de sincronização precisa especificar que é um adaptador de sincronização que define {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} como true.
    • + + +
    • Os adaptadores de sincronização precisam fornecer um {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} e um {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} como parâmetros da consulta na URI.
    • + +
    • Os adaptadores de sincronização têm acesso de gravação a mais colunas do que um aplicativo ou widget. + Por exemplo: um aplicativo só pode modificar algumas características de uma agenda, +como nome, nome de exibição, configurações de visibilidade e se a agenda está +sincronizada. Por comparação, um adaptador de sincronização pode acessar não somente essas colunas, mas muitas outras, +como cores da agenda, fuso horário, nível de acesso, local etc. +No entanto, um adaptador de sincronização é restrito ao ACCOUNT_NAME +e ao ACCOUNT_TYPE que especificou.
    + +

    A seguir há um método auxiliar que pode ser usado para retornar uma URI para uso com um adaptador de sincronização:

    +
     static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    +    return uri.buildUpon()
    +        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
    +        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
    +        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
    + }
    +
    +

    Para obter uma implementação de exemplo de um adaptador de sincronização (não especificamente relacionada ao Agenda), consulte +SampleSyncAdapter. diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/contacts-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..0d42d2daed530d74530c8bce80ac736a297ee3a9 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=Provedor de Contatos +@jd:body +

    +
    +

    Visualização rápida

    +
      +
    • Repositório de informações sobre pessoas do Android.
    • +
    • + Sincronização com a web. +
    • +
    • + Integração de dados de fluxos sociais. +
    • +
    +

    Neste documento

    +
      +
    1. + Organização do Provedor de Contatos +
    2. +
    3. + Contatos brutos +
    4. +
    5. + Dados +
    6. +
    7. + Contatos +
    8. +
    9. + Dados de adaptadores de sincronização +
    10. +
    11. + Permissões necessárias +
    12. +
    13. + O perfil do usuário +
    14. +
    15. + Metadados do Provedor de Contatos +
    16. +
    17. + Acesso ao Provedor de Contatos +
    18. +
    19. +
    20. + Adaptadores de sincronização do Provedor de Contatos +
    21. +
    22. + Dados de fluxos sociais +
    23. +
    24. + Recursos adicionais do Provedor de Contatos +
    25. +
    +

    Classes principais

    +
      +
    1. {@link android.provider.ContactsContract.Contacts}
    2. +
    3. {@link android.provider.ContactsContract.RawContacts}
    4. +
    5. {@link android.provider.ContactsContract.Data}
    6. +
    7. {@code android.provider.ContactsContract.StreamItems}
    8. +
    +

    Exemplos relacionados

    +
      +
    1. + + Gerente de contatos + +
    2. +
    3. + + Exemplo de adaptador de sincronização +
    4. +
    +

    Veja também

    +
      +
    1. + + Preceitos do provedor de conteúdo + +
    2. +
    +
    +
    +

    + O Provedor de Contatos é um componente poderoso e flexível do Android que gerencia +o principal repositório de dados sobre pessoas do dispositivo. O Provedor de Contatos é a fonte dos dados + vistos nos aplicativos de contatos do dispositivo e, por ele, também é possível acessar os dados + no próprio aplicativo e transferi-los entre o dispositivo e serviços on-line. O provedor fornece + uma grande variedade de fontes de dados e tenta gerenciar o máximo de dados possíveis para cada pessoa, + uma vez que organizá-los é algo complexo. Por isso, a API do provedor contém + um conjunto extensivo de classes e interfaces de contrato que facilitam a recuperação e a modificação + dos dados. +

    +

    + Este guia descreve o seguinte: +

    +
      +
    • + A estrutura básica do provedor. +
    • +
    • + Como recuperar dados por um provedor. +
    • +
    • + Como modificar dados no provedor. +
    • +
    • + Como criar um adaptador de sincronização para sincronizar dados do servidor com +o Provedor de Contatos. +
    • +
    +

    + Este guia considera que o leitor conhece os preceitos dos provedores de conteúdo do Android. Para saber mais + sobre provedores de conteúdo do Android, leia o guia + + Preceitos do provedor de conteúdo. + O aplicativo Exemplo de adaptador de sincronização + é um exemplo de uso de um adaptador de sincronização que transfere dados entre o Provedor + de contatos e um aplicativo de amostra hospedado pelo Google Web Services. +

    +

    Organização do Provedor de Contatos

    +

    + O Provedor de Contatos é um componente do provedor de conteúdo do Android. Ele mantém três tipos de + dados sobre uma pessoa, sendo cada um deles correspondente a uma tabela fornecida pelo provedor, como + ilustrado na figura 1: +

    + +

    + Figura 1. Estrutura da tabela do Provedor de Contatos. +

    +

    + As três tabelas são comumente identificadas pelo nome de suas classes de contrato. As classes + definem constantes para URIs de conteúdo e nomes e valores de colunas usados pela tabela: +

    +
    +
    + Tabela {@link android.provider.ContactsContract.Contacts} +
    +
    + As linhas representam pessoas diferentes com base em agregações de linhas de contatos brutos. +
    +
    + Tabela {@link android.provider.ContactsContract.RawContacts} +
    +
    + As linhas contêm um resumo dos dados de uma pessoa, específicos a um tipo e uma conta de usuário. +
    +
    + Tabela {@link android.provider.ContactsContract.Data} +
    +
    + As linhas contêm os detalhes dos contatos brutos, como endereços de e-mail ou números de telefone. +
    +
    +

    + As outras tabelas representadas pelas classes de contrato em {@link android.provider.ContactsContract} + são tabelas auxiliares que o Provedor de Contatos usa para gerenciar suas operações ou compatibilizar + funções específicas nos contatos do dispositivo ou em aplicativos de telefonia. +

    +

    Contatos brutos

    +

    + Os contatos brutos representam os dados de uma pessoa provenientes de um tipo único de conta e um nome + de conta. Como o Provedor de Contatos permite mais de um serviço on-line como fonte +de dados de uma pessoa, ele permite diversos contatos brutos para a mesma pessoa. + Diversos contatos brutos também permitem que um usuário combine os dados de uma pessoa de mais de uma conta + a partir do mesmo tipo de conta. +

    +

    + A maioria dos dados de um contato bruto não é armazenada + na tabela {@link android.provider.ContactsContract.RawContacts}. Em vez disso, é armazenada em uma ou mais + linhas na tabela {@link android.provider.ContactsContract.Data}. Cada linha de dados tem uma coluna + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID} que + contém o valor {@code android.provider.BaseColumns#_ID RawContacts._ID} de sua + linha {@link android.provider.ContactsContract.RawContacts} pai. +

    +

    Colunas importantes de contatos brutos

    +

    + As colunas importantes na tabela {@link android.provider.ContactsContract.RawContacts} são + listadas na tabela 1. Leia as observações que se seguem após a tabela: +

    +

    + Tabela 1. Importantes colunas de contatos brutos. +

    + + + + + + + + + + + + + + + + + + + + +
    Nome da colunaUsoObservações
    + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + + O nome da conta para o tipo de conta que é a fonte desse contato bruto. + Por exemplo: o nome da conta de uma conta da Google é um dos endereços do Gmail do proprietário + do dispositivo. Para obter mais informações, + acesse o próximo item + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}. + + O formato desse nome é específico deste tipo de conta. + Não se trata necessariamente de um endereço de e-mail. +
    + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + + O tipo de conta que é a fonte desse contato bruto. Por exemplo: o tipo + de conta de uma conta da Google é com.google. Sempre qualifique o tipo de conta + com um identificador de domínio para um domínio que você controla ou possui. Isso garantirá que seu + tipo de conta seja único. + + Os tipos de conta que fornecem dados de contatos normalmente têm um adaptador de sincronização associado que + sincroniza-se com o Provedor de Contatos. +
    + {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + + O sinalizador "excluído" de um contato bruto. + + Esse sinalizador permite que o Provedor de Contatos mantenha a linha internamente até + que os adaptadores de sincronização possam excluí-la dos servidores e, em seguida, excluí-la + do repositório. +
    +

    Observações

    +

    + As observações a seguir sobre a tabela + {@link android.provider.ContactsContract.RawContacts} são importantes: +

    +
      +
    • + O nome de um contato bruto não é armazenado em sua linha em + {@link android.provider.ContactsContract.RawContacts}. Em vez disso, é armazenado na + tabela {@link android.provider.ContactsContract.Data}, em uma + linha {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}. Os contatos brutos + têm apenas uma linha desse tipo na tabela {@link android.provider.ContactsContract.Data}. +
    • +
    • + Atenção: para usar os dados da própria conta em uma linha de contato bruto, é necessário + registrá-la primeiro com o {@link android.accounts.AccountManager}. Para isso, faça com que + os usuários adicionem o tipo e o nome da conta à lista de contas. Caso + contrário, o Provedor de Contatos excluirá a linha do contato bruto automaticamente. +

      + Por exemplo: se quiser que o aplicativo mantenha dados de contato do seu serviço baseado em web + com o {@code com.example.dataservice} de domínio e com a conta do usuário do serviço + {@code becky.sharp@dataservice.example.com}, o usuário precisa, primeiramente, adicionar o "tipo" + de conta ({@code com.example.dataservice}) e o "nome" da conta + ({@code becky.smart@dataservice.example.com}) antes que o aplicativo adicione linhas de contato bruto. + Você pode explicar esse requisito ao usuário em documentações ou pode exigir que o + usuário adicione o tipo ou o nome ou ambos. Tipos e nomes de conta + são descritos com mais detalhes na próxima seção. +

    • +
    +

    Fontes de dados de contatos brutos

    +

    + Para compreender como os contatos brutos funcionam, considere a usuária "Emily Dickinson" que tem as seguintes + três contas de usuário definidas no seu dispositivo: +

    +
      +
    • emily.dickinson@gmail.com
    • +
    • emilyd@gmail.com
    • +
    • Conta do twitter "belle_of_amherst"
    • +
    +

    + Essa usuária ativou Sincronizar contatos para todas as três contas nas + Configurações da conta. +

    +

    + Suponhamos que Emily Dickinson abra uma janela do navegador, acesse o Gmail como + emily.dickinson@gmail.com, abra + Contatos e adicione "Thomas Higginson". Mais tarde, ela acessa o Gmail como + emilyd@gmail.com e envia um e-mail para "Thomas Higginson", o que automaticamente + o adiciona como um contato. Ela também segue "colonel_tom" (ID do twitter de Thomas Higginson) no + Twitter. +

    +

    + O Provedor de Contatos cria três contatos brutos como resultado desse trabalho: +

    +
      +
    1. + Um contato bruto de "Thomas Higginson" associado a emily.dickinson@gmail.com. + O tipo de conta do usuário é Google. +
    2. +
    3. + Um segundo contato bruto de "Thomas Higginson" associado a emilyd@gmail.com. + O tipo de conta do usuário também é Google. Há um segundo contato bruto, + embora o nome seja idêntico a um nome anterior porque a pessoa foi adicionada + a uma conta de usuário diferente. +
    4. +
    5. + Um terceiro contato bruto de "Thomas Higginson" associado a "belle_of_amherst". O tipo + de conta de usuário é Twitter. +
    6. +
    +

    Dados

    +

    + Como observado anteriormente, os dados de um contato bruto são armazenados + em uma linha {@link android.provider.ContactsContract.Data} vinculada ao valor _ID + do contato bruto. Isso permite que um único contato bruto tenha diversas instâncias do mesmo + tipo de dados, como endereços de e-mail ou números de telefone. Por exemplo: se + "Thomas Higginson" para {@code emilyd@gmail.com} (a linha do contato bruto de Thomas Higginson + associada à conta Google emilyd@gmail.com) tem um endereço de e-mail pessoal + thigg@gmail.com e um de trabalho + thomas.higginson@gmail.com, o Provedor de Contatos armazena as duas linhas de endereço de e-mail + e vincula ambas ao contato bruto. +

    +

    + Observe que diferentes tipos de dados são armazenados nessa única tabela. Linhas de nome de exibição, + número de telefone, e-mail, endereço postal, foto e detalhes de site são encontradas + na tabela {@link android.provider.ContactsContract.Data}. Para ajudar a gerenciar isso, + a tabela {@link android.provider.ContactsContract.Data} tem algumas colunas com nomes descritivos + e outras com nomes genéricos. O conteúdo de uma coluna de nome descritivo tem o mesmo significado + independente do tipo de dado da linha, enquanto o conteúdo de uma coluna de nome genérico tem + diferentes significados dependendo do tipo de dados. +

    +

    Nomes de coluna descritiva

    +

    + A seguir há alguns exemplos de nomes descritivos de colunas: +

    +
    +
    + {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} +
    +
    + O valor da coluna _ID do contato bruto para estes dados. +
    +
    + {@link android.provider.ContactsContract.Data#MIMETYPE} +
    +
    + O tipo de dado armazenado nessa linha, expresso como um tipo MIME personalizado. O Provedor de Contatos + usa os tipos MIME definidos nas subclasses de + {@link android.provider.ContactsContract.CommonDataKinds}. Esses tipos MIME têm código aberto + e podem ser usados por qualquer aplicativo ou adaptador de sincronização que funcione com o Provedor de Contatos. +
    +
    + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} +
    +
    + Se esse tipo de linha de dados puder ocorrer mais do que uma vez em um contato bruto, + a coluna {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} sinaliza + a linha de dados que contém os dados primários do tipo. Por exemplo: se + o usuário pressionar por algum tempo um número de telefone de um contato e selecionar Definir padrão, + a linha {@link android.provider.ContactsContract.Data} que contém esse número + tem sua coluna {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} definida + como um valor diferente de zero. +
    +
    +

    Nomes de coluna genérica

    +

    + Há 15 colunas genéricas de nome DATA1 a + DATA15 que estão, em geral, disponíveis e quatro colunas genéricas + adicionais SYNC1 a SYNC4 que devem ser usadas somente por adaptadores + de sincronização. As constantes de nome da coluna genérica sempre funcionam independentemente do tipo + de dados que a linha contenha. +

    +

    + A coluna DATA1 é indexada. O Provedor de Contatos sempre usa essa coluna para + os dados que o provedor espera serem os alvos mais frequentes de uma consulta. Por exemplo: + em uma linha de e-mail, essa coluna contém o endereço de e-mail atual. +

    +

    + Por convenção, a coluna DATA15 é reservada para armazenar dados de BLOBs + (Binary Large Object), como miniaturas de fotos. +

    +

    Nomes de coluna de tipo específico

    +

    + Para facilitar o trabalho com as colunas para um tipo específico de linha, o Provedor de Contatos + também fornece constantes de nome de colunas de tipo específico, definidas em subclasses de + {@link android.provider.ContactsContract.CommonDataKinds}. As constantes simplesmente dão + um nome de constante diferente para o mesmo nome de coluna, o que ajuda no acesso aos dados em uma linha + de um tipo específico. +

    +

    + Por exemplo: a classe {@link android.provider.ContactsContract.CommonDataKinds.Email} define + constantes de nome de coluna de tipo específico para uma linha {@link android.provider.ContactsContract.Data} + que tem o tipo MIME + {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE}. A classe contém a constante + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} para a coluna de endereço + de e-mail. O valor atual + de {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} é "data1", que é + igual ao nome genérico da coluna. +

    +

    + Atenção: não adicione seus dados personalizados + à tabela {@link android.provider.ContactsContract.Data} usando uma linha que tenha um dos + tipos de MIME predefinidos do provedor. Caso contrário, há o risco de perda de dados ou mau funcionamento + do provedor. Por exemplo: não se deve adicionar uma linha com o tipo MIME + {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE} que contenha um nome de usuário em vez de um endereço de e-mail na + coluna DATA1. Se for usado um tipo MIME personalizado para a linha, será possível + definir os próprios nomes de coluna de tipo específico e usar as colunas como quiser. +

    +

    + A figura 2 mostra como colunas descritivas e colunas de dados aparecem + em uma linha {@link android.provider.ContactsContract.Data} e como os nomes de coluna de tipo específico se "sobrepõem" + aos nomes de coluna genérica. +

    +How type-specific column names map to generic column names +

    + Figura 2. Nomes de coluna de tipo específico e nomes de coluna genérica. +

    +

    Classes de nome de coluna de tipo específico

    +

    + A tabela 2 lista as classes de nome de coluna de tipo específico mais usadas: +

    +

    + Tabela 2. Classes de nome de coluna de tipo específico

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Classes de mapeamentoTipo de dadosObservações
    {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}Os dados de nome do contato bruto associados a essa linha de dados.Os contatos brutos têm apenas uma dessas linhas.
    {@link android.provider.ContactsContract.CommonDataKinds.Photo}A foto principal do contato bruto associada a essa linha de dados.Os contatos brutos têm apenas uma dessas linhas.
    {@link android.provider.ContactsContract.CommonDataKinds.Email}Um endereço de e-mail do contato bruto associado a essa linha de dados.Os contatos brutos podem ter diversos endereços de e-mail.
    {@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}Um endereço postal do contato bruto associado a essa linha de dados.Os contatos brutos podem ter diversos endereços postais.
    {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}Um identificador que vincula o contato bruto a um dos grupos no Provedor de Contatos. + Grupos são um recurso opcional de um tipo e um nome de conta. Eles são descritos + mais detalhadamente na seção Grupos de contato. +
    +

    Contatos

    +

    + O Provedor de Contatos combina as linhas do contato bruto entre todos os tipos e nomes de conta + para formar um contato. Isso facilita a exibição e modificação de todos os dados de uma pessoa que um + usuário tenha coletado. O Provedor de Contatos gerencia a criação de linhas + de novos contatos e a agregação de contatos brutos a uma linha de contato existente. Nem os aplicativos, nem + os adaptadores de sincronização têm permissão para adicionar contatos, e algumas colunas em uma linha de contato são de somente leitura. +

    +

    + Observação: a tentativa de adicionar um contato ao Provedor de Contatos com um + {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()} gerará + uma exceção {@link java.lang.UnsupportedOperationException}. A tentativa de atualizar uma coluna + listada como "somente leitura" será ignorada. +

    +

    + O Provedor de Contatos cria um novo contato em resposta à adição de um novo contato bruto + que não corresponda a nenhum contato existente. O provedor também faz isso se os dados de um + contato bruto existente mudam de modo a não corresponder mais ao contato + inserido anteriormente. Se um aplicativo ou um adaptador de sincronização criar um novo contato bruto que + corresponda a um contato existente, o novo contato bruto será agregado ao contato + existente. +

    +

    + O Provedor de Contatos liga uma linha do contato às linhas do contato bruto com a coluna _ID + da linha do contato na tabela + {@link android.provider.ContactsContract.Contacts Contacts}. A coluna CONTACT_ID da tabela de contatos brutos + {@link android.provider.ContactsContract.RawContacts} contém valores _ID para + a linha dos contatos associados a cada linha dos contatos brutos. +

    +

    + A tabela {@link android.provider.ContactsContract.Contacts} também tem a coluna + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY}, que é + um vínculo "permanente" com a linha do contato. Como o Provedor de Contatos mantém contatos + automaticamente, ele pode mudar o valor de {@code android.provider.BaseColumns#_ID} da linha do contato + em resposta a uma agregação ou sincronização. Mesmo que isso aconteça, a URI de conteúdo + {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} combinada com + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} do contato continuará + apontado para a linha do contato para permitir o uso de + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} + e manter ligações com contatos "favoritos" e assim por diante. Essa coluna tem o próprio formato, que + não tem nenhuma relação com o formato da coluna {@code android.provider.BaseColumns#_ID}. +

    +

    + A figura 3 mostra como as três tabelas principais se relacionam entre si. +

    +Contacts provider main tables +

    + Figura 3. Contatos, contatos brutos e relacionamentos da tabela de detalhes. +

    +

    Dados de adaptadores de sincronização

    +

    + Os usuários inserem dados de contato diretamente no dispositivo, mas os dados também são direcionados ao Provedor + de Contatos a partir de serviços web via adaptadores de sincronização, que automatizam + a transferência de dados entre dispositivo e serviços. Adaptadores de sincronização funcionam em segundo plano, + controlados pelo sistema, e chamam métodos {@link android.content.ContentResolver} + para gerenciar os dados. +

    +

    + No Android, o serviço web com o qual um adaptador de sincronização trabalha é identificado por um tipo de conta. + Cada adaptador de sincronização funciona com um tipo de conta, mas é compatível com diversos nomes de conta + para o tipo em questão. Tipos e nomes de conta são descritos brevemente na seção + Fontes de dados de contatos brutos. As definições a seguir fornecem + mais detalhes e descrevem como o tipo e o nome de conta se relacionam com adaptadores de sincronização e serviços. +

    +
    +
    + Tipo de conta +
    +
    + Identifica um serviço em que o usuário tenha armazenado dados. Na maior parte do tempo, o usuário deve + autenticar com o serviço. Por exemplo: Google Contacts é um tipo de conta, identificado + pelo código google.com. Esse valor corresponde ao tipo de conta usado pelo + {@link android.accounts.AccountManager}. +
    +
    + Nome da conta +
    +
    + Identifica uma conta ou login específico de um tipo de conta. As contas Google Contacts + são idênticas às contas Google, que têm um endereço de e-mail como nome da conta. + Outros serviços podem usar um nome de usuário com só uma palavra ou ID numérico. +
    +
    +

    + Os tipos de conta não precisam ser exclusivos. Um usuário pode configurar diversas contas Google Contacts + e baixar os dados dela para o Provedor de Contatos. Isso pode acontecer se o usuário tiver um grupo de + contatos pessoais para um nome de conta pessoal e outro grupo para um de conta de trabalho. Nomes de conta normalmente + são exclusivos. Juntos, eles identificam um fluxo de dados específicos entre o Provedor de Contatos e + um serviço externo. +

    +

    + Se você desejar transferir os dados do serviço ao Provedor de Contatos, precisará criar seu + próprio adaptador de sincronização. Isso é descrito com mais detalhes na seção + Adaptadores de sincronização do Provedor de Contatos. +

    +

    + A figura 4 mostra como o Provedor de Contatos se insere no fluxo de dados + sobre pessoas. Na caixa marcada "adaptadores de sincronização", cada adaptador é etiquetado pelo tipo de conta. +

    +Flow of data about people +

    + Figura 4. Fluxo de dados do Provedor de Contatos. +

    +

    Permissões necessárias

    +

    + Os aplicativos que queiram acessar o Provedor de Contatos devem solicitar as seguintes + permissões: +

    +
    +
    Acesso de leitura a uma ou mais tabelas
    +
    + {@link android.Manifest.permission#READ_CONTACTS}, especificado em + AndroidManifest.xml com + o elemento + <uses-permission> + como <uses-permission android:name="android.permission.READ_CONTACTS">. +
    +
    Acesso de gravação a uma ou mais tabelas
    +
    + {@link android.Manifest.permission#WRITE_CONTACTS}, especificado em + AndroidManifest.xml com + o elemento + <uses-permission> + como <uses-permission android:name="android.permission.WRITE_CONTACTS">. +
    +
    +

    + Essas permissões não se estendem aos dados do perfil do usuário. O perfil do usuário + e suas permissões necessárias são abordados na seção a seguir: + O perfil do usuário. +

    +

    + Lembre-se de que os dados de contato do usuário são pessoais e confidenciais. Os usuários se preocupam + com a privacidade e, por isso, não querem aplicativos que coletem dados sobre eles ou seus contatos. + Se não for óbvio o motivo da necessidade de permissões para acessar os dados de contato de um usuário, eles podem atribuir + avaliações ruins ao seu aplicativo ou simplesmente não instalá-lo. +

    +

    O perfil do usuário

    +

    + A tabela {@link android.provider.ContactsContract.Contacts} tem uma única linha contendo + os dados do perfil do usuário do dispositivo. Esses dados descrevem o user do dispositivo em vez + de um dos contatos do usuário. A linha de contatos do perfil é vinculada a uma linha + de contatos brutos para cada sistema que usa um perfil. + Cada linha de contato bruto de perfil pode ter diversas linhas de dados. Constantes de acesso ao perfil + do usuário estão disponíveis na classe {@link android.provider.ContactsContract.Profile}. +

    +

    + O acesso ao perfil do usuário exige permissões especiais. Além das permissões + {@link android.Manifest.permission#READ_CONTACTS} e + {@link android.Manifest.permission#WRITE_CONTACTS} necessárias para ler e gravar, o acesso + ao perfil do usuário requer as permissões {@code android.Manifest.permission#READ_PROFILE} e + {@code android.Manifest.permission#WRITE_PROFILE}, respectivamente, para ler e + gravar. +

    +

    + Lembre-se de que é preciso considerar a confidencialidade de um perfil do usuário. A permissão + {@code android.Manifest.permission#READ_PROFILE} permite o acesso aos dados de identificação + pessoal do usuário do dispositivo. Certifique-se de informar ao usuário o motivo + da necessidade de permissões de acesso ao perfil do usuário na descrição do aplicativo. +

    +

    + Para recuperar a linha de contato que contém o perfil do usuário, + chame {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) + ContentResolver.query()}. Defina a URI de conteúdo como + {@link android.provider.ContactsContract.Profile#CONTENT_URI} e não forneça nenhum + critério de seleção. Também é possível usar essa URI de conteúdo como base para recuperar + contatos brutos ou dados do perfil. Por exemplo, esse fragmento recupera dados do perfil: +

    +
    +// Sets the columns to retrieve for the user profile
    +mProjection = new String[]
    +    {
    +        Profile._ID,
    +        Profile.DISPLAY_NAME_PRIMARY,
    +        Profile.LOOKUP_KEY,
    +        Profile.PHOTO_THUMBNAIL_URI
    +    };
    +
    +// Retrieves the profile from the Contacts Provider
    +mProfileCursor =
    +        getContentResolver().query(
    +                Profile.CONTENT_URI,
    +                mProjection ,
    +                null,
    +                null,
    +                null);
    +
    +

    + Observação: se você recuperar diversas linhas de contato e quiser determinar se uma delas + é o perfil do usuário, teste a coluna + {@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} da linha. Essa coluna + é definida como "1" se o contato for o perfil do usuário. +

    +

    Metadados do Provedor de Contatos

    +

    + O Provedor de Contatos gerencia dados que acompanham o estado dos dados de contatos + no repositório. Esses metadados sobre o repositório são armazenados em vários locais, inclusive + os contatos brutos, os dados e as linhas da tabela de contatos, + a tabela {@link android.provider.ContactsContract.Settings} + e a tabela {@link android.provider.ContactsContract.SyncState}. A tabela a seguir mostra + o efeito de cada uma dessas partes de metadados: +

    +

    + Tabela 3. Metadados no Provedor de Contatos

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TabelaColunaValoresSignificado
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#DIRTY}"0" - sem modificação desde a última sincronização. + Sinaliza contatos brutos que não mudaram no dispositivo e devem ser sincronizados com + o servidor. O valor é definido automaticamente pelo Provedor de Contatos quando os aplicativos + do Android atualizam uma linha. +

    + Adaptadores de sincronização que modificam o contato bruto ou as tabelas de dados sempre devem anexar a + string {@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER} + à URI de conteúdo usada. Isso evita que o provedor sinalize alguma linha como suja. + Caso contrário, as modificações do adaptador de sincronização parecem ser modificações locais e são + enviadas ao servidor, mesmo que o servidor seja a origem da modificação. +

    +
    "1" - modificado desde a última sincronização, precisa ser sincronizado com o servidor.
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#VERSION}O número da versão dessa linha. + O Provedor de Contatos incrementa esse valor automaticamente sempre que a linha ou + os dados relacionados a ela mudam. +
    {@link android.provider.ContactsContract.Data}{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}O número da versão dessa linha. + O Provedor de Contatos incrementa esse valor automaticamente sempre que a linha de dados + é modificada. +
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} + Valor de string que identifica exclusivamente esse contato bruto para a conta em + que foi criado. + + Quando um adaptador de sincronização cria um novo contato bruto, essa coluna deve ser definida como + o ID exclusivo do servidor para o contato bruto. Quando um aplicativo do Android cria um novo + contato bruto, o aplicativo deve deixar essa coluna vazia. Isso sinaliza ao adaptador + de sincronização que ele deve criar um novo contato bruto no servidor e obter + um valor para o {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}. +

    + Em particular, o ID de origem deve ser exclusivo de cada tipo + de conta e estável em sincronizações: +

    +
      +
    • + Exclusivo: cada contato bruto de uma conta deve ter o próprio ID de origem. Se isso + não for aplicado, haverá problemas no aplicativo de contatos. + Observe que dois contatos brutos do mesmo tipo de conta podem ter + o mesmo ID de origem. Por exemplo: o contato bruto "Thomas Higginson" da + conta {@code emily.dickinson@gmail.com} pode ter o mesmo ID + de origem do contato bruto "Thomas Higginson" da conta + {@code emilyd@gmail.com}. +
    • +
    • + Estável: IDs de origem são uma parte permanente dos dados do serviço on-line para + o contato bruto. Por exemplo: se o usuário apaga o Armazenamento de Contatos + nas configurações dos aplicativos e ressincroniza, os contatos brutos restaurados devem ter os mesmos + IDs de origem de antes. Se isso não for aplicado, os atalhos + deixarão de funcionar. +
    • +
    +
    {@link android.provider.ContactsContract.Groups}{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}"0" - os contatos nesse grupo não devem ser visíveis em IUs do aplicativo do Android. + Essa coluna se destina à compatibilidade com servidores, que permitem que um usuário oculte contatos + em determinados grupos. +
    "1" - os contatos nesse grupo podem ser visíveis nas IUs do aplicativo.
    {@link android.provider.ContactsContract.Settings} + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} + "0" - para essa conta e esse tipo de conta, os contatos que não pertencem a um grupo são + invisíveis nas IUs do aplicativo do Android. + + Por padrão, os contatos são invisíveis se nenhum dos contatos brutos pertencer a algum grupo + (a associação de grupo de um contato bruto é indicada por uma ou mais + linhas {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} + na tabela {@link android.provider.ContactsContract.Data}). + Por padrão, é possível fazer com que contatos sem grupos sejam visíveis + por meio desse sinalizador na linha da tabela {@link android.provider.ContactsContract.Settings} para um tipo de conta e uma conta. + Esse sinalizador serve para exibir contatos de servidores que não usam grupos. +
    + "1" - para essa conta e esse tipo de conta, os contatos que não pertencem a um grupo são + visíveis nas IUs do aplicativo. +
    {@link android.provider.ContactsContract.SyncState}(todos) + Use essa tabela para armazenar metadados do seu adaptador de sincronização. + + Com essa tabela, é possível armazenar o estado de sincronização e outros dados relacionados à sincronização persistentes + no dispositivo. +
    +

    Acesso ao Provedor de Contatos

    +

    + Esta seção descreve orientações quanto ao acesso a dados do Provedor de Contatos, com foco + no seguinte: +

    +
      +
    • + Consultas de entidade. +
    • +
    • + Modificação em lote. +
    • +
    • + Recuperação e modificação com intenções. +
    • +
    • + Integridade dos dados. +
    • +
    +

    + A realização de modificações de um adaptador de sincronização também é abordada na seção + Adaptadores de sincronização do Provedor de Contatos. +

    +

    Consultas de entidades

    +

    + A organização hierárquica das tabelas do Provedor de Contatos é muito útil + para recuperar uma linha e todas as linhas "filhas" vinculadas. Por exemplo: para exibir + todas as informações de uma pessoa, você pode querer recuperar todas + as linhas {@link android.provider.ContactsContract.RawContacts} de uma única + linha {@link android.provider.ContactsContract.Contacts} ou todas + as linhas {@link android.provider.ContactsContract.CommonDataKinds.Email} de uma única + linha {@link android.provider.ContactsContract.RawContacts}. Para facilitar isso, o Provedor + de contatos oferece a ideia de entidade, que atua como uma junção dos bancos de dados entre + tabelas. +

    +

    + Entidade é como uma tabela composta de colunas selecionadas de uma tabela pai e uma tabela filha. + Ao consultar uma entidade, fornece-se uma projeção e buscam-se critérios com base nas colunas + disponíveis da entidade. O resultado é um {@link android.database.Cursor} que contém + uma linha para cada linha recuperada da tabela filha. Por exemplo: ao consultar + {@link android.provider.ContactsContract.Contacts.Entity} de um nome de contato + e todas as linhas {@link android.provider.ContactsContract.CommonDataKinds.Email} de todos os + contatos brutos por esse nome, você obterá um {@link android.database.Cursor} contendo uma linha + para cada linha {@link android.provider.ContactsContract.CommonDataKinds.Email}. +

    +

    + As entidades simplificam as consultas. Usando uma entidade, é possível recuperar todos os dados de contatos + de um contato ou contato bruto de uma vez, sem precisar consultar primeiro a tabela pai para obter + um ID e, em seguida, ter que consultar a tabela filha com esse ID. Além disso, o Provedor de Contatos processa + uma consulta com uma entidade em uma transação única, o que garante que os dados recuperados + sejam consistentes internamente. +

    +

    + Observação: uma entidade normalmente não contém todas as colunas da tabela pai e + da tabela filha. Se você tentar trabalhar com um nome de coluna que não esteja na lista de constantes de nomes de coluna + da entidade, será gerada uma {@link java.lang.Exception}. +

    +

    + O fragmento a seguir mostra como recuperar todas as linhas de contato bruto de um contato. O fragmento + é parte de um aplicativo maior que tem duas atividades: "principal" e “de detalhes". A atividade principal + exibe uma lista de linhas de contato. Quando um usuário seleciona uma delas, a atividade envia o ID correspondente à atividade + de detalhes. A atividade de detalhes usa {@link android.provider.ContactsContract.Contacts.Entity} + para exibir todas as linhas de dados de todos os contatos brutos associados ao contato + selecionado. +

    +

    + Esse fragmento é extraído da atividade "de detalhes": +

    +
    +...
    +    /*
    +     * Appends the entity path to the URI. In the case of the Contacts Provider, the
    +     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
    +     */
    +    mContactUri = Uri.withAppendedPath(
    +            mContactUri,
    +            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
    +
    +    // Initializes the loader identified by LOADER_ID.
    +    getLoaderManager().initLoader(
    +            LOADER_ID,  // The identifier of the loader to initialize
    +            null,       // Arguments for the loader (in this case, none)
    +            this);      // The context of the activity
    +
    +    // Creates a new cursor adapter to attach to the list view
    +    mCursorAdapter = new SimpleCursorAdapter(
    +            this,                        // the context of the activity
    +            R.layout.detail_list_item,   // the view item containing the detail widgets
    +            mCursor,                     // the backing cursor
    +            mFromColumns,                // the columns in the cursor that provide the data
    +            mToViews,                    // the views in the view item that display the data
    +            0);                          // flags
    +
    +    // Sets the ListView's backing adapter.
    +    mRawContactList.setAdapter(mCursorAdapter);
    +...
    +@Override
    +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +
    +    /*
    +     * Sets the columns to retrieve.
    +     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
    +     * DATA1 contains the first column in the data row (usually the most important one).
    +     * MIMETYPE indicates the type of data in the data row.
    +     */
    +    String[] projection =
    +        {
    +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
    +            ContactsContract.Contacts.Entity.DATA1,
    +            ContactsContract.Contacts.Entity.MIMETYPE
    +        };
    +
    +    /*
    +     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
    +     * contact collated together.
    +     */
    +    String sortOrder =
    +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
    +            " ASC";
    +
    +    /*
    +     * Returns a new CursorLoader. The arguments are similar to
    +     * ContentResolver.query(), except for the Context argument, which supplies the location of
    +     * the ContentResolver to use.
    +     */
    +    return new CursorLoader(
    +            getApplicationContext(),  // The activity's context
    +            mContactUri,              // The entity content URI for a single contact
    +            projection,               // The columns to retrieve
    +            null,                     // Retrieve all the raw contacts and their data rows.
    +            null,                     //
    +            sortOrder);               // Sort by the raw contact ID.
    +}
    +
    +

    + Quando o carregamento é finalizado, {@link android.app.LoaderManager} invoca um retorno de chamada para + {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) + onLoadFinished()}. Um dos argumentos de entrada nesse método é um + {@link android.database.Cursor} com os resultados da consulta. No seu aplicativo, você pode obter os + dados desse {@link android.database.Cursor} para exibi-los ou trabalhar com eles posteriormente. +

    +

    Modificação em lote

    +

    + Sempre que possível, deve-se inserir, atualizar e excluir dados no Provedor de Contatos + em "modo de lote", criando um {@link java.util.ArrayList} de + objetos {@link android.content.ContentProviderOperation} e chamando + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Como + o Provedor de Contatos realiza todas as operações em um + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} em uma única + transação, as modificações nunca deixarão o repositório de contatos em um estado + inconsistente. As modificações em lote também facilitam a inserção de um contato bruto e seus dados de detalhe + ao mesmo tempo. +

    +

    + Observação: para modificar um contato bruto exclusivo, considere enviar uma intenção ao + aplicativo de contatos do dispositivo em vez de realizar a modificação no aplicativo. + Na seção Recuperação e modificação com intenções há mais detalhes + sobre como fazer isso. +

    +

    Pontos de rendimento

    +

    + As modificações em lote que contiverem muitas operações podem bloquear outros processos, + resultando em uma experiência geral ruim para o usuário. Para organizar todas as modificações + a realizar no menor número de listas separadas possível e, ao mesmo tempo, evitar + o bloqueio do sistema, é preciso definir pontos de rendimento para uma ou mais operações. + Pontos de rendimento são objetos {@link android.content.ContentProviderOperation} que têm seu + valor {@link android.content.ContentProviderOperation#isYieldAllowed()} definido como + true. Quando o Provedor de Contatos encontra um ponto de rendimento, ele pausa o trabalho + para permitir a execução de outros processos e encerra a transação em curso. Quando o provedor retorna, ele + continua na próxima operação da {@link java.util.ArrayList} e inicia + uma nova transação. +

    +

    + Os pontos de rendimento resultam em mais de uma transação por chamada de + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Por isso, + é preciso definir um ponto de rendimento para a última operação de um conjunto de linhas relacionadas. + Por exemplo: é preciso definir um ponto de rendimento para a última operação em um conjunto que adicione + linhas de um contato bruto e linhas de dados associados a ele, ou para a última operação de um conjunto de linhas relacionadas + a um único contato. +

    +

    + Os pontos de rendimento também são uma unidade de operação atômica. Todos os acessos entre dois pontos de rendimento + terão sucesso ou fracasso como uma unidade. Se não houver pontos de rendimento definidos, a menor + operação atômica será todo o lote de operações. Se forem usados pontos de rendimento, eles evitarão + que as operações prejudiquem o desempenho do sistema e, ao mesmo tempo, garantirá que o subconjunto + de operações seja atômico. +

    +

    Referências de retorno da modificação

    +

    + Ao inserir uma nova linha de contato bruto e as linhas de dados associados como um conjunto + de objetos {@link android.content.ContentProviderOperation}, é preciso vincular as linhas de dados + à linha de contato bruto pela inserção do valor + {@code android.provider.BaseColumns#_ID} do contato bruto como + o valor {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Contudo, esse + valor não está disponível ao criar a {@link android.content.ContentProviderOperation} + para a linha de dados porque + {@link android.content.ContentProviderOperation} ainda não foi aplicada à linha de contato bruto. Para trabalhar com isso, + a classe {@link android.content.ContentProviderOperation.Builder} tem o método + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}. + Esse método permite a inserção ou modificação de uma coluna com + o resultado de uma operação anterior. +

    +

    + O método {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + tem dois argumentos: +

    +
    +
    + key +
    +
    + O principal de um par de valores principais. O valor desse argumento deve ser o nome de uma coluna + na tabela que será modificada. +
    +
    + previousResult +
    +
    + O índice com base 0 de um valor em uma matriz + de objetos {@link android.content.ContentProviderResult} +de {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Quando + as operações em lote são aplicadas, o resultado de cada operação é armazenado + em uma matriz intermediária de resultados. O valor previousResult é o índice + de um desses resultados, que é recuperado e armazenado com o valor + key. Isso permite a inserção de um novo registro de contato bruto e a recuperação do seu + valor {@code android.provider.BaseColumns#_ID}, seguido de uma "referência de retorno" ao + valor ao adicionar uma linha {@link android.provider.ContactsContract.Data}. +

    + Toda a matriz de resultados é criada ao chamar + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} pela primeira vez, + com tamanho igual ao da {@link java.util.ArrayList} + de objetos {@link android.content.ContentProviderOperation} fornecidos. No entanto, todos + os elementos na matriz de resultados são definidos como null e, se houver uma tentativa + de referência de retorno a um resultado de uma operação ainda não realizada, +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + gera uma {@link java.lang.Exception}. + +

    +
    +
    +

    + Os fragmentos a seguir mostram como inserir um novo contato bruto e dados em lote. Eles + contém o código que estabelece um ponto de rendimento e usam uma referência de retorno. Os fragmentos são + uma versão expandida do método createContacEntry(), que é parte + da classe ContactAdder + no aplicativo de exemplo de + Contact Manager. +

    +

    + O primeiro fragmento recupera dados de contato da IU. Nesse momento, o usuário já + selecionou a conta na qual o novo contato bruto deve ser adicionado. +

    +
    +// Creates a contact entry from the current UI values, using the currently-selected account.
    +protected void createContactEntry() {
    +    /*
    +     * Gets values from the UI
    +     */
    +    String name = mContactNameEditText.getText().toString();
    +    String phone = mContactPhoneEditText.getText().toString();
    +    String email = mContactEmailEditText.getText().toString();
    +
    +    int phoneType = mContactPhoneTypes.get(
    +            mContactPhoneTypeSpinner.getSelectedItemPosition());
    +
    +    int emailType = mContactEmailTypes.get(
    +            mContactEmailTypeSpinner.getSelectedItemPosition());
    +
    +

    + O próximo fragmento cria uma operação para inserir a linha de contato bruto + na tabela {@link android.provider.ContactsContract.RawContacts}: +

    +
    +    /*
    +     * Prepares the batch operation for inserting a new raw contact and its data. Even if
    +     * the Contacts Provider does not have any data for this person, you can't add a Contact,
    +     * only a raw contact. The Contacts Provider will then add a Contact automatically.
    +     */
    +
    +     // Creates a new array of ContentProviderOperation objects.
    +    ArrayList<ContentProviderOperation> ops =
    +            new ArrayList<ContentProviderOperation>();
    +
    +    /*
    +     * Creates a new raw contact with its account type (server type) and account name
    +     * (user's account). Remember that the display name is not stored in this row, but in a
    +     * StructuredName data row. No other data is required.
    +     */
    +    ContentProviderOperation.Builder op =
    +            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
    +            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
    +            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +

    + Em seguida, o código cria linhas de dados para as linhas de nome de exibição, telefone e e-mail. +

    +

    + Cada objeto construtor de operações usa + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + para obter + o {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Os pontos de referência + voltam ao objeto {@link android.content.ContentProviderResult} a partir da primeira operação, + que adiciona a linha de contato bruto e retorna seu novo valor + {@code android.provider.BaseColumns#_ID}. Como resultado, cada linha de dados é automaticamente vinculada por meio do + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} + à nova linha {@link android.provider.ContactsContract.RawContacts} à qual ela pertence. +

    +

    + O objeto {@link android.content.ContentProviderOperation.Builder} que adiciona a linha de e-mail é + sinalizado com {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) + withYieldAllowed()}, que define um ponto de rendimento: +

    +
    +    // Creates the display name for the new raw contact, as a StructuredName data row.
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * withValueBackReference sets the value of the first argument to the value of
    +             * the ContentProviderResult indexed by the second argument. In this particular
    +             * call, the raw contact ID column of the StructuredName data row is set to the
    +             * value of the result returned by the first operation, which is the one that
    +             * actually adds the raw contact row.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to StructuredName
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
    +
    +            // Sets the data row's display name to the name in the UI.
    +            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +    // Inserts the specified phone number and type as a Phone data row
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * Sets the value of the raw contact id column to the new raw contact ID returned
    +             * by the first operation in the batch.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to Phone
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
    +
    +            // Sets the phone number and type
    +            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
    +            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +    // Inserts the specified email and type as a Phone data row
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * Sets the value of the raw contact id column to the new raw contact ID returned
    +             * by the first operation in the batch.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to Email
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
    +
    +            // Sets the email address and type
    +            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
    +            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
    +
    +    /*
    +     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
    +     * will yield priority to other threads. Use after every set of operations that affect a
    +     * single contact, to avoid degrading performance.
    +     */
    +    op.withYieldAllowed(true);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +

    + O último fragmento exibe a chamada + a {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} que + insere as novas linhas de contato bruto e de dados. +

    +
    +    // Ask the Contacts Provider to create a new contact
    +    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
    +            mSelectedAccount.getType() + ")");
    +    Log.d(TAG,"Creating contact: " + name);
    +
    +    /*
    +     * Applies the array of ContentProviderOperation objects in batch. The results are
    +     * discarded.
    +     */
    +    try {
    +
    +            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    +    } catch (Exception e) {
    +
    +            // Display a warning
    +            Context ctx = getApplicationContext();
    +
    +            CharSequence txt = getString(R.string.contactCreationFailure);
    +            int duration = Toast.LENGTH_SHORT;
    +            Toast toast = Toast.makeText(ctx, txt, duration);
    +            toast.show();
    +
    +            // Log exception
    +            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    +    }
    +}
    +
    +

    + As operações de lote também permitem a implementação de controle otimista de simultaneidade, + um método de aplicar transações de modificação sem precisar bloquear o repositório subjacente. + Para usar esse método, aplique a transação e, em seguida, verifique se há outras modificações que + possam ter sido realizadas ao mesmo tempo. Se ficar determinado que houve uma modificação incoerente, + reverte-se a transação e tenta-se novamente. +

    +

    + O controle otimista de simultaneidade é útil para dispositivos móveis em que haja somente um usuário + de uma vez e que sejam raros os acessos simultâneos a um repositório de dados. Como os bloqueios não são usados, + não há tempo gasto em bloqueios de configuração nem espera para que outras transações liberem os respectivos bloqueios. +

    +

    + Para usar o controle otimista de simultaneidade ao atualizar uma única + linha {@link android.provider.ContactsContract.RawContacts}, siga estas etapas: +

    +
      +
    1. + Recupere a coluna {@link android.provider.ContactsContract.SyncColumns#VERSION} + do contato bruto em conjunto com os outros dados recuperados. +
    2. +
    3. + Crie um objeto {@link android.content.ContentProviderOperation.Builder} adequado para + forçar uma restrição com o método + {@link android.content.ContentProviderOperation#newAssertQuery(Uri)}. Para a URI de conteúdo, + use {@link android.provider.ContactsContract.RawContacts#CONTENT_URI + RawContacts.CONTENT_URI} + com o {@code android.provider.BaseColumns#_ID} do contato bruto anexado a ela. +
    4. +
    5. + Para o objeto {@link android.content.ContentProviderOperation.Builder}, chame + {@link android.content.ContentProviderOperation.Builder#withValue(String, Object) + withValue()} para comparar a coluna + {@link android.provider.ContactsContract.SyncColumns#VERSION} com o número da versão recém-recuperado. +
    6. +
    7. + Para o mesmo {@link android.content.ContentProviderOperation.Builder}, chame + {@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) + withExpectedCount()} para garantir que somente uma linha seja testada por essa instrução. +
    8. +
    9. + Chame {@link android.content.ContentProviderOperation.Builder#build()} para criar + o objeto {@link android.content.ContentProviderOperation}; em seguida, adicione esse objeto como + o primeiro objeto na {@link java.util.ArrayList} passada para + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. +
    10. +
    11. + Aplique a transação em lote. +
    12. +
    +

    + Se a linha de contato bruto for atualizada por outra operação entre o momento da leitura da linha + e o momento de sua modificação, a "assert" {@link android.content.ContentProviderOperation} + falhará e todo o lote de operações será cancelado. Depois disso, as opções são tentar novamente + ou tomar outra medida. +

    +

    + O fragmento a seguir demonstra como criar uma "assert" + {@link android.content.ContentProviderOperation} após consultar um contato bruto exclusivo usando + um {@link android.content.CursorLoader}: +

    +
    +/*
    + * The application uses CursorLoader to query the raw contacts table. The system calls this method
    + * when the load is finished.
    + */
    +public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    +
    +    // Gets the raw contact's _ID and VERSION values
    +    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    +    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
    +}
    +
    +...
    +
    +// Sets up a Uri for the assert operation
    +Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
    +
    +// Creates a builder for the assert operation
    +ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
    +
    +// Adds the assertions to the assert operation: checks the version and count of rows tested
    +assertOp.withValue(SyncColumns.VERSION, mVersion);
    +assertOp.withExpectedCount(1);
    +
    +// Creates an ArrayList to hold the ContentProviderOperation objects
    +ArrayList ops = new ArrayList<ContentProviderOperationg>;
    +
    +ops.add(assertOp.build());
    +
    +// You would add the rest of your batch operations to "ops" here
    +
    +...
    +
    +// Applies the batch. If the assert fails, an Exception is thrown
    +try
    +    {
    +        ContentProviderResult[] results =
    +                getContentResolver().applyBatch(AUTHORITY, ops);
    +
    +    } catch (OperationApplicationException e) {
    +
    +        // Actions you want to take if the assert operation fails go here
    +    }
    +
    +

    Recuperação e modificação com intenções

    +

    + Enviar uma intenção ao aplicativo de contatos do dispositivo permite o acesso indireto ao Provedor + de contatos. A intenção inicia a IU do aplicativo de contatos do dispositivo, na qual os usuários podem + fazer tarefas relacionadas a contatos. Com esse tipo de acesso, os usuários podem: +

      +
    • Selecionar um contato de uma lista e retorná-lo ao aplicativo para trabalhos futuros.
    • +
    • Editar dados de um contato existente.
    • +
    • Inserir um novo contato bruto para quaisquer das suas contas.
    • +
    • Excluir um contato ou dados dos contatos.
    • +
    +

    + Se o usuário estiver inserindo ou atualizando dados, é possível coletar os dados antes e enviá-los como + parte da intenção. +

    +

    + Ao usar intenções para acessar o Provedor de Contatos por meio do aplicativo de contatos do dispositivo, + não é necessário criar a própria IU ou código para acessar o provedor. Também não é necessário + solicitar permissão de leitura e gravação ao provedor. O aplicativo de contatos do dispositivo pode + delegar a você permissões de leitura de um contato e, pelo fato de você fazer modificações + no provedor por meio de outro aplicativo, não é necessário ter permissões de gravação. +

    +

    + O processo geral de envio de uma intenção para acessar um provedor é descrito detalhadamente + no guia +Preceitos do provedor de conteúdo, seção "Acesso a dados via intenções". Os valores + de ação, do tipo MIME e dos dados usados para as tarefas disponíveis são resumidos na tabela 4 + e os valores extras que podem ser usados com + {@link android.content.Intent#putExtra(String, String) putExtra()} são listados na + documentação de referência de {@link android.provider.ContactsContract.Intents.Insert}: +

    +

    + Tabela 4. Intenções do Provedor de Contatos +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    TarefaAçãoDadosTipo MIMEObservações
    Selecionar um contato de uma lista{@link android.content.Intent#ACTION_PICK} + Um dos seguintes: +
      +
    • +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}, + que exibe uma lista de contatos. +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI}, + que exibe uma lista de números de telefone de um contato bruto. +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI}, + que exibe uma lista de endereços postais de um contato bruto. +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}, + que exibe uma lista de endereços de e-mail de um contato bruto. +
    • +
    +
    + Não usado + + Exibe uma lista de contatos brutos ou uma lista de dados de um contato bruto conforme + o tipo da URI de conteúdo fornecido. +

    + Chame + {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}, + que retorna a URI de conteúdo da linha selecionada. O formulário da URI é a + URI de conteúdo da tabela com o LOOKUP_ID da linha anexado a ela. + O aplicativo de contatos do dispositivo delega permissões de leitura e gravação a essa URI de conteúdo + pelo tempo que a atividade durar. Consulte + o guia + Preceitos do provedor de conteúdo para obter mais detalhes. +

    +
    Inserir um novo contato bruto{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}Não aplicável + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE + RawContacts.CONTENT_TYPE}, tipo MIME para um grupo de contatos brutos. + + Exibe a tela Adicionar contato do aplicativo de contatos do dispositivo. + São exibidos os valores extras adicionados à intenção. Se enviada com + {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}, + a URI de conteúdo do contato bruto recentemente adicionado é passada de volta + ao método de retorno de chamada + {@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()}no argumento {@link android.content.Intent}, +no campo "dados". Para obter o valor, chame {@link android.content.Intent#getData()}. +
    Editar um contato{@link android.content.Intent#ACTION_EDIT} + {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} + do contato. A atividade do editor permitirá que o usuário edite os dados associados + a esse contato. + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE + Contacts.CONTENT_ITEM_TYPE}, um único contato. + Exibe a tela Editar contato no aplicativo de contatos. São exibidos os valores extras + adicionados à intenção. Quando o usuário clica em Concluído para salvar + as edições, a atividade retorna ao primeiro plano. +
    Exibir um seletor que também pode adicionar dados.{@link android.content.Intent#ACTION_INSERT_OR_EDIT} + Não aplicável + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + + Essa intenção sempre exibe a tela do seletor do aplicativo de contatos. O usuário pode + selecionar um contato para editar ou adicionar um novo. É exibida a tela de edição ou de adição, + conforme a escolha do usuário e são exibidos os dados extras passados + para a intenção. Se o aplicativo exibe dados de contato como um e-mail ou número de telefone, use + essa intenção para permitir que o usuário adicione os dados a um contato existente. + +

    + Observação: Não é necessário enviar nenhum valor de nome dos extras da intenção + porque o usuário sempre seleciona um nome existente ou adiciona um novo. Além disso, + se você enviar um nome e o usuário escolher editar, o aplicativo de contatos + exibirá o nome enviado, substituindo o valor anterior. Se o usuário + não notar isso e salvar a edição, o valor antigo será perdido. +

    +
    +

    + O aplicativo de contatos do dispositivo não permite a exclusão de um contato bruto ou de seus dados + com uma intenção. Em vez disso, para excluir um contato bruto, use + {@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} + ou {@link android.content.ContentProviderOperation#newDelete(Uri) + ContentProviderOperation.newDelete()}. +

    +

    + O fragmento a seguir mostra como construir e enviar uma intenção que insira um novo + contato bruto e dados: +

    +
    +// Gets values from the UI
    +String name = mContactNameEditText.getText().toString();
    +String phone = mContactPhoneEditText.getText().toString();
    +String email = mContactEmailEditText.getText().toString();
    +
    +String company = mCompanyName.getText().toString();
    +String jobtitle = mJobTitle.getText().toString();
    +
    +// Creates a new intent for sending to the device's contacts application
    +Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
    +
    +// Sets the MIME type to the one expected by the insertion activity
    +insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
    +
    +// Sets the new contact name
    +insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
    +
    +// Sets the new company and job title
    +insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
    +insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
    +
    +/*
    + * Demonstrates adding data rows as an array list associated with the DATA key
    + */
    +
    +// Defines an array list to contain the ContentValues objects for each row
    +ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
    +
    +
    +/*
    + * Defines the raw contact row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues rawContactRow = new ContentValues();
    +
    +// Adds the account type and name to the row
    +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
    +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
    +
    +// Adds the row to the array
    +contactData.add(rawContactRow);
    +
    +/*
    + * Sets up the phone number data row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues phoneRow = new ContentValues();
    +
    +// Specifies the MIME type for this data row (all data rows must be marked by their type)
    +phoneRow.put(
    +        ContactsContract.Data.MIMETYPE,
    +        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
    +);
    +
    +// Adds the phone number and its type to the row
    +phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
    +
    +// Adds the row to the array
    +contactData.add(phoneRow);
    +
    +/*
    + * Sets up the email data row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues emailRow = new ContentValues();
    +
    +// Specifies the MIME type for this data row (all data rows must be marked by their type)
    +emailRow.put(
    +        ContactsContract.Data.MIMETYPE,
    +        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
    +);
    +
    +// Adds the email address and its type to the row
    +emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
    +
    +// Adds the row to the array
    +contactData.add(emailRow);
    +
    +/*
    + * Adds the array to the intent's extras. It must be a parcelable object in order to
    + * travel between processes. The device's contacts app expects its key to be
    + * Intents.Insert.DATA
    + */
    +insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
    +
    +// Send out the intent to start the device's contacts app in its add contact activity.
    +startActivity(insertIntent);
    +
    +

    Integridade dos dados

    +

    + Como o repositório de contatos contém dados importantes e confidenciais que usuários esperam que estejam + corretos e atualizados, o Provedor de Contatos tem regras bem definidas para a integridade dos dados. É de + sua responsabilidade adequar-se a essas regras ao modificar dados de contatos. As regras + importantes são listadas a seguir: +

    +
    +
    + Sempre adicione uma linha {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} + para cada linha {@link android.provider.ContactsContract.RawContacts} adicionada. +
    +
    + Uma linha {@link android.provider.ContactsContract.RawContacts} sem uma + linha {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} + na tabela {@link android.provider.ContactsContract.Data} pode causar problemas durante + a agregação. +
    +
    + Sempre conecte novas linhas {@link android.provider.ContactsContract.Data} às respectivas + linhas pai {@link android.provider.ContactsContract.RawContacts}. +
    +
    + Uma linha {@link android.provider.ContactsContract.Data} que não esteja conectada a um + {@link android.provider.ContactsContract.RawContacts} não será visível no aplicativo + de contatos do dispositivo, o que pode causar problemas com adaptadores de sincronização. +
    +
    + Altere dados somente para os seus contatos brutos. +
    +
    + Lembre-se de que o Provedor de Contatos normalmente gerencia dados de diversos tipos de conta + e serviços on-line diferentes. É preciso garantir que o aplicativo modifique + ou exclua somente dados de linhas que pertencem a você e que ele insira dados apenas + com um tipo e nome de conta que você controla. +
    +
    + Sempre use as constantes definidas em {@link android.provider.ContactsContract} e suas + subclasses de autoridades, URIs de conteúdo, caminhos de URI, nomes de coluna, tipos MIME e + valores {@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE}. +
    +
    + O uso dessas constantes ajuda a evitar erros. Você também será notificado com avisos + do compilador se uma das constantes não for aprovada. +
    +
    +

    Linhas de dados personalizados

    +

    + Ao criar e usar seus próprios tipos MIME personalizados, você pode inserir, editar, excluir e recuperar + suas linhas de dados na tabela {@link android.provider.ContactsContract.Data}. As linhas + são limitadas a usar a coluna definida em + {@link android.provider.ContactsContract.DataColumns}, embora seja possível mapear + os nomes de coluna de tipo específico para os nomes de coluna padrão. No aplicativo de contatos do dispositivo, + os dados das linhas são exibidos, mas não podem ser editados nem excluídos e os usuários não podem inserir + dados adicionais. Para permitir que usuários modifiquem suas linhas de dados personalizados, é necessário fornecer + uma atividade do editor no próprio aplicativo. +

    +

    + Para exibir os dados personalizados, forneça um arquivo contacts.xml contendo + um elemento <ContactsAccountType> ou um ou mais + dos elementos filho <ContactsDataKind>. Isso é abordado com mais detalhes na + seção <ContactsDataKind> element. +

    +

    + Para saber mais sobre tipos MIME personalizados, leia o + guia + Criação de um provedor de conteúdo. +

    +

    Adaptadores de sincronização do Provedor de Contatos

    +

    + O Provedor de Contatos foi projetado especificamente para tratar a sincronização + de dados de contatos entre um dispositivo e um serviço on-line. Isso permite que usuários baixem + dados existentes em um novo dispositivo e transfiram dados existentes a uma nova conta. + A sincronização também garante que usuários tenham os dados mais recentes à mão, independentemente + da origem de adições e alterações. Outra vantagem da sincronização é + a disponibilidade dos dados dos contatos mesmo quando o dispositivo não está conectado à rede. +

    +

    + Embora seja possível implementar a sincronização de diversos modos, o sistema Android fornece + uma estrutura de sincronização de extensão que automatiza as seguintes tarefas: +

      + +
    • + Verificação da disponibilidade da rede. +
    • +
    • + Agendamento e execução de sincronizações, com base nas preferências do usuário. +
    • +
    • + Reinicialização de sincronizações que foram interrompidas. +
    • +
    +

    + Para usar essa estrutura, deve-se fornecer uma extensão do adaptador de sincronização. Cada adaptador de sincronização é exclusivo + de um serviço e um provedor de conteúdo, mas pode tratar diversos nomes de conta do mesmo serviço. + A estrutura também permite diversos adaptadores de sincronização para o mesmo serviço e provedor. +

    +

    Classes e arquivos do adaptador de sincronização

    +

    + O adaptador de sincronização é implementado como uma subclasse + de {@link android.content.AbstractThreadedSyncAdapter} e instalado como parte do aplicativo + do Android. O sistema coleta dados do adaptador de sincronização a partir de elementos no manifesto + do aplicativo e de um arquivo XML especial direcionado pelo manifesto. O arquivo XML define + o tipo de conta do serviço on-line e a autoridade do provedor de conteúdo que, juntos, + identificam exclusivamente o adaptador. O adaptador de sincronização não fica ativo até que o usuário adicione + uma conta para o tipo de conta do adaptador de sincronização e habilite a sincronização para + o provedor de conteúdo com o qual o adaptador se sincroniza. Nesse ponto, o sistema começa a gerenciar o adaptador, + chamando-o quando necessário para estabelecer a sincronização entre o provedor de conteúdo e o servidor. +

    +

    + Observação: usar um tipo de conta como parte da identificação do adaptador de sincronização permite + que o sistema detecte e agrupe adaptadores de sincronização que acessam diferentes serviços + da mesma organização. Por exemplo, todos os adaptadores de sincronização dos serviços on-line da Google têm o mesmo + tipo de conta com.google. Quando o usuário adiciona uma conta Google aos dispositivos, todos + os adaptadores de sincronização dos serviços Google instalados são listados juntos; cada um + dos listados sincroniza-se com um provedor de conteúdo diferente no dispositivo. +

    +

    + Como a maioria dos serviços exige que usuários verifiquem a identidade antes de acessar + os dados, o sistema Android oferece uma estrutura de autenticação similar à estrutura + dos adaptadores de sincronização e muitas vezes usada junto com eles. A estrutura de autenticação usa + autenticadores de extensão que são subclasses + de {@link android.accounts.AbstractAccountAuthenticator}. Um autenticador verifica + a identidade do usuário nas seguintes etapas: +

      +
    1. + Coleta nome, senha ou informação similar do usuário (as credenciais + do usuário). +
    2. +
    3. + Envia as credenciais para o serviço +
    4. +
    5. + Avalia a resposta do serviço. +
    6. +
    +

    + Se o serviço aceitar as credenciais, o autenticador pode + armazená-las para uso futuro. Devido à estrutura do autenticador de extensão, + o {@link android.accounts.AccountManager} pode conferir acesso a quaisquer tokens de autenticação compatíveis com + um autenticador que escolha expô-los, como os tokens de autenticação OAuth2. +

    +

    + Embora as autenticações não sejam necessárias, a maioria dos serviços de contato as usam. + Contudo, não é obrigatório usar a estrutura de autenticação do Android para efetuá-las. +

    +

    Implementação do adaptador de sincronização

    +

    + Para implementar um adaptador de sincronização para o Provedor de Contatos, primeiramente é necessário criar + um aplicativo do Android que contenha o seguinte: +

    +
    +
    + Um componente {@link android.app.Service} que responda a solicitações do sistema para + vincular ao adaptador de sincronização. +
    +
    + Quando o sistema quer executar uma sincronização, ele chama o método + {@link android.app.Service#onBind(Intent) onBind()} do serviço para obter + um {@link android.os.IBinder} para o adaptador de sincronização. Isso permite que o sistema efetue chamadas + entre processos aos métodos do adaptador. +

    + No aplicativo + Exemplo de adaptador de sincronização, o nome de classe desse serviço é + com.example.android.samplesync.syncadapter.SyncService. +

    +
    +
    + O adaptador de sincronização atual, implementado como uma subclasse concreta de + {@link android.content.AbstractThreadedSyncAdapter}. +
    +
    + Essa classe realiza o trabalho de baixar dados do servidor, atualizar dados + do dispositivo e resolver conflitos. A principal tarefa do adaptador é + feita no método {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( + Account, Bundle, String, ContentProviderClient, SyncResult) + onPerformSync()}. Essa classe deve ser instanciada como um singleton. +

    + No aplicativo + Exemplo de adaptador de sincronização, o adaptador de sincronização é definido na classe + com.example.android.samplesync.syncadapter.SyncAdapter. +

    +
    +
    + Uma subclasse de {@link android.app.Application}. +
    +
    + Essa classe atua como uma fábrica para o singleton do adaptador de sincronização. Use + o método {@link android.app.Application#onCreate()} para instanciar o adaptador de sincronização + e fornecer um método "coletor" estático para retornar o singleton ao + método {@link android.app.Service#onBind(Intent) onBind()} do serviço do adaptador + de sincronização. +
    +
    + Opcional: um componente {@link android.app.Service} que responda a + solicitações do sistema para autenticação do usuário. +
    +
    + O {@link android.accounts.AccountManager} ativa esse serviço para iniciar o processo + de autenticação. O método {@link android.app.Service#onCreate()} do serviço instancia + um objeto autenticador. Quando o sistema quer autenticar uma conta de usuário para + o adaptador de sincronização do aplicativo, ele chama o método + {@link android.app.Service#onBind(Intent) onBind()} do serviço para obter um + {@link android.os.IBinder} para o autenticador. Isso permite que o sistema efetue chamadas + entre processos aos métodos do autenticador. +

    + No aplicativo + Exemplo de adaptador de sincronização, o nome de classe desse serviço é + com.example.android.samplesync.authenticator.AuthenticationService. +

    +
    +
    + Opcional: uma subclasse concreta de + {@link android.accounts.AbstractAccountAuthenticator} que trate de solicitações de + autenticação. +
    +
    + Essa classe fornece métodos que chamam {@link android.accounts.AccountManager} + para autenticar as credenciais do usuário no servidor. Os detalhes + desse processo de autenticação variam amplamente, com base na tecnologia em uso no servidor. Consulte + a documentação do software do servidor para ver mais informações sobre autenticação. +

    + No aplicativo + Exemplo de adaptador de sincronização, o autenticador é definido na classe + com.example.android.samplesync.authenticator.Authenticator. +

    +
    +
    + Os arquivos XML que definem o adaptador de sincronização e o autenticador para o sistema. +
    +
    + Os componentes de serviço do adaptador de sincronização e do autenticador descritos anteriormente são + definidos em +elementos <service> + no manifesto do aplicativo. Esses elementos + contêm +elementos filho <meta-data> +que fornecem dados específicos ao + sistema: +
      +
    • + O +elemento <meta-data> + de serviço do adaptador de sincronização direciona-se + ao res/xml/syncadapter.xml do arquivo XML. Em troca, esse arquivo especifica + uma URI para o serviço web que será sincronizado com o Provedor de Contatos + e um tipo de conta. +
    • +
    • + Opcional: o +elemento <meta-data> + do autenticador aponta para o arquivo XML + res/xml/authenticator.xml. Em troca, esse arquivo especifica + o tipo de conta compatível com esse autenticador, bem como recursos de IU + que aparecem durante o processo de autenticação. O tipo de conta especificado + nesse elemento deve ser o mesmo que o especificado para + o adaptador de sincronização. +
    • +
    +
    +
    +

    Dados de fluxos sociais

    +

    + As tabelas {@code android.provider.ContactsContract.StreamItems} + e {@code android.provider.ContactsContract.StreamItemPhotos} + gerenciam dados advindos de redes sociais. É possível criar um adaptador de sincronização que adicione dados de fluxo + da sua própria rede a essas tabelas ou ler dados de fluxo dessas tabelas + e exibi-los no seu aplicativo, ou ambos. Com esses recursos, os serviços e aplicativos + de redes sociais podem ser integrados na experiência das redes sociais no Android. +

    +

    Textos de fluxos sociais

    +

    + Itens de fluxo sempre são associados a um contato bruto. + O {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} conecta-se ao + valor _ID do contato bruto. O tipo e o nome da conta do contato + bruto também são armazenados na linha do item de fluxo. +

    +

    + Armazene os dados do seu fluxo nas colunas a seguir: +

    +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} +
    +
    + Obrigatório. O tipo de conta do usuário do contato bruto associado a esse + item de fluxo. Lembre-se de definir esse valor ao inserir um item de fluxo. +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} +
    +
    + Obrigatório. O nome de conta do usuário do contato bruto associado + a esse item de fluxo. Lembre-se de definir esse valor ao inserir um item de fluxo. +
    +
    + Colunas identificadoras +
    +
    + Obrigatório. É necessário inserir as colunas identificadoras a seguir + ao inserir um item de fluxo: +
      +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}: + o valor {@code android.provider.BaseColumns#_ID} do contato ao qual esse item + de fluxo está associado. +
    • +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}: + o valor{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} do + contato ao qual esse item de fluxo está associado. +
    • +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}: + o valor {@code android.provider.BaseColumns#_ID} do contato bruto ao qual esse item + de fluxo está associado. +
    • +
    +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} +
    +
    + Opcional. Armazena informações resumidas que podem ser exibidas no início do item de fluxo. +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} +
    +
    + O texto do item de fluxo, o conteúdo que foi publicado pela fonte do item + ou uma descrição de alguma ação que gerou o item de fluxo. Essa coluna pode conter + imagens de recurso incorporadas e de qualquer formato que possam ser renderizadas + {@link android.text.Html#fromHtml(String) fromHtml()}. O provedor pode truncar + ou abreviar conteúdos longos, mas ele tentará evitar quebrar as tags. +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} +
    +
    + Uma string de texto contendo o tempo em que o item de fluxo foi inserido ou atualizado, em forma + de milissegundos desde a época. Aplicativos que inserem ou atualizam itens de fluxo são + responsáveis por manter essa coluna, ela não é mantida automaticamente pelo + Provedor de Contatos. +
    +
    +

    + Para exibir informações de identificação para os itens de fluxo, use + {@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}, + {@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} e + {@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} para vincular a recursos + no aplicativo. +

    +

    + A tabela {@code android.provider.ContactsContract.StreamItems} também contém as colunas + {@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} por meio de + {@code android.provider.ContactsContract.StreamItemsColumns#SYNC4} para uso exclusivo + dos adaptadores de sincronização. +

    +

    Fotos de fluxos sociais

    +

    + A tabela {@code android.provider.ContactsContract.StreamItemPhotos} armazena fotos associadas + a um item de fluxo. A coluna + {@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} da tabela + vincula-se a valores na coluna {@code android.provider.BaseColumns#_ID} + da tabela {@code android.provider.ContactsContract.StreamItems}. Referências de fotografias são armazenadas + na tabela nessas colunas: +

    +
    +
    + Coluna {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} (um BLOB). +
    +
    + Representação binária da foto, redimensionada pelo provedor para armazenar e exibir. + Essa coluna está disponível para compatibilidade retroativa com versões anteriores do Provedor + de Contatos que a usavam para armazenar fotos. Contudo, na versão atual, + não se deve usar essa coluna para armazenar fotos. Em vez disso, use + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} ou + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (ambas são + descritas nos pontos a seguir) para armazenar fotos em um arquivo. Essa coluna + passa a conter uma miniatura da foto, que estará disponível para leitura. +
    +
    + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} +
    +
    + Identificador numérico de uma foto de um contato bruto. Anexe esse valor à constante + {@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI} + para obter uma URI de conteúdo direcionada para um único arquivo de foto e, em seguida, chame + {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) + openAssetFileDescriptor()} para obter um identificador para o arquivo de foto. +
    +
    + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} +
    +
    + URI de conteúdo direcionada diretamente para o arquivo de foto da foto representada por essa linha. + Chame {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) + openAssetFileDescriptor()} com essa URI para obter um identificador para o arquivo de foto. +
    +
    +

    Uso de tabelas de fluxos sociais

    +

    + Essas tabelas funcionam como as outras tabelas principais do Provedor de Contatos, exceto que: +

    +
      +
    • + Exigem permissões de acesso adicionais. Para ler o conteúdo delas, o aplicativo + deve ter a permissão {@code android.Manifest.permission#READ_SOCIAL_STREAM}. Para + modificá-las, o aplicativo precisa ter a permissão + {@code android.Manifest.permission#WRITE_SOCIAL_STREAM}. +
    • +
    • + Para a tabela {@code android.provider.ContactsContract.StreamItems}, o número de linhas + armazenadas para cada contato bruto é limitado. Ao atingir o limite, + o Provedor de Contatos abre espaço para novas linhas de itens de fluxo excluindo automaticamente + as linhas que têm + o {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} mais antigo. Para conhecer + o limite, faça uma consulta à URI de conteúdo + {@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}. É possível deixar + todos os argumentos, exceto a URI de conteúdo, definidos como null. A consulta + retorna um Cursor contendo uma linha única com a coluna única + {@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}. +
    • +
    + +

    + A classe {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} define + uma subtabela de {@code android.provider.ContactsContract.StreamItemPhotos} que contém as linhas + de foto de um único item de fluxo. +

    +

    Interações de fluxos sociais

    +

    + Os dados de fluxos sociais gerenciados pelo Provedor de Contatos, em conjunto com o + aplicativo de contatos do dispositivo, oferecem um método poderoso de conexão ao sistema de redes sociais + com contatos existentes. Os seguintes recursos estão disponíveis: +

    +
      +
    • + Ao sincronizar seu serviço de rede social com o Provedor de Contatos com um adaptador + de sincronização, é possível recuperar atividades recentes de contatos de um usuário e armazená-las + nas tabelas {@code android.provider.ContactsContract.StreamItems} + e {@code android.provider.ContactsContract.StreamItemPhotos} para uso posterior. +
    • +
    • + Além da sincronização regular, é possível ativar o adaptador de sincronização para recuperar + dados adicionais quando o usuário seleciona um contato para exibir. Isso permite que o adaptador de sincronização + recupere fotos de alta resolução e os itens de fluxo mais recentes do contato. +
    • +
    • + Ao registrar uma notificação com o aplicativo de contatos do dispositivo e o Provedor + de Contatos, é possível recuperar uma intenção quando um contato é exibido e, nesse ponto, + atualizar o status do contato pelo serviço. Essa abordagem pode ser mais rápida e usar menos + largura de banda do que realizar uma sincronização completa com um adaptador de sincronização. +
    • +
    • + Os usuários podem adicionar um contato ao seu serviço de rede social enquanto exibem-no + no aplicativo de contatos do dispositivo. É possível ativar isso com o recurso "convidar contato", + que é ativado com uma combinação de uma atividade que adiciona um contato existente à sua + rede, um arquivo XML que fornece o aplicativo de contatos do dispositivo + e o Provedor de Contatos com os detalhes do aplicativo. +
    • +
    +

    + A sincronização regular de itens de fluxo com o Provedor de Contatos é igual + a outras sincronizações. Para saber mais sobre sincronizações, consulte a seção + Adaptadores de sincronização do Provedor de Contatos. O registro de notificações + e o convite a contatos são abordados nas próximas duas seções. +

    +

    Registro para manipular exibições de redes sociais

    +

    + Para registrar o adaptador de sincronização para receber notificações quando o usuário exibe um contato que é + gerenciado pelo adaptador de sincronização: +

    +
      +
    1. + Crie um arquivo chamado contacts.xml no diretório res/xml/ + do projeto. Se você já tiver esse arquivo, pule esta etapa. +
    2. +
    3. + Nesse arquivo, adicione o elemento +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Se esse elemento já existir, pule esta etapa. +
    4. +
    5. + Para registrar um serviço que seja notificado quando o usuário abre uma página de detalhes do contato + no aplicativo de contatos do dispositivo, adicione o atributo + viewContactNotifyService="serviceclass" ao elemento, em que + serviceclass é o nome de classe totalmente qualificado do serviço + que deve receber a intenção do aplicativo de contatos do dispositivo. Para o serviço + de notificação, use a classe que estende {@link android.app.IntentService} para permitir que o serviço + receba intenções. Os dados nas intenções recebidas contêm a URI de conteúdo do contato + bruto em que o usuário clicou. No serviço de notificação, é possível vincular e chamar + o adaptador de sincronização para atualizar os dados do contato bruto. +
    6. +
    +

    + Para registrar uma atividade a ser chamada quando o usuário clica em um item de fluxo ou foto, ou ambos: +

    +
      +
    1. + Crie um arquivo chamado contacts.xml no diretório res/xml/ + do projeto. Se você já tiver esse arquivo, pule esta etapa. +
    2. +
    3. + Nesse arquivo, adicione o elemento +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Se esse elemento já existir, pule esta etapa. +
    4. +
    5. + Para registrar uma das atividades para tratar do usuário que clica em um item de fluxo + no aplicativo de contatos do dispositivo, adicione o atributo + viewStreamItemActivity="activityclass" ao elemento, em que + activityclass é o nome de classe totalmente qualificado da atividade + que deve receber a intenção do aplicativo de contatos do dispositivo. +
    6. +
    7. + Para registrar uma das atividades para tratar do usuário que clica em uma foto de fluxo + no aplicativo de contatos do dispositivo, adicione o atributo + viewStreamItemPhotoActivity="activityclass" ao elemento, em que + activityclass é o nome de classe totalmente qualificado da atividade + que deve receber a intenção do aplicativo de contatos do dispositivo. +
    8. +
    +

    + O elemento <ContactsAccountType> é descrito em mais detalhes + na seção Elemento <ContactsAccountType>. +

    +

    + A intenção recebida contém a URI de conteúdo do item ou foto em que o usuário clicou. + Para ter atividades separadas para itens de texto e para fotos, use os dois atributos no mesmo arquivo. +

    +

    Interação com o serviço de redes sociais

    +

    + Os usuários não precisam sair do aplicativo de contatos do dispositivo para convidar um contato para + seu site de contatos. Em vez disso, o aplicativo de contatos do dispositivo pode enviar uma intenção de convidar + o contato para uma das atividades. Para configurar isso: +

    +
      +
    1. + Crie um arquivo chamado contacts.xml no diretório res/xml/ + do projeto. Se você já tiver esse arquivo, pule esta etapa. +
    2. +
    3. + Nesse arquivo, adicione o elemento +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Se esse elemento já existir, pule esta etapa. +
    4. +
    5. + Adicione os seguintes atributos: +
        +
      • inviteContactActivity="activityclass"
      • +
      • + inviteContactActionLabel="@string/invite_action_label" +
      • +
      + O valor activityclass é o nome de classe totalmente qualificado + da atividade que deve receber a intenção. O valor invite_action_label + é uma string de texto exibida no menu Adicionar conexão + no aplicativo de contatos do dispositivo. +
    6. +
    +

    + Observação: ContactsSource é um nome de tag reprovado para + ContactsAccountType. +

    +

    Referência do contacts.xml

    +

    + O arquivo contacts.xml contém elementos XML que controlam a interação + do adaptador de sincronização e o aplicativo com o aplicativo de contatos e o Provedor de Contatos. Esses + elementos são descritos nas seções a seguir. +

    +

    Elemento <ContactsAccountType>

    +

    + O elemento <ContactsAccountType> controla a interação + do aplicativo com o aplicativo de contatos. Ele tem a seguinte sintaxe: +

    +
    +<ContactsAccountType
    +        xmlns:android="http://schemas.android.com/apk/res/android"
    +        inviteContactActivity="activity_name"
    +        inviteContactActionLabel="invite_command_text"
    +        viewContactNotifyService="view_notify_service"
    +        viewGroupActivity="group_view_activity"
    +        viewGroupActionLabel="group_action_text"
    +        viewStreamItemActivity="viewstream_activity_name"
    +        viewStreamItemPhotoActivity="viewphotostream_activity_name">
    +
    +

    + contido em: +

    +

    + res/xml/contacts.xml +

    +

    + pode conter: +

    +

    + <ContactsDataKind> +

    +

    + Descrição: +

    +

    + Declara componentes e etiquetas da IU do Android que permitem aos usuários convidar um dos seus contatos a + uma rede social, notificar usuários quando um dos seus fluxos de redes sociais é atualizado + etc. +

    +

    + Observe que o prefixo android: do atributo não é necessário para os atributos + de <ContactsAccountType>. +

    +

    + Atributos: +

    +
    +
    {@code inviteContactActivity}
    +
    + O nome de classe totalmente qualificado da atividade no aplicativo que você deseja + ativar quando o usuário seleciona Adicionar conexão no aplicativo + de contatos do dispositivo. +
    +
    {@code inviteContactActionLabel}
    +
    + Uma string de texto exibida para a atividade especificada + em {@code inviteContactActivity}, no menu Adicionar conexão. + Por exemplo: você pode usar a string "Siga-me na rede". Você pode usar um identificador + do recurso da string nessa etiqueta. +
    +
    {@code viewContactNotifyService}
    +
    + O nome de classe totalmente qualificado de um serviço no aplicativo que deve receber + notificações de quando o usuário exibe um contato. Essa notificação é enviada pelo aplicativo + de contatos do dispositivo, isso permite que o aplicativo adie operações de dados intensivos + até que elas sejam necessárias. Por exemplo: o aplicativo pode responder a essa notificação + lendo e exibindo a foto de alta resolução do contato e os itens de fluxo social + mais recentes. Esse recurso é descrito com mais detalhes na seção + Interações de fluxos sociais. É possível ver + um exemplo de serviço de notificação no arquivo NotifierService.java + no aplicativo de exemplo + SampleSyncAdapter. +
    +
    {@code viewGroupActivity}
    +
    + O nome de classe totalmente qualificado de uma atividade no aplicativo que pode exibir + informações de grupo. Quando o usuário clica na etiqueta do grupo no aplicativo + de contatos do dispositivo, a IU dessa atividade é exibida. +
    +
    {@code viewGroupActionLabel}
    +
    + A etiqueta que o aplicativo de contatos exibe para um controle de IU que permite + que o usuário veja grupos no seu aplicativo. +

    + Por exemplo: se você instalar o aplicativo do Google+ no dispositivo e sincronizá-lo + com o aplicativo de contatos, verá os círculos do Google+ listados como grupos + na aba Grupos do aplicativo de contatos. Ao clicar + em um círculo do Google+, você verá pessoas nesse círculo listadas como um "grupo". Na parte superior + da tela, você verá um ícone do Google+; ao clicar nele, o controle muda para + o aplicativo do Google+. O aplicativo de contatos faz isso com + {@code viewGroupActivity} usando o ícone como o valor + de {@code viewGroupActionLabel}. +

    +

    + Um identificador de recurso de string é permitido para esse atributo. +

    +
    +
    {@code viewStreamItemActivity}
    +
    + O nome de classe totalmente qualificado de uma atividade no aplicativo que + o aplicativo de contatos do dispositivo executa quando o usuário clica em um item de fluxo de um contato bruto. +
    +
    {@code viewStreamItemPhotoActivity}
    +
    + O nome de classe totalmente qualificado de uma atividade no aplicativo que + o aplicativo de contatos do dispositivo executa quando o usuário clica em uma foto no item de fluxo + de um contato bruto. +
    +
    +

    Elemento <ContactsDataKind>

    +

    + O elemento <ContactsDataKind> controla a tela das linhas de dados + personalizados do aplicativo na IU do aplicativo de contatos. Ele tem a seguinte sintaxe: +

    +
    +<ContactsDataKind
    +        android:mimeType="MIMEtype"
    +        android:icon="icon_resources"
    +        android:summaryColumn="column_name"
    +        android:detailColumn="column_name">
    +
    +

    + contido em: +

    +<ContactsAccountType> +

    + Descrição: +

    +

    + Use esse elemento para que o aplicativo de contatos exiba o conteúdo de uma linha de dados personalizados como + parte dos detalhes de um contato bruto. Cada elemento filho <ContactsDataKind> + de <ContactsAccountType> representa um tipo de linha de dados personalizados que o adaptador + de sincronização adiciona à tabela {@link android.provider.ContactsContract.Data}. Adicione + um elemento <ContactsDataKind> para cada tipo MIME personalizado usado. Não será necessário + adicionar o elemento se você tiver uma linha de dados personalizados para a qual não deseja exibir dados. +

    +

    + Atributos: +

    +
    +
    {@code android:mimeType}
    +
    + O tipo MIME personalizado definido para um dos tipos de linha de dados personalizados + na tabela {@link android.provider.ContactsContract.Data}. Por exemplo: o valor + vnd.android.cursor.item/vnd.example.locationstatus poderia ser um tipo MIME + personalizado para uma linha de dados que registra a última localização conhecida do contato. +
    +
    {@code android:icon}
    +
    + + Recurso desenhável do Android + que o aplicativo de contatos exibe próximo aos dados. Use isso para indicar + ao usuário que os dados são advindos do seu serviço. +
    +
    {@code android:summaryColumn}
    +
    + O nome de coluna do primeiro de dois valores recuperados da linha de dados. + O valor é exibido como a primeira linha da entrada para essa linha de dados. A primeira linha + destina-se ao uso como um resumo dos dados, mas isso é opcional. Veja também + android:detailColumn. +
    +
    {@code android:detailColumn}
    +
    + O nome de coluna do segundo de dois valores recuperados da linha de dados. O valor é + exibido como a segunda linha da entrada dessa linha de dados. Veja também + {@code android:summaryColumn}. +
    +
    +

    Recursos adicionais do Provedor de Contatos

    +

    + Além dos principais recursos descritos nas seções anteriores, o Provedor de Contatos oferece + estes recursos úteis para trabalhar com dados de contatos: +

    +
      +
    • Grupos de contatos
    • +
    • Recursos de foto
    • +
    +

    Grupos de contatos

    +

    + O Provedor de Contatos pode, opcionalmente, etiquetar grupos de contatos relacionados com + dados de grupo. Se o servidor associado a uma conta de usuário + deseja manter grupos, o adaptador de sincronização para o tipo da conta deve transferir + dados de grupo entre o Provedor de Contatos e o servidor. Quando o usuário adiciona um novo contato + ao servidor e, em seguida, coloca este contato em um novo grupo, o adaptador de sincronização deve adicionar o novo grupo + na tabela {@link android.provider.ContactsContract.Groups}. O grupo ou grupos a que um contato + bruto pertence são armazenados na tabela {@link android.provider.ContactsContract.Data}, usando + o tipo MIME {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}. +

    +

    + Se estiver projetando um adaptador de sincronização que adicionará dados de contato bruto ao Provedor de Contatos + a partir de servidores e não estiver usando grupos, será necessário fazer com que + o provedor torne os dados visíveis. No código que é executado quando um usuário adiciona uma conta + ao dispositivo, atualize a linha {@link android.provider.ContactsContract.Settings} + que o Provedor de Contatos adicionou para a conta. Nessa linha, defina o valor da + coluna {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE + Settings.UNGROUPED_VISIBLE} como 1. Ao fazê-lo, o Provedor de Contatos sempre + deixará os dados de contatos visíveis, mesmo sem usar grupos. +

    +

    Fotos de contatos

    +

    + A tabela {@link android.provider.ContactsContract.Data} armazena fotos como linhas com tipo MIME + {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE + Photo.CONTENT_ITEM_TYPE}. A coluna + {@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} da linha é vinculada + à coluna {@code android.provider.BaseColumns#_ID} do contato bruto à qual pertence. + A classe {@link android.provider.ContactsContract.Contacts.Photo} define uma subtabela de + {@link android.provider.ContactsContract.Contacts} contendo informações de foto + de uma foto principal do contato, que é a foto principal do contato bruto principal do contato. Da mesma forma, + a classe {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} define uma subtabela + de {@link android.provider.ContactsContract.RawContacts} contendo informações de foto + de uma foto principal do contato bruto. +

    +

    + A documentação de referência de {@link android.provider.ContactsContract.Contacts.Photo} e + {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} contém exemplos + de recuperação de informações de foto. Não há classe conveniente para recuperar a miniatura + principal de um contato bruto, mas é possível enviar uma consulta + à tabela {@link android.provider.ContactsContract.Data}, selecionando no {@code android.provider.BaseColumns#_ID} + do contato bruto, o + {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE + Photo.CONTENT_ITEM_TYPE} e a coluna + {@link android.provider.ContactsContract.Data#IS_PRIMARY} para encontrar a linha da foto principal do contato bruto. +

    +

    + Dados de fluxos sociais de uma pessoa também podem conter fotos. Elas são armazenadas + na tabela {@code android.provider.ContactsContract.StreamItemPhotos}, que é descrita com mais + detalhes na seção Fotos de fluxos sociais. +

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 0000000000000000000000000000000000000000..5005f9208bfe7026bac63524ecbfd9dd6dd2d2f4 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=Preceitos do provedor de conteúdo +@jd:body +
    +
    + +

    Neste documento

    +
      +
    1. + Visão geral +
        +
      1. + Acesso a um provedor +
      2. +
      3. + URIs de conteúdo +
      4. +
      +
    2. +
    3. + Recuperação de dados do provedor +
        +
      1. + Solicitação de permissão de acesso para leitura +
      2. +
      3. + Construção da consulta +
      4. +
      5. + Exibição dos resultados da consulta +
      6. +
      7. + Obtenção de dados de resultados da consulta +
      8. +
      +
    4. +
    5. + Permissões do provedor de conteúdo +
    6. +
    7. + Inserção, atualização e exclusão de dados +
        +
      1. + Inserção de dados +
      2. +
      3. + Atualização de dados +
      4. +
      5. + Exclusão de dados +
      6. +
      +
    8. +
    9. + Tipos de dados do provedor +
    10. +
    11. + Formas alternativas de acesso ao provedor +
        +
      1. + Acesso em lote +
      2. +
      3. + Acesso a dados via intenções +
      4. +
      +
    12. +
    13. + Classes de contrato +
    14. +
    15. + Referência de tipo MIME +
    16. +
    + + +

    Classes principais

    +
      +
    1. + {@link android.content.ContentProvider} +
    2. +
    3. + {@link android.content.ContentResolver} +
    4. +
    5. + {@link android.database.Cursor} +
    6. +
    7. + {@link android.net.Uri} +
    8. +
    + + +

    Exemplos relacionados

    +
      +
    1. + + Cursor (Pessoas) +
    2. +
    3. + + Cursor (Telefones) +
    4. +
    + + +

    Veja também

    +
      +
    1. + + Criação de um Provedor de conteúdo +
    2. +
    3. + + Provedor de agenda +
    4. +
    +
    +
    + + +

    + O provedor de conteúdo gerencia o acesso a um repositório central de dados. Um provedor + é parte de um aplicativo do Android, que, em geral, fornece a própria IU para trabalhar com + os dados. Contudo, provedores de conteúdo destinam-se principalmente ao uso por outros + aplicativos, que acessam o provedor usando um objeto cliente do provedor. Juntos, provedores + e clientes do provedor oferecem interface padronizada e consistente para dados que também lidam com + comunicação em processos internos e garantem acesso a dados. +

    +

    + Esse tópico descreve os conceitos básicos do seguinte: +

    +
      +
    • Como os provedores de conteúdo funcionam.
    • +
    • A API usada para recuperar dados de um provedor de conteúdo.
    • +
    • A API usada para inserir, atualizar ou excluir dados em um provedor de conteúdo.
    • +
    • Outros recursos de API que facilitam o trabalho com provedores.
    • +
    + + +

    Visão geral

    +

    + O provedor de conteúdo apresenta dados a aplicativos externos na forma de uma ou mais tabelas + similares às tabelas encontradas em um banco de dados relacional. Uma linha representa uma instância de algum tipo + de dados que o provedor coleta e cada coluna na linha representa uma parte individual de + dados coletados por uma instância. +

    +

    + Por exemplo: um dos provedores embutidos na plataforma do Android é o dicionário do usuário, que + armazena as grafias de palavras incomuns que o usuário deseja manter. A tabela 1 ilustra + como podem ser os dados nesta tabela do provedor: +

    +

    + Tabela 1: Tabela de dicionário do usuário de exemplo. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    palavraid do aplicativofrequêncialocalidade_ID
    reduçãodomapausuário1100en_US1
    pré-compiladorusuário14200fr_FR2
    appletusuário2225fr_CA3
    constusuário1255pt_BR4
    intusuário5100en_UK5
    +

    + Na tabela 1, cada linha representa uma instância de uma palavra que pode não ser + encontrada em um dicionário comum. Cada coluna representa alguns dados dessa palavra, como + a localidade em que foi encontrada pela primeira vez. Os cabeçalhos da coluna são nomes de coluna armazenados + no provedor. Para consultar a localidade de uma linha, consulte a sua coluna locale. Para + esse provedor, a coluna _ID serve como uma coluna de "chave principal" que + o provedor mantém automaticamente. +

    +

    + Observação: os provedores não precisam ter uma chave principal e não precisam + usar _ID como o nome de coluna de uma chave principal se uma for apresentada. Contudo, + se você deseja agrupar dados de um provedor em um {@link android.widget.ListView}, um dos + nomes de coluna deve ser _ID. Esse requisito é explicado com mais detalhes + na seção Exibição dos resultados da consulta. +

    +

    Acesso a um provedor

    +

    + Os aplicativos acessam dados a partir de um provedor de conteúdo + com um objeto cliente {@link android.content.ContentResolver}. Esse objeto tem métodos que chamam + métodos de nome idêntico no objeto do provedor, uma instância de uma das subclasses + concretas de {@link android.content.ContentProvider}. + Os métodos {@link android.content.ContentResolver} fornecem as funções básicas + do "CRUD" (criar, recuperar, atualizar e excluir) de armazenamento persistente. +

    +

    + O objeto {@link android.content.ContentResolver} no processo do aplicativo + cliente e o objeto {@link android.content.ContentProvider} no aplicativo que possui + o provedor lidam automaticamente com a comunicação de processos internos. + {@link android.content.ContentProvider} também age como uma camada de abstração entre + ser repositório de dados e a aparência externa de dados na forma de tabelas. +

    +

    + Observação: para acessar um provedor, o aplicativo normalmente precisa solicitar permissões + específicas no arquivo de manifesto. Isso é descrito com mais detalhes na seção + Permissões do provedor de conteúdo. +

    +

    + Por exemplo: para obter uma lista das palavras e respectivas localidades do Provedor de dicionário do usuário, + chama-se {@link android.content.ContentResolver#query ContentResolver.query()}. + O método {@link android.content.ContentResolver#query query()} chama + o método {@link android.content.ContentProvider#query ContentProvider.query()} definido pelo + Provedor de dicionário do usuário. As linhas de código a seguir exibem + uma chamada {@link android.content.ContentResolver#query ContentResolver.query()}: +

    +

    +// Queries the user dictionary and returns results
    +mCursor = getContentResolver().query(
    +    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    +    mProjection,                        // The columns to return for each row
    +    mSelectionClause                    // Selection criteria
    +    mSelectionArgs,                     // Selection criteria
    +    mSortOrder);                        // The sort order for the returned rows
    +
    +

    + A tabela 2 mostra como os argumentos para + {@link android.content.ContentResolver#query + query(Uri,projection,selection,selectionArgs,sortOrder)} correspondem a uma declaração SQL SELECT: +

    +

    + Tabela 2: Query() comparada à consulta SQL. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Argumento query()Palavra-chave/parâmetro de SELEÇÃOObservações
    UriFROM table_nameUri mapeia para a tabela no provedor chamado table_name.
    projectioncol,col,col,... + projection é uma matriz de colunas que devem ser incluídas para cada linha + recuperada. +
    selectionWHERE col = valueselection especifica o critério para a seleção de linhas.
    selectionArgs + (Não exatamente equivalente. Argumentos de seleção substituem marcadores de posição ? + na cláusula de seleção.) +
    sortOrderORDER BY col,col,... + sortOrder especifica a ordem em que as linhas aparecem + no {@link android.database.Cursor} retornado. +
    +

    URIs de conteúdo

    +

    + URI de conteúdo é uma URI que identifica dados em um provedor. URIs de conteúdo + contêm o nome simbólico de todo o provedor (sua autoridade) + e um nome que aponta para uma tabela (um caminho). Ao chamar + um método cliente para acessar uma tabela em um provedor, a URI de conteúdo da tabela é + um dos argumentos. +

    +

    + Nas linhas de código anteriores, a constante + {@link android.provider.UserDictionary.Words#CONTENT_URI} contém a URI de conteúdo + da tabela de "palavras" do dicionário do usuário. O objeto {@link android.content.ContentResolver} + analisa a autoridade da URI e usa-na para "determinar" o provedor + comparando a autoridade a uma tabela de provedores conhecidos do sistema. + O {@link android.content.ContentResolver} pode, então, enviar os argumentos da consulta ao provedor + correto. +

    +

    + O {@link android.content.ContentProvider} usa o caminho que é parte da URI de conteúdo para escolher + a tabela para acessar. Os provedores normalmente têm um caminho para cada tabela exposta. +

    +

    + Nas linhas de código anteriores, a URI completa da tabela de "palavras" é: +

    +
    +content://user_dictionary/words
    +
    +

    + onde a string user_dictionary é a autoridade do provedor e + a string words é o caminho da tabela. A string + content:// (o esquema) está sempre presente + e identifica isso como uma URI de conteúdo. +

    +

    + Muitos provedores permitem acesso a uma única linha em uma tabela, por meio da anexação do valor de um ID + no fim da URI. Por exemplo, para recuperar uma linha em que _ID seja + 4 do dicionário do usuário, é possível usar essa URI de conteúdo: +

    +
    +Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
    +
    +

    + Normalmente usam-se valores de ID ao recuperar um conjunto de linhas e, em seguida, é necessário atualizar ou excluir + uma delas. +

    +

    + Observação: as classes {@link android.net.Uri} e {@link android.net.Uri.Builder} + contêm métodos convenientes para a construção de objetos de URI bem formados a partir de strings. + As {@link android.content.ContentUris} contêm métodos conveniente para anexar valores de ID + a uma URI. O fragmento anterior usa {@link android.content.ContentUris#withAppendedId + withAppendedId()} para anexar um ID à URI de conteúdo UserDictionary. +

    + + + +

    Recuperação de dados pelo Provedor

    +

    + Esta seção descreve como recuperar dados de um provedor usando o Provedor de dicionário do usuário + como um exemplo. +

    +

    + Por uma questão de clareza, os fragmentos de código nesta seção chamam + {@link android.content.ContentResolver#query ContentResolver.query()} no "encadeamento da IU". + No código atual, contudo, deve-se realizar consultas assincronamente em um encadeamento separado. Um modo de fazê-lo + é usar a classe {@link android.content.CursorLoader}, descrita + com mais detalhes no guia + Carregadores. Além disso, as linhas de código são somente fragmentos — não mostram um aplicativo + completo. +

    +

    + Para recuperar dados de um provedor, siga essas etapas básicas: +

    +
      +
    1. + Solicite a permissão de acesso para leitura para um provedor. +
    2. +
    3. + Defina o código que envia uma consulta ao provedor. +
    4. +
    +

    Solicitação de permissão de acesso para leitura

    +

    + Para recuperar dados de um provedor, o aplicativo precisa de "permissão de acesso a leitura" + para o provedor. Não é possível solicitar essa permissão em tempo de execução. Em vez disso, deve-se especificar + que precisa dessa permissão no manifesto com o elemento +<uses-permission> + e o nome da permissão exata definida + pelo provedor. Ao especificar esse elemento no manifesto, você está efetivamente "solicitando" essa + permissão para o aplicativo. Quando usuários instalam seu aplicativo, eles concedem essa solicitação + implicitamente. +

    +

    + Para encontrar o nome exato da permissão de acesso para leitura do provedor que está usando, bem + como os nomes de outras permissões de acesso usadas pelo provedor, consulte a documentação + do provedor. +

    +

    + O papel das permissões no acesso a provedores é descrito com mais detalhes na seção + Permissões do provedor de conteúdo. +

    +

    + O Provedor de Dicionário do Usuário define a permissão + android.permission.READ_USER_DICTIONARY no arquivo de manifesto, portanto, se um + aplicativo quiser ler pelo provedor, deve solicitar essa permissão. +

    + +

    Construção da consulta

    +

    + A próxima etapa na recuperação de dados de um provedor é construir uma consulta. Este primeiro fragmento + define algumas variáveis para acessar o Provedor de Dicionário do Usuário: +

    +
    +
    +// A "projection" defines the columns that will be returned for each row
    +String[] mProjection =
    +{
    +    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    +    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    +    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
    +};
    +
    +// Defines a string to contain the selection clause
    +String mSelectionClause = null;
    +
    +// Initializes an array to contain selection arguments
    +String[] mSelectionArgs = {""};
    +
    +
    +

    + O próximo fragmento mostra como usar + {@link android.content.ContentResolver#query ContentResolver.query()} usando o Provedor de + Dicionário do Usuário como exemplo. Uma consulta cliente do provedor é similar a uma consulta SQL e contém um + conjunto de colunas para retornar, um conjunto de critérios de seleção e uma classificação ordenada. +

    +

    + O conjunto de colunas que a consulta deve retornar é chamado de projeção + (a variável mProjection). +

    +

    + A expressão que especifica as linhas a recuperar é dividida em uma cláusula de seleção + e em argumentos de seleção. A cláusula de seleção é uma combinação de expressões lógicas e booleanas + e nomes e valores de colunas (a variável mSelectionClause). Ao especificar + o parâmetro ? substituível em vez de um valor, o método da consulta recupera o valor + da matriz de argumentos de seleção (a variável mSelectionArgs). +

    +

    + No próximo fragmento, se o usuário não inserir nenhuma palavra, a cláusula de seleção será definida como + null e a consulta retornará todas as palavras no provedor. Se o usuário inserir + uma palavra, a cláusula de seleção será definida como UserDictionary.Words.WORD + " = ?" + e o primeiro elemento da matriz de argumentos de seleção é definida como a palavra que o usuário inserir. +

    +
    +/*
    + * This defines a one-element String array to contain the selection argument.
    + */
    +String[] mSelectionArgs = {""};
    +
    +// Gets a word from the UI
    +mSearchString = mSearchWord.getText().toString();
    +
    +// Remember to insert code here to check for invalid or malicious input.
    +
    +// If the word is the empty string, gets everything
    +if (TextUtils.isEmpty(mSearchString)) {
    +    // Setting the selection clause to null will return all words
    +    mSelectionClause = null;
    +    mSelectionArgs[0] = "";
    +
    +} else {
    +    // Constructs a selection clause that matches the word that the user entered.
    +    mSelectionClause = UserDictionary.Words.WORD + " = ?";
    +
    +    // Moves the user's input string to the selection arguments.
    +    mSelectionArgs[0] = mSearchString;
    +
    +}
    +
    +// Does a query against the table and returns a Cursor object
    +mCursor = getContentResolver().query(
    +    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    +    mProjection,                       // The columns to return for each row
    +    mSelectionClause                   // Either null, or the word the user entered
    +    mSelectionArgs,                    // Either empty, or the string the user entered
    +    mSortOrder);                       // The sort order for the returned rows
    +
    +// Some providers return null if an error occurs, others throw an exception
    +if (null == mCursor) {
    +    /*
    +     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
    +     * call android.util.Log.e() to log this error.
    +     *
    +     */
    +// If the Cursor is empty, the provider found no matches
    +} else if (mCursor.getCount() < 1) {
    +
    +    /*
    +     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
    +     * an error. You may want to offer the user the option to insert a new row, or re-type the
    +     * search term.
    +     */
    +
    +} else {
    +    // Insert code here to do something with the results
    +
    +}
    +
    +

    + Essa consulta é análoga à declaração SQL: +

    +
    +SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
    +
    +

    + Nesta declaração SQL, os nomes de coluna reais são usados no lugar de constantes de classes de contrato. +

    +

    Proteção contra inserções mal-intencionadas

    +

    + Se os dados gerenciados pelo provedor de conteúdo estiverem em um banco de dados SQL, inclusive dados não confiáveis + externos nas declarações SQL brutas, existe a possibilidade de haver uma injeção de SQL. +

    +

    + Considere esta cláusula de seleção: +

    +
    +// Constructs a selection clause by concatenating the user's input to the column name
    +String mSelectionClause =  "var = " + mUserInput;
    +
    +

    + Se você fizer isso, estará permitindo que o usuário concatene SQL mal-intencionados na sua declaração SQL. + Por exemplo: o usuário poderia inserir "nada; REMOVER TABELA *;" para mUserInput, o que + resultaria na cláusula de seleção var = nothing; DROP TABLE *;. Como + a cláusula de seleção é tratada como uma declaração SQL, isso pode fazer com que o provedor apague todas + as tabelas no banco de dados SQLite em questão (a menos que o provedor esteja configurado para capturar + tentativas de injeção de SQL). +

    +

    + Para evitar este problema, use uma cláusula de seleção que use ? como um parâmetro + substituível e uma matriz de argumentos de seleção separada. Ao fazer isso, a inserção de dados do usuário + limita-se diretamente à consulta em vez de ser interpretada como parte de uma declaração SQL. + Pelo fato de não ser tratada como SQL, a inserção de dados do usuário não injeta SQL mal-intencionados. Em vez de usar + a concatenação para incluir a inserção de dados do usuário, use esta cláusula de seleção: +

    +
    +// Constructs a selection clause with a replaceable parameter
    +String mSelectionClause =  "var = ?";
    +
    +

    + Configure a matriz de argumentos de seleção desta maneira: +

    +
    +// Defines an array to contain the selection arguments
    +String[] selectionArgs = {""};
    +
    +

    + Insira um valor na matriz de argumentos de seleção desta maneira: +

    +
    +// Sets the selection argument to the user's input
    +selectionArgs[0] = mUserInput;
    +
    +

    + Usar uma cláusula de seleção que use ? como um parâmetro substituível e uma matriz + de argumentos de seleção é o melhor modo de especificar uma seleção, mesmo que o provedor se baseie + base em um banco de dados SQL. +

    + +

    Exibição dos resultados da consulta

    +

    + O método cliente {@link android.content.ContentResolver#query ContentResolver.query()} sempre + retorna um {@link android.database.Cursor} contendo as colunas especificadas pela projeção + da consulta para as linhas que atendem aos critérios de seleção da consulta. + Um objeto {@link android.database.Cursor} fornece acesso para leitura aleatório para as linhas e colunas que + contém. Usando métodos {@link android.database.Cursor}, é possível repetir as linhas + nos resultados, determinar o tipo dos dados de cada coluna, extrair os dados de uma coluna e examinar + outras propriedades dos resultados. Algumas implementações do {@link android.database.Cursor} atualizam o objeto + automaticamente quando os dados do provedor mudam ou acionam métodos em um objeto observador + quando o {@link android.database.Cursor} muda, ou ambos. +

    +

    + Observação: os provedores podem restringir acesso a colunas com base na natureza + do objeto que realiza a consulta. Por exemplo: o Provedor de Contatos restringe o acesso de algumas colunas + a adaptadores de sincronização, por isso ela não os retornará a uma atividade ou serviço. +

    +

    + Se nenhuma linha atender aos critérios de seleção, o provedor + retorna um objeto {@link android.database.Cursor} para o qual + {@link android.database.Cursor#getCount Cursor.getCount()} é 0 (um cursor vazio). +

    +

    + Se ocorrer um erro interno, os resultados da consulta dependem do provedor determinado. Ele pode + escolher retornar null ou pode gerar uma {@link java.lang.Exception}. +

    +

    + Já que {@link android.database.Cursor} é uma "lista" de linhas, um bom modo de exibir + o conteúdo de um {@link android.database.Cursor} é vinculá-lo a uma {@link android.widget.ListView} + por meio de um {@link android.widget.SimpleCursorAdapter}. +

    +

    + O fragmento a seguir continua o código do fragmento anterior. Ele cria + um objeto {@link android.widget.SimpleCursorAdapter} contendo o {@link android.database.Cursor} + recuperado pela consulta e configura esse objeto para ser o adaptador de uma + {@link android.widget.ListView}: +

    +
    +// Defines a list of columns to retrieve from the Cursor and load into an output row
    +String[] mWordListColumns =
    +{
    +    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    +    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
    +};
    +
    +// Defines a list of View IDs that will receive the Cursor columns for each row
    +int[] mWordListItems = { R.id.dictWord, R.id.locale};
    +
    +// Creates a new SimpleCursorAdapter
    +mCursorAdapter = new SimpleCursorAdapter(
    +    getApplicationContext(),               // The application's Context object
    +    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    +    mCursor,                               // The result from the query
    +    mWordListColumns,                      // A string array of column names in the cursor
    +    mWordListItems,                        // An integer array of view IDs in the row layout
    +    0);                                    // Flags (usually none are needed)
    +
    +// Sets the adapter for the ListView
    +mWordList.setAdapter(mCursorAdapter);
    +
    +

    + Observação: para retornar uma {@link android.widget.ListView} com um + {@link android.database.Cursor}, o cursor deve conter uma coluna chamada _ID. + Por isso, a consulta exibida anteriormente recupera a coluna _ID da + tabelas de "palavras", mesmo que a {@link android.widget.ListView} não a exiba. + Essa restrição também explica por que a maioria dos provedores tem uma coluna _ID para cada + tabela. +

    + + +

    Obtenção de dados de resultados da consulta

    +

    + Em vez de simplesmente exibir resultados da consulta, é possível usá-los para outras tarefas. Por + exemplo: é possível recuperar grafias de um dicionário do usuário e, em seguida, procurá-los + em outros provedores. Para isso, repetem-se as linhas no {@link android.database.Cursor}: +

    +
    +
    +// Determine the column index of the column named "word"
    +int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
    +
    +/*
    + * Only executes if the cursor is valid. The User Dictionary Provider returns null if
    + * an internal error occurs. Other providers may throw an Exception instead of returning null.
    + */
    +
    +if (mCursor != null) {
    +    /*
    +     * Moves to the next row in the cursor. Before the first movement in the cursor, the
    +     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
    +     * exception.
    +     */
    +    while (mCursor.moveToNext()) {
    +
    +        // Gets the value from the column.
    +        newWord = mCursor.getString(index);
    +
    +        // Insert code here to process the retrieved word.
    +
    +        ...
    +
    +        // end of while loop
    +    }
    +} else {
    +
    +    // Insert code here to report an error if the cursor is null or the provider threw an exception.
    +}
    +
    +

    + As implementações {@link android.database.Cursor} contêm diversos métodos "get" (obter) + para recuperar diferentes tipos de dados do objeto. Por exemplo, o fragmento anterior + usa {@link android.database.Cursor#getString getString()}. Elas também têm + um método {@link android.database.Cursor#getType getType()} que retorna um valor indicando + o tipo dos dados da coluna. +

    + + + +

    Permissões do provedor de conteúdo

    +

    + Um aplicativo do provedor pode especificar permissões que outros aplicativos devem ter para + acessar os dados do provedor. Essas permissões garantem que o usuário saiba quais dados + um aplicativo tentará acessar. Com base nos requisitos do provedor, outros aplicativos + solicitam as permissões de que precisam para acessar o provedor. Usuários finais veem as permissões + solicitadas quando instalam o aplicativo. +

    +

    + Se um aplicativo do provedor não especificar nenhuma permissão, outros aplicativos não terão + acesso aos dados do provedor. No entanto, os componentes no aplicativo do provedor sempre têm + acesso total para leitura e gravação, independentemente das permissões especificadas. +

    +

    + Como observado anteriormente, o Provedor de Dicionário do Usuário requer + a permissão android.permission.READ_USER_DICTIONARY para recuperar dados dele. + O provedor tem a permissão android.permission.WRITE_USER_DICTIONARY + separadamente para inserção, atualização ou exclusão de dados. +

    +

    + Para obter as permissões necessárias para acessar um provedor, um aplicativo as solicita +como um elemento <uses-permission> + no arquivo de manifesto. Quando o Android Package Manager (gerente de pacotes do Android) instala o aplicativo, o usuário + precisa aprovar todas as permissões que o aplicativo solicita. Se o usuário aprovar todas elas, + o gerente de pacotes continua a instalação; se o usuário não as aprovar, o gerente de pacotes + aborta a instalação. +

    +

    + O elemento +<uses-permission> a seguir + solicita acesso para leitura ao Provedor de Dicionário do Usuário: +

    +
    +    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
    +
    +

    + O impacto das permissões no acesso ao provedor é explicado com mais detalhes + no guia Permissões e segurança. +

    + + + +

    Inserção, atualização e exclusão de dados

    +

    + Do mesmo modo que se recupera dados de um provedor, também usa-se a interação entre + um cliente do provedor e o {@link android.content.ContentProvider} do provedor para modificar dados. + Chama-se um método de {@link android.content.ContentResolver} com argumentos que são passados para + o método correspondente de {@link android.content.ContentProvider}. O provedor e o cliente + do provedor tratam automaticamente da segurança e da comunicação de processos internos. +

    +

    Inserção de dados

    +

    + Para inserir dados em um provedor, chame + o método +{@link android.content.ContentResolver#insert ContentResolver.insert()}. Esse método insere uma nova linha no provedor e retorna uma URI de conteúdo dessa linha. + Este fragmento mostra como inserir uma nova palavra no Provedor de Dicionário do Usuário: +

    +
    +// Defines a new Uri object that receives the result of the insertion
    +Uri mNewUri;
    +
    +...
    +
    +// Defines an object to contain the new values to insert
    +ContentValues mNewValues = new ContentValues();
    +
    +/*
    + * Sets the values of each column and inserts the word. The arguments to the "put"
    + * method are "column name" and "value"
    + */
    +mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
    +mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
    +mNewValues.put(UserDictionary.Words.WORD, "insert");
    +mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
    +
    +mNewUri = getContentResolver().insert(
    +    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
    +    mNewValues                          // the values to insert
    +);
    +
    +

    + Os dados da nova linha vão para um objeto {@link android.content.ContentValues} único, que + tem forma semelhante a um cursor de uma linha. As colunas nesse objeto não precisam ter + o mesmo tipo de dados e, se você não quiser especificar um valor, pode definir uma coluna + como null usando {@link android.content.ContentValues#putNull ContentValues.putNull()}. +

    +

    + O fragmento não adiciona a coluna _ID porque essa coluna é mantida + automaticamente. O provedor atribui um valor exclusivo de _ID para cada linha + adicionada. Os provedores normalmente usam esse valor como a chave principal da tabela. +

    +

    + A URI de conteúdo retornada em newUri identifica a linha recentemente adicionada com + o seguinte formato: +

    +
    +content://user_dictionary/words/<id_value>
    +
    +

    + O <id_value> é o conteúdo de _ID da nova linha. + A maioria dos provedores pode detectar essa forma de URI de conteúdo automaticamente e, em seguida, realizar a operação + solicitada naquela linha. +

    +

    + Para obter o valor de _ID do {@link android.net.Uri} retornado, chame + {@link android.content.ContentUris#parseId ContentUris.parseId()}. +

    +

    Atualização de dados

    +

    + Para atualizar uma linha, use um objeto {@link android.content.ContentValues} com os valores + e os critérios de seleção atualizados, como se faz com uma inserção e em uma consulta, respectivamente. + O método cliente usado é + {@link android.content.ContentResolver#update ContentResolver.update()}. Só é necessário adicionar + valores ao objeto {@link android.content.ContentValues} das colunas que forem atualizadas. Se você + deseja apagar o conteúdo de uma coluna, defina o valor como null. +

    +

    + O fragmento a seguir altera todas as linhas cuja localidade tem o idioma "en" para + terem uma localidade de null. O valor de retorno é o número de linhas que foram atualizadas: +

    +
    +// Defines an object to contain the updated values
    +ContentValues mUpdateValues = new ContentValues();
    +
    +// Defines selection criteria for the rows you want to update
    +String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
    +String[] mSelectionArgs = {"en_%"};
    +
    +// Defines a variable to contain the number of updated rows
    +int mRowsUpdated = 0;
    +
    +...
    +
    +/*
    + * Sets the updated value and updates the selected words.
    + */
    +mUpdateValues.putNull(UserDictionary.Words.LOCALE);
    +
    +mRowsUpdated = getContentResolver().update(
    +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    +    mUpdateValues                       // the columns to update
    +    mSelectionClause                    // the column to select on
    +    mSelectionArgs                      // the value to compare to
    +);
    +
    +

    + Você também deve filtrar a inserção de dados do usuário ao chamar + {@link android.content.ContentResolver#update ContentResolver.update()}. Para saber mais sobre + isso, leia a seção Proteção contra inserções mal-intencionadas. +

    +

    Exclusão de dados

    +

    + Excluir linhas é semelhante a recuperar dados de linhas: especificam-se critérios de seleção para as linhas + que se deseja excluir e o método cliente retorna o número de linhas excluídas. + O fragmento a seguir exclui linhas cujo appid (id do aplicativo) corresponda a "usuário". O método retorna + o número de linhas excluídas. +

    +
    +
    +// Defines selection criteria for the rows you want to delete
    +String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
    +String[] mSelectionArgs = {"user"};
    +
    +// Defines a variable to contain the number of rows deleted
    +int mRowsDeleted = 0;
    +
    +...
    +
    +// Deletes the words that match the selection criteria
    +mRowsDeleted = getContentResolver().delete(
    +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    +    mSelectionClause                    // the column to select on
    +    mSelectionArgs                      // the value to compare to
    +);
    +
    +

    + Também se deve filtrar a inserção de dados do usuário ao chamar + {@link android.content.ContentResolver#delete ContentResolver.delete()}. Para saber mais sobre + isso, leia a seção Proteção contra inserções mal-intencionadas. +

    + +

    Tipos de dados do provedor

    +

    + Provedores de conteúdo podem oferecer muitos tipos de dados diferentes. O Provedor de Dicionário do Usuário oferece somente + texto, mas provedores também podem oferecer os seguintes formatos: +

    +
      +
    • + número inteiro +
    • +
    • + número inteiro longo (longo) +
    • +
    • + ponto flutuante +
    • +
    • + ponto flutuante longo (duplo) +
    • +
    +

    + Outros tipos de dados que provedores usam com frequência são objetos binários largos (BLOB) implementados como uma + matriz de byte de 64 kB. É possível ver os tipos de dados disponíveis consultando + os métodos "get" da classe {@link android.database.Cursor}. +

    +

    + O tipo dos dados para cada coluna em um provedor normalmente é listado na documentação do provedor. + Os tipos de dados do Provedor de Dicionário do Usuário são listados na documentação de referência + para sua classe de contrato {@link android.provider.UserDictionary.Words} (classes de contrato são + descritas na seção Classes de contrato). + Também é possível determinar o tipo dos dados chamando {@link android.database.Cursor#getType + Cursor.getType()}. +

    +

    + Os provedores também mantêm informações do tipo MIME de dados para cada URI de conteúdo que definem. É possível + usar as informações do tipo MIME para descobrir se o aplicativo pode tratar de dados que + o provedor fornece ou para escolher um tipo de tratamento com base no tipo MIME. Normalmente é necessário ter + o tipo MIME ao trabalhar com um provedor que contenha estruturas + complexas de dados ou arquivos. Por exemplo: a tabela {@link android.provider.ContactsContract.Data} + no Provedor de contatos usa tipos MIME para etiquetar o tipo dos dados de contato armazenados em cada + linha. Para obter o tipo MIME correspondente a uma URI de conteúdo, chame + {@link android.content.ContentResolver#getType ContentResolver.getType()}. +

    +

    + A seção Referência de tipo MIME descreve + a sintaxe de tipos MIME padrão e personalizados. +

    + + + +

    Formas alternativas de acessar o provedor

    +

    + Três formas alternativas de acesso ao provedor são importantes no desenvolvimento do aplicativo: +

    +
      +
    • + Acesso em lote: É possível criar um lote de chamadas de acesso com métodos na + classe {@link android.content.ContentProviderOperation} e, em seguida, aplicá-los com + {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. +
    • +
    • + Consultas assíncronas: Devem-se realizar consultas em um encadeamento separado. Um modo de fazer isso é + usar um objeto {@link android.content.CursorLoader}. Os exemplos + no guia Carregadores demonstram + como fazer isso. +
    • +
    • + Acesso a dados via intenções Embora não seja possível enviar uma intenção + diretamente a um provedor, é possível enviar uma intenção ao aplicativo do provedor, que + normalmente é o recomendável para modificar os dados do provedor. +
    • +
    +

    + O acesso em lote e a modificação via intenções são descritos nas seções a seguir. +

    +

    Acesso em lote

    +

    + O acesso em lote a um provedor é útil para inserir uma grande quantidade de linhas ou para inserir + linhas em diversas tabelas na mesma chamada de método ou, em geral, para realizar um conjunto de + operações dentre limites de processo como uma transação (uma operação atômica). +

    +

    + Para acessar um provedor em "modo de lote", + cria-se uma matriz de objetos {@link android.content.ContentProviderOperation} e, em seguida, + envia-os a um provedor de conteúdo com + {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. Confere-se a + autoridade do provedor de conteúdo para esse método em vez de para uma URI de conteúdo específica. + Isso permite que cada objeto {@link android.content.ContentProviderOperation} na matriz trabalhe + com uma tabela diferente. Chamar {@link android.content.ContentResolver#applyBatch + ContentResolver.applyBatch()} retorna uma matriz de resultados. +

    +

    + A descrição da classe de contrato {@link android.provider.ContactsContract.RawContacts} + contém um fragmento de código que demonstra a inserção em lote. +O aplicativo de exemplo do Gerente de contato + contém um exemplo de acesso em lote em seu + arquivo de origem ContactAdder.java. +

    + +

    Acesso a dados via intenções

    +

    + Intenções podem fornecer acesso indireto a um provedor de conteúdo. Basta permitir que o usuário acesse + dados em um provedor mesmo se o aplicativo não tiver permissões de acesso, tanto + retornando uma intenção de resultado de um aplicativo que tem permissões quanto ativando + um aplicativo que tem permissões e permitindo que o usuário trabalhe nele. +

    +

    Obtenção de acesso com permissões temporárias

    +

    + É possível acessar dados em um provedor de conteúdo, mesmo sem s permissões + de acesso adequadas. Basta enviar uma intenção a um aplicativo que tenha as permissões e + receber de volta uma intenção de resultado contendo permissões da "URI". + Essas são permissões para uma URI de conteúdo específica que duram enquanto durar a atividade + que as recebeu. O aplicativo que tem permissões permanentes concedem permissões + temporárias ativando um sinalizador na intenção de resultado: +

    +
      +
    • + Permissão de leitura: + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} +
    • +
    • + Permissão de gravação: + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} +
    • +
    +

    + Observação: esses sinalizadores não fornecem acesso geral de leitura ou gravação ao provedor + cuja autoridade esteja contida na URI de conteúdo. O acesso destina-se somente à URI. +

    +

    + Um provedor define permissões de URI para URIs de conteúdo no manifesto usando o atributo +android:grantUriPermission + do elemento +<provider>, + bem como o elemento filho +<grant-uri-permission> + do elemento +<provider> +. O mecanismo das permissões de URI é explicado com mais detalhes + no guia Permissões e segurança, + na seção "Permissões da URI". +

    +

    + Por exemplo: é possível recuperar dados de um contato no Provedor de Contatos, mesmo sem + a permissão {@link android.Manifest.permission#READ_CONTACTS}. Você pode desejar fazer + isso em um aplicativo que envie e-mails de parabenização a um contato no aniversário dele. Em vez de + solicitar {@link android.Manifest.permission#READ_CONTACTS}, que fornece acesso a todos + os contatos do usuário e suas informações, é preferível deixar que o usuário controle quais + contatos serão usados pelo seu aplicativo. Para isso, use o processo a seguir: +

    +
      +
    1. + O aplicativo envia uma intenção contendo a ação + {@link android.content.Intent#ACTION_PICK} e o tipo MIME + {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} dos "contatos" usando + o método {@link android.app.Activity#startActivityForResult + startActivityForResult()}. +
    2. +
    3. + Como essa intenção corresponde ao filtro de intenções da + atividade de "seleção" do aplicativo Pessoas, a atividade ficará em primeiro plano. +
    4. +
    5. + Na atividade de seleção, o usuário seleciona + um contato para atualizar. Quando isso acontece, a atividade de seleção chama + {@link android.app.Activity#setResult setResult(resultcode, intent)} + para configurar uma intenção para retornar ao aplicativo. A intenção contém a URI de conteúdo + do contato que o usuário selecionou e os sinalizadores "extras" + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}. Esses sinalizadores concedem + permissão de URI para que o aplicativo leia dados do contato apontados pela + URI de conteúdo. A atividade de seleção, em seguida, chama {@link android.app.Activity#finish()} para + retornar o controle para o aplicativo. +
    6. +
    7. + A atividade volta ao primeiro plano e o sistema chama o + método {@link android.app.Activity#onActivityResult onActivityResult()} + da sua atividade. Esse método recebe a intenção de resultado criada pela atividade de seleção + no aplicativo Pessoas. +
    8. +
    9. + Com a URI de conteúdo da intenção de resultado, é possível ler os dados do contato + a partir do Provedor de Contatos, mesmo que não tenha solicitado permissão permanente de acesso a leitura + para o provedor no manifesto. Pode-se, então, pode obter as informações de data de nascimento do contato + ou seu endereço de e-mail e, assim, enviar um e-mail de parabenização. +
    10. +
    +

    Uso de outro aplicativo

    +

    + Um modo simples que permite ao usuário modificar dados para os quais você não tem permissões de acesso é + ativar um aplicativo que tenha permissões e deixar o usuário fazer o resto. +

    +

    + Por exemplo: o aplicativo Agenda aceita uma + intenção {@link android.content.Intent#ACTION_INSERT} que permite a ativação da + IU de inserção do aplicativo. Você pode passar dados "extras" nessa intenção que o aplicativo + usa para "pré-preencher" a IU. como os eventos recorrentes têm sintaxe complexa, o modo + recomendado de inserir eventos no Provedor de Agenda é ativar o aplicativo Agenda com um + {@link android.content.Intent#ACTION_INSERT} e, em seguida, deixar o usuário inserir o evento. +

    + +

    Classes de contrato

    +

    + As classes de contrato definem constantes que ajudam os aplicativos a trabalhar com URIs de conteúdo, nomes + de colunas, ações de intenções e outros recursos de um provedor de conteúdo. Elas não estão + automaticamente inclusas em um provedor; o desenvolvedor do provedor deve defini-las e + disponibilizá-las a outros desenvolvedores. Muitos dos provedores incluídos na plataforma + do Android têm classes de contrato correspondentes no pacote {@link android.provider}. +

    +

    + Por exemplo: o Provedor de Dicionário do Usuário tem uma classe de contrato + {@link android.provider.UserDictionary} que contém constantes de URI de conteúdo e de nome de coluna. + A URI de conteúdo da tabela de "palavras" é definida na constante + {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. + A classe {@link android.provider.UserDictionary.Words} também contém constantes de nome de coluna + que são usadas nos fragmentos de exemplo neste guia. Por exemplo, uma projeção de consulta pode ser + definida como: +

    +
    +String[] mProjection =
    +{
    +    UserDictionary.Words._ID,
    +    UserDictionary.Words.WORD,
    +    UserDictionary.Words.LOCALE
    +};
    +
    +

    + Outra classe de contrato é {@link android.provider.ContactsContract} para o Provedor de Contatos. + A documentação de referência desta classe contém fragmentos de código de exemplo. Uma das suas + subclasses, {@link android.provider.ContactsContract.Intents.Insert}, é uma classe + de contrato que contém constantes para intenções e dados da intenção. +

    + + + +

    Referência do tipo MIME

    +

    + Provedores de conteúdo podem retornar tipos MIME de mídia, strings de tipos MIME personalizados ou ambos. +

    +

    + Tipos MIME têm o formato +

    +
    +type/subtype
    +
    +

    + Por exemplo: o tipo MIME text/html conhecido tem o tipo text + e o subtipo html. Se o provedor retornar esse tipo para uma URI, + uma consulta usando essa URI retornará um texto que contém tags HTML. +

    +

    + Strings de tipo MIME personalizado, também chamadas de tipos MIME "específicos do fornecedor" (vendor-specific), têm mais + valores de tipo e subtipo complexos. O valor de tipo é sempre +

    +
    +vnd.android.cursor.dir
    +
    +

    + para diversas linhas ou +

    +
    +vnd.android.cursor.item
    +
    +

    + para uma única linha. +

    +

    + O subtipo é específico do provedor. Os provedores embutidos do Android normalmente têm um subtipo + simples. Por exemplo, quando o aplicativo de contatos cria uma linha para um número de telefone, + ele configura o seguinte tipo MIME na linha: +

    +
    +vnd.android.cursor.item/phone_v2
    +
    +

    + Observe que o valor do subtipo é simplesmente phone_v2. +

    +

    + Outros desenvolvedores de provedor podem criar os próprios padrões de subtipos com base + na autoridade do provedor e nos nomes da tabela. Por exemplo: considere um provedor que contenha horários de trens. + A autoridade do provedor é com.example.trains e ele contém as tabelas + Linha1, Linha2 e Linha3. Em resposta à URI de conteúdo +

    +

    +

    +content://com.example.trains/Line1
    +
    +

    + para a tabela Linha1, o provedor retorna o tipo MIME +

    +
    +vnd.android.cursor.dir/vnd.example.line1
    +
    +

    + Em resposta à URI de conteúdo +

    +
    +content://com.example.trains/Line2/5
    +
    +

    + da linha 5 na tabela Linha2, o provedor retorna o tipo MIME +

    +
    +vnd.android.cursor.item/vnd.example.line2
    +
    +

    + A maioria dos provedores define constantes de classe de contrato para os tipos MIME que usam. + A classe de contrato {@link android.provider.ContactsContract.RawContacts} do Provedor de Contatos, + por exemplo, define a constante + {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} para o tipo MIME + de uma linha exclusiva do contato bruto. +

    +

    + URIs de conteúdo de linhas exclusivas são descritas na seção + URIs de conteúdo. +

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..11ad4c3b1cca845e6d513919fd3a41039eafa615 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=Criação de um Provedor de Conteúdo +@jd:body + + + +

    + O provedor de conteúdo gerencia o acesso a um repositório central de dados. Implementa-se + um provedor como uma ou mais classes em um aplicativo do Android, em conjunto com elementos + no arquivo de manifesto. Uma das classes implementa uma subclasse + {@link android.content.ContentProvider}, que é a interface entre o provedor + e outros aplicativos. Embora provedores de conteúdo se destinem a disponibilizar dados a outros + aplicativos, naturalmente é possível ter atividades no aplicativo que permitam ao usuário + consultar e modificar os dados gerenciados pelo provedor. +

    +

    + O restante deste tópico é uma lista básica de etapas para a criação de um provedor de conteúdo e uma lista + de APIs para usar. +

    + + + +

    Antes de começar a criar

    +

    + Antes de começar a criar um provedor, faça o seguinte: +

    +
      +
    1. + Avalie se você precisa de um provedor de conteúdo. É necessário criar um provedor + de conteúdo para fornecer um ou mais dos recursos a seguir: +
        +
      • Oferecer dados ou arquivos complexos a outros aplicativos.
      • +
      • Permitir que usuários copiem dados complexos do seu aplicativo para outros aplicativos.
      • +
      • Fornecer sugestões de pesquisa personalizada usando a estrutura de pesquisa.
      • +
      +

      + Não é necessário um provedor para usar um banco de dados SQLite se o uso for inteiramente dentro + do próprio aplicativo. +

      +
    2. +
    3. + Se você ainda não tiver feito isso, leia o tópico + + Preceitos do provedor de conteúdo para saber mais sobre provedores. +
    4. +
    +

    + A seguir, siga estas etapas para criar seu provedor: +

    +
      +
    1. + Projete o armazenamento bruto dos dados. Os provedores de conteúdo oferecem dados de duas maneiras: +
      +
      + Dados de arquivo +
      +
      + Dados que normalmente transitam em arquivos, como + fotos, áudio ou vídeos. Armazene os arquivos no espaço privado + do seu aplicativo. Em resposta a uma solicitação de um arquivo por outro aplicativo, + o provedor pode oferecer um identificador para o arquivo. +
      +
      + Dados "estruturados" +
      +
      + Dados que normalmente transitam em um banco de dados, uma matriz ou estrutura similar. + Armazene os dados de forma compatível com tabelas de linhas e colunas. Cada linha + representa uma entidade, como uma pessoa ou um item em um inventário. Cada coluna representa + alguns dados da entidade, como o nome da pessoa ou o preço do item. Um modo comum de + armazenar esse tipo de dados é em um banco de dados SQLite, mas é possível usar qualquer tipo + de armazenamento persistente. Para saber mais sobre os tipos de armazenamento disponíveis + no sistema Android, consulte a seção +Projeto de armazenamento de dados. +
      +
      +
    2. +
    3. + Defina uma implementação sólida da classe {@link android.content.ContentProvider} + e seus métodos obrigatórios. Essa classe é a interface entre seus dados e o restante + do sistema Android. Para obter mais informações sobre essa classe, consulte a seção + Implementação da classe ContentProvider. +
    4. +
    5. + Defina a string de autoridade do provedor, suas URIs de conteúdo e seus nomes de coluna. Se você deseja que + o aplicativo do provedor trate de intenções, defina também ações de intenção, dados extras + e sinalizadores. Também defina as permissões necessárias para aplicativos que queiram + acessar seus dados. Deve-se considerar definir todos esses valores como constantes + em uma classe de contrato separada; posteriormente, será possível expor essa classe a outros desenvolvedores. Para + obter mais informações sobre URIs de conteúdo, consulte + a seção Projeto de URIs de conteúdo. + Para obter mais informações sobre intenções, consulte + a seção Intenções e acesso a dados. +
    6. +
    7. + Adicione outras partes opcionais, como dados de exemplo ou uma implementação + de {@link android.content.AbstractThreadedSyncAdapter} que possa sincronizar dados entre + o provedor e os dados com base em nuvem. +
    8. +
    + + + +

    Projeto de armazenamento de dados

    +

    + Os provedores de conteúdo são a interface para dados salvos em um formato estruturado. Antes de criar + a interface, é preciso decidir como armazenar os dados. É possível armazená-los da forma que + quiser e, em seguida, projetar a interface para ler e gravar os dados conforme o necessário. +

    +

    + Essas são algumas das tecnologias de armazenamento de dados disponíveis no Android: +

    +
      +
    • + O sistema Android contém uma API de banco de dados SQLite que os provedores do Android usam + para armazenar dados orientados a tabela. + A classe {@link android.database.sqlite.SQLiteOpenHelper} ajuda a criar bancos de dados + e a classe {@link android.database.sqlite.SQLiteDatabase} é a classe de base para acessar + banco de dados. +

      + Lembre-se de que não é necessário usar nenhum banco de dados para implementar o repositório. Um provedor + aparece externamente como um conjunto de tabelas, semelhante a um banco de dados relacional, mas isso 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 armazenamento de arquivos, leia o tópico + Armazenamento de dados. Se você estiver + projetando um provedor que oferece dados relacionados a mídia como música ou vídeos, é possível + ter um provedor que combine dados de tabela e de arquivos. +
    • +
    • + Para trabalhar com dados com base em rede, use classes em {@link java.net} + e em {@link android.net}. É possível, também, sincronizar dados com base em rede com uma armazenagem + local de dados, como um banco de dados e, em seguida, fornecer os dados na forma de tabelas ou arquivos. + O exemplo de aplicativo + Exemplo de adaptador de sincronização demonstra o tipo de sincronização. +
    • +
    +

    + Considerações para projetar dados +

    +

    + A seguir há algumas dicas para projetar a estrutura de dados do seu provedor: +

    +
      +
    • + Os dados de tabela sempre devem ter uma coluna de "chave principal" que o provedor mantém + como um valor numérico exclusivo para cada linha. É possível usar esse valor para vincular a linha a linhas + relacionadas em outras tabelas (usando-o como uma "chave externa"). Embora seja possível usar qualquer nome + para essa coluna, é melhor usar {@link android.provider.BaseColumns#_ID BaseColumns._ID} + porque a vinculação dos resultados de uma consulta de provedor com uma + {@link android.widget.ListView} requer que uma das colunas tenha o nome + _ID. +
    • +
    • + Se você deseja fornecer imagens bitmap ou outras partes muito grandes de dados orientados a arquivo, armazene + os dados em um arquivo e, em seguida, forneça-o indiretamente em vez de armazená-lo diretamente em uma + tabela. Ao fazer isso, será necessário informar aos usuários do provedor que eles precisam usar + um método de arquivo {@link android.content.ContentResolver} para acessar os dados. +
    • +
    • + Use objeto grande binário (BLOB) como tipo de dados para armazenar dados que variam em tamanho ou que tenham + estrutura variável. Por exemplo: é possível usar uma coluna de BLOB para armazenar + um buffer de protocolo ou + uma estrutura JSON. +

      + Pode-se usar um BLOB para implementar uma tabela independente de esquema. Nesse + tipo de tabela, define-se uma coluna de chave principal, uma coluna de tipo MIME e uma + ou mais colunas genéricas como BLOB. O significado dos dados nas colunas BLOB é indicado + pelo valor na coluna de tipo MIME. Isso permite o armazenamento de diferentes tipos de linha + na mesma tabela. A tabela de "dados" {@link android.provider.ContactsContract.Data} + do Provedor de contatos é um exemplo de uma tabela + independente de esquema. +

      +
    • +
    + +

    Projeto de URIs de conteúdo

    +

    + URI de conteúdo é uma URI que identifica dados em um provedor. URIs de conteúdo + contêm o nome simbólico de todo o provedor (sua autoridade) e um + nome que aponta para uma tabela ou arquivo (um caminho). Parte do ID opcional aponta para + uma linha individual em uma tabela. Cada método de acesso aos dados + {@link android.content.ContentProvider} de tem uma URI de conteúdo como um argumento. Isso permite + determinar a tabela, a linha ou o arquivo a acessar. +

    +

    + Os conceitos básicos de URIs de conteúdo são escritos no tópico + + Preceitos do provedor de conteúdo. +

    +

    Projeto de uma autoridade

    +

    + Os provedores normalmente têm uma única autoridade, que serve como seu nome interno do Android. Para + evitar conflitos com outros provedores, deve-se usar a propriedade de domínio da internet (ao contrário) + como a base da autoridade do provedor. Em virtude de esta recomendação ser verdadeira para nomes + de pacote do Android, é 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 pacote do Android for + com.example.<appname>, deve-se atribuir ao provedor + a autoridade com.example.<appname>.provider. +

    +

    Projeto de uma estrutura de caminho

    +

    + Desenvolvedores normalmente criam URIs de conteúdo a partir da autoridade anexando caminhos que apontam para + tabelas individuais. Por exemplo: se você tiver duas tabelas (tabela1 e + tabela2), combine a autoridade do exemplo anterior para produzir + as URIs de conteúdo + com.example.<appname>.provider/table1 e + com.example.<appname>.provider/table2. Caminhos não + se limitam a um único segmento e não precisa ter uma tabela para cada nível do caminho. +

    +

    Identificação de IDs de URIs de conteúdo

    +

    + Por convenção, provedores fornecem acesso a uma linha exclusiva em uma tabela aceitando uma URI de conteúdo + com um valor de ID para a linha no fim da URI. Também por convenção, provedores combinam + o valor do ID com a coluna _ID da tabela e realizam o acesso solicitado + na linha correspondente. +

    +

    + Essa convenção facilita a padronização comum do projeto para aplicativos que acessam um provedor. O aplicativo + realiza uma consulta no provedor e exibe o {@link android.database.Cursor} + resultante em uma {@link android.widget.ListView} usando um {@link android.widget.CursorAdapter}. + A definição de {@link android.widget.CursorAdapter} requer que uma das colunas + no {@link android.database.Cursor} seja _ID +

    +

    + Em seguida, o usuário seleciona uma das linhas exibidas na IU para visualizar ou modificar + os dados. O aplicativo tem a linha correspondente do {@link android.database.Cursor} apoiando + a {@link android.widget.ListView}, obtém o valor _ID para essa linha, anexa-o + à URI de conteúdo e envia a solicitação de acesso ao provedor. O provedor, então, pode realizar + a consulta ou modificação na exata linha que o usuário selecionou. +

    +

    Padrões de URI de conteúdo

    +

    + Para ajudar a escolher que ação tomar para uma URI de conteúdo recebida, a API do provedor contém + a classe de conveniência {@link android.content.UriMatcher}, que mapeia "padrões" de URI de conteúdo + como valores de número inteiro. É possível usar os valores de número inteiro em uma declaração switch que + escolha a ação desejada para a URI de conteúdo ou URIs que atendam ao padrão determinado. +

    +

    + Um padrão de URI de conteúdo corresponde a URIs de conteúdo que usam caracteres especiais: +

    +
      +
    • + *: Corresponde a uma string de qualquer caractere válido de qualquer comprimento. +
    • +
    • + #: Corresponde a uma string de caracteres numéricos de qualquer comprimento. +
    • +
    +

    + Como um exemplo de projeto e codificação da identificação de URIs de conteúdo, considere um provedor + com a autoridade com.example.app.provider que reconheça as URIs de conteúdo + que apontam para tabelas: +

    +
      +
    • + content://com.example.app.provider/table1: Uma tabela chamada table1. +
    • +
    • + content://com.example.app.provider/table2/dataset1: Uma tabela chamada + dataset1. +
    • +
    • + content://com.example.app.provider/table2/dataset2: Uma tabela chamada + dataset1. +
    • +
    • + content://com.example.app.provider/table3: Uma tabela chamada table3. +
    • +
    +

    + O provedor também reconhece essas URIs de conteúdo se elas tiverem um ID de linha anexado, 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 seriam possíveis: +

    +
    +
    + content://com.example.app.provider/* +
    +
    + Corresponde a qualquer URI de conteúdo no provedor. +
    +
    + content://com.example.app.provider/table2/*: +
    +
    + Corresponde a uma URI de conteúdo das tabelas dataset1 + e dataset2, mas não a URIs de conteúdo de table1 + nem table3. +
    +
    + content://com.example.app.provider/table3/#: Corresponde a uma URI de conteúdo + para linhas exclusivas em table3, como + content://com.example.app.provider/table3/6 da linha identificada por + 6. +
    +
    +

    + O fragmento de código a seguir mostra como funcionam os métodos em {@link android.content.UriMatcher}. + Esse código identifica URIs para toda uma tabela, diferentemente das URIs + para uma linha exclusiva, usando o padrão de URI de conteúdo + content://<authority>/<path> para tabelas e + content://<authority>/<path>/<id> para linhas exclusivas. +

    +

    + O método {@link android.content.UriMatcher#addURI(String, String, int) addURI()} mapeia + uma autoridade e um caminho como um valor de número inteiro. O método {@link android.content.UriMatcher#match(Uri) + match()} retorna o valor de número inteiro para a URI. Uma declaração switch + escolhe entre consultar a tabela inteira e consultar um único registro: +

    +
    +public class ExampleProvider extends ContentProvider {
    +...
    +    // Creates a UriMatcher object.
    +    private static final UriMatcher sUriMatcher;
    +...
    +    /*
    +     * The calls to addURI() go here, for all of the content URI patterns that the provider
    +     * should recognize. 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
    +     */
    +    sUriMatcher.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.
    +     */
    +    sUriMatcher.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 (sUriMatcher.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 is not recognized, you should do some error handling here.
    +        }
    +        // call the code to actually do the query
    +    }
    +
    +

    + Outra classe, {@link android.content.ContentUris}, fornece métodos convenientes para trabalhar + com a parte id das URIs de conteúdo. As classes {@link android.net.Uri} + e {@link android.net.Uri.Builder} contêm métodos convenientes para analisar objetos + {@link android.net.Uri} existentes e criar novos. +

    + + +

    Implementação da classe ContentProvider

    +

    + A instância {@link android.content.ContentProvider} gerencia o acesso + a um conjunto de dados estruturado lidando com solicitações de outros aplicativos. Todas as formas + de acesso ocasionalmente chamam {@link android.content.ContentResolver} que, em seguida, chama um método + concreto de {@link android.content.ContentProvider} para obter acesso. +

    +

    Métodos obrigatórios

    +

    + A classe abstrata {@link android.content.ContentProvider} define seis métodos abstratos + que devem ser implementados como parte das subclasses concretas. Todos esses métodos, exceto + {@link android.content.ContentProvider#onCreate() onCreate()}, são chamados por um aplicativo cliente + que está tentando acessar seu provedor de conteúdo: +

    +
    +
    + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + query()} +
    +
    + Recupere dados do provedor. Use os argumentos para selecionar a tabela + para consultar as linhas e colunas a retornar e a ordem de classificação do resultado. + Retorne os dados como um objeto {@link android.database.Cursor}. +
    +
    + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} +
    +
    + Insira uma nova linha no provedor. Use os argumentos para selecionar + a tabela de destino e obter os valores de coluna a usar. Retorne uma URI de conteúdo para + a linha recentemente inserida. +
    +
    + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} +
    +
    + Atualize as linhas existentes no provedor. Use os argumentos para selecionar a tabela e linhas + para atualizar e obter os valores de coluna atualizados. Retorne o número de linhas atualizadas. +
    +
    + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +
    +
    + Exclua linhas do provedor. Use os argumentos para selecionar a tabela e as linhas + a excluir. Retorne o número de linhas excluídas. +
    +
    + {@link android.content.ContentProvider#getType(Uri) getType()} +
    +
    + Retorne o tipo MIME correspondente a uma URI de conteúdo. Esse método é descrito com mais + detalhes na seção Implementação de tipos MIME do Provedor de Conteúdo. +
    +
    + {@link android.content.ContentProvider#onCreate() onCreate()} +
    +
    + Inicializar o provedor. O sistema Android chama esse método imediatamente + após criar o provedor. Observe que o provedor não é criado até que + um objeto {@link android.content.ContentResolver} tente acessá-lo. +
    +
    +

    + Observe que esses métodos têm a mesma assinatura dos métodos + {@link android.content.ContentResolver} de mesmo nome. +

    +

    + A implementação desses métodos deve levar em conta o seguinte: +

    +
      +
    • + Todos esses métodos, exceto {@link android.content.ContentProvider#onCreate() onCreate()}, + podem ser chamados por diversos encadeamentos simultaneamente, então devem ter encadeamento seguro. Para saber + mais sobre encadeamentos múltiplos, consulte o tópico + + Processos e encadeamentos. +
    • +
    • + Evite realizar longas operações em {@link android.content.ContentProvider#onCreate() + onCreate()}. Adie tarefas de inicialização até que sejam realmente necessárias. + A seção Implementação do método onCreate() + aborda o assunto mais detalhadamente. +
    • +
    • + Embora seja preciso implementar esses métodos, o código não precisa fazer nada além + de retornar o tipo de dados esperado. Por exemplo: você pode querer evitar que outros aplicativos + insiram dados em algumas tabelas. Para tanto, pode-se ignorar a chamada de + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} e retornar + 0. +
    • +
    +

    Implementação do método query()

    +

    + + O método {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} precisa retornar um objeto {@link android.database.Cursor} ou, se + falhar, gerar uma {@link java.lang.Exception}. Se você estiver usando um banco de dados SQLite + como armazenamento de dados, pode simplesmente retornar o {@link android.database.Cursor} retornado + por um dos métodos query() da classe {@link android.database.sqlite.SQLiteDatabase}. + Se a consulta não corresponder a nenhuma linha, deve-se retornar uma instância {@link android.database.Cursor} +cujo método {@link android.database.Cursor#getCount()} retorne 0. + Deve-se retornar null somente se ocorrer um erro interno durante o processo de consulta. +

    +

    + Se você não estiver usando um banco de dados SQLite como modo de armazenamento de dados, use uma das subclasses concretas + de {@link android.database.Cursor}. Por exemplo, a classe {@link android.database.MatrixCursor} + implementa um cursor em que cada linha é uma matriz de {@link java.lang.Object}. Com essa classe, + use {@link android.database.MatrixCursor#addRow(Object[]) addRow()} para adicionar uma nova linha. +

    +

    + Lembre-se de que o sistema Android deve ser capaz de se comunicar com {@link java.lang.Exception} + entre limites de processo. O Android pode fazer isso para as exceções a seguir, que podem ser úteis + para tratar de erros de consulta: +

    +
      +
    • + {@link java.lang.IllegalArgumentException} (é possível escolher lançar isso se o provedor + receber uma URI de conteúdo inválida) +
    • +
    • + {@link java.lang.NullPointerException} +
    • +
    +

    Implementação do método insert()

    +

    + O método {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} adiciona + uma nova linha à tabela apropriada usando os valores no argumento + {@link android.content.ContentValues}. Se um nome de coluna não estiver no argumento {@link android.content.ContentValues}, + talvez seja necessário fornecer um valor padrão para ele tanto no código do provedor quanto no esquema + do banco de dados. +

    +

    + Esse método deve retornar a URI de conteúdo da nova linha. Para construir isso, anexe o valor _ID + da nova linha (ou outra chave principal) à URI de conteúdo da tabela usando + {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}. +

    +

    Implementação do método delete()

    +

    + O método {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} + não precisa excluir linhas fisicamente do armazenamento de dados. Se você estiver usando um adaptador de sincronização + com o provedor, considere marcar uma linha excluída + com um sinalizador "excluir" em vez de removê-la totalmente. O adaptador de sincronização pode + verificar se há linhas excluídas e removê-las do servidor antes de excluí-las do provedor. +

    +

    Implementação do método update()

    +

    + O método {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} usa o mesmo argumento {@link android.content.ContentValues} usado por + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}, e os + mesmos argumentos selection e selectionArgs usados por + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} e + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()}. Isso deve permitir a reutilização do código entre esses métodos. +

    +

    Implementação do método onCreate()

    +

    + O sistema Android chama {@link android.content.ContentProvider#onCreate() + onCreate()} quando inicia o provedor. Nesse método, devem-se realizar apenas tarefas de inicialização + de execução rápida e adiar a criação do banco de dados e o carregamento dos dados até que o provedor + receba efetivamente uma solicitação de acesso aos dados. Se houver tarefas longas + em {@link android.content.ContentProvider#onCreate() onCreate()}, a inicialização do provedor + ficará lenta. Consequentemente, isso diminuirá a rapidez da resposta do provedor + a outros aplicativos. +

    +

    + Por exemplo: se você estiver usando um banco de dados SQLite, é possível criar + um novo objeto {@link android.database.sqlite.SQLiteOpenHelper} + em {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} + e, em seguida, criar as tabelas SQL na primeira vez em que o banco de dados for aberto. Para facilitar isso, + a primeira vez que chamar {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase + getWritableDatabase()}, ele automaticamente chamará + o método {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} +

    +

    + Os dois fragmentos a seguir demonstram a interação + entre {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} + e {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()}. O primeiro fragmento é a implementação de + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}. +

    +
    +public class ExampleProvider extends ContentProvider
    +
    +    /*
    +     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
    +     * in a following snippet.
    +     */
    +    private MainDatabaseHelper mOpenHelper;
    +
    +    // Defines the database name
    +    private static final String DBNAME = "mydb";
    +
    +    // Holds the database object
    +    private SQLiteDatabase db;
    +
    +    public boolean onCreate() {
    +
    +        /*
    +         * Creates a new helper object. This method always returns quickly.
    +         * Notice that the database itself isn't created or opened
    +         * until SQLiteOpenHelper.getWritableDatabase is called
    +         */
    +        mOpenHelper = new MainDatabaseHelper(
    +            getContext(),        // the application context
    +            DBNAME,              // the name of the database)
    +            null,                // uses the default SQLite cursor
    +            1                    // the version number
    +        );
    +
    +        return true;
    +    }
    +
    +    ...
    +
    +    // Implements the provider's insert method
    +    public Cursor insert(Uri uri, ContentValues values) {
    +        // Insert code here to determine which table to open, handle error-checking, and so forth
    +
    +        ...
    +
    +        /*
    +         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
    +         *
    +         */
    +        db = mOpenHelper.getWritableDatabase();
    +    }
    +}
    +
    +

    + O próximo fragmento é a implementação de + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()}, inclusive uma classe auxiliar: +

    +
    +...
    +// A string that defines the SQL statement for creating a table
    +private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    +    "main " +                       // Table's name
    +    "(" +                           // The columns in the table
    +    " _ID INTEGER PRIMARY KEY, " +
    +    " WORD TEXT"
    +    " FREQUENCY INTEGER " +
    +    " LOCALE TEXT )";
    +...
    +/**
    + * Helper class that actually creates and manages the provider's underlying data repository.
    + */
    +protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
    +
    +    /*
    +     * Instantiates an open helper for the provider's SQLite data repository
    +     * Do not do database creation and upgrade here.
    +     */
    +    MainDatabaseHelper(Context context) {
    +        super(context, DBNAME, null, 1);
    +    }
    +
    +    /*
    +     * Creates the data repository. This is called when the provider attempts to open the
    +     * repository and SQLite reports that it doesn't exist.
    +     */
    +    public void onCreate(SQLiteDatabase db) {
    +
    +        // Creates the main table
    +        db.execSQL(SQL_CREATE_MAIN);
    +    }
    +}
    +
    + + + +

    Implementação de tipos MIME de ContentProvider

    +

    + A classe {@link android.content.ContentProvider} tem dois métodos para retornar tipos MIME: +

    +
    +
    + {@link android.content.ContentProvider#getType(Uri) getType()} +
    +
    + Um dos métodos obrigatórios que devem ser implementados em qualquer provedor. +
    +
    + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +
    +
    + Um método que deve ser implementado se o provedor fornecer arquivos. +
    +
    +

    Tipos MIME para tabelas

    +

    + O método {@link android.content.ContentProvider#getType(Uri) getType()} retorna + uma {@link java.lang.String} em formato MIME que descreve o tipo de dados retornado pelo argumento + da URI de conteúdo. O argumento {@link android.net.Uri} pode ser um padrão em vez de uma URI específica; + nesse caso, deve-se retornar o tipo de dados associado a URIs de conteúdo que correspondam + ao padrão. +

    +

    + Para tipos de dados comuns como texto, HTML ou JPEG, + {@link android.content.ContentProvider#getType(Uri) getType()} deve retornar o tipo MIME + padrão daqueles dados. Há uma lista completa desse tipos de padrão + no site de + Tipos de mídia MIME IANA. +

    +

    + Para obter URIs de conteúdo que apontam para uma linha ou linhas de dados de tabela, + {@link android.content.ContentProvider#getType(Uri) getType()} deve retornar + um tipo MIME no formato MIME específico do fornecedor do Android: +

    +
      +
    • + Parte do tipo: vnd +
    • +
    • + Parte do subtipo: +
        +
      • + Se um padrão de URI se destinar a uma única linha: android.cursor.item/ +
      • +
      • + Se um padrão de URI se destinar a mais de uma linha: android.cursor.dir/ +
      • +
      +
    • +
    • + Parte específica do provedor: vnd.<name>.<type> +

      + Fornecem-se <name> e o <type>. + O valor <name> deve ser globalmente exclusivo + e o <type> deve ser exclusivo para o padrão de URI + correspondente. Uma boa escolha para <name> é o nome da empresa + ou alguma parte do nome do pacote do Android do seu aplicativo. Uma boa escolha para + <type> é uma string que identifique a tabela associada + à URI. +

      + +
    • +
    +

    + Por exemplo: se a autoridade de um provedor + for com.example.app.provider e ele expuser uma tabela chamada + table1, o tipo MIME de diversas 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

    +

    + Se o provedor oferecer arquivos, implemente + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}. + O método retorna uma matriz {@link java.lang.String} de tipos MIME para os arquivos que o provedor + pode retornar de uma dada URI de conteúdo. É preciso filtrar os tipos MIME oferecidos pelo argumento do filtro + de tipo MIME para retornar somente os tipos MIME que o cliente quer tratar. +

    +

    + Por exemplo: considere um provedor que ofereça imagens de foto como arquivos em formatos .jpg, + .gif e .png. + Se um aplicativo chama {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} com a string de filtro image/* (algo que + seja uma "imagem"), + o método {@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} deve retornar a matriz: +

    +
    +{ "image/jpeg", "image/png", "image/gif"}
    +
    +

    + Se o aplicativo estiver interessado apenas em arquivos .jpg, ele pode chamar + {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} com a string de filtro *\/jpeg + e {@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} deve retornar: +

    +{"image/jpeg"}
    +
    +

    + Se o provedor não oferecer nenhum tipo MIME solicitado na string de filtro, + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} + deve retornar null. +

    + + + +

    Implementação de uma classe de contrato

    +

    + Uma classe de contrato é uma classe public final que contém definições de constantes para + os nomes de coluna das URIs, dos tipos MIME e de outros metadados que pertencem ao provedor. A classe + estabelece um contrato entre o provedor e outros aplicativos, garantindo que o provedor + possa ser corretamente acessado mesmo se houver mudanças nos valores atuais de URIs, nomes de coluna + etc. +

    +

    + As classes de contrato também ajudam os desenvolvedores porque normalmente suas constantes têm nomes mnemônicos e, + por isso, os desenvolvedores têm menos chance de usar valores incorretos para nomes de coluna ou URIs. Já que é + uma classe, ela pode conter documentação Javadoc. Ambientes de desenvolvimento integrados, como + o Eclipse, podem preencher automaticamente os nomes de constantes da classe de contrato e exibir Javadoc para + as constantes. +

    +

    + Os desenvolvedores não podem acessar o arquivo de classe da classe de contrato do aplicativo, mas podem + compilá-lo estaticamente no aplicativo a partir de um arquivo .jar que você fornece. +

    +

    + A classe {@link android.provider.ContactsContract} e classes aninhadas são exemplos + de classes de contrato. +

    +

    Implementação de permissões do Provedor de Conteúdo

    +

    + Permissões e acesso, para todos os aspectos do sistema Android, são descritos detalhadamente no + tópico Permissões e segurança. + O tópico Armazenamento de dados também + descreve segurança e permissões em vigor para diversos tipos de armazenamento. + Em resumo, os pontos importantes são: +

    +
      +
    • + Por padrão, arquivos de dados armazenados no armazenamento interno do dispositivo são privados em relação + ao aplicativo e ao provedor. +
    • +
    • + Os bancos de dados {@link android.database.sqlite.SQLiteDatabase} criados são privados em relação + ao aplicativo e ao provedor. +
    • +
    • + Por padrão, arquivos de dados salvos em armazenamentos externos são públicos + e legíveis para todos. Não é possível usar um provedor de conteúdo para restringir o acesso a arquivos + em um armazenamento externo porque outros aplicativos podem usar chamadas de outra API para lê-los e gravar neles. +
    • +
    • + O método que chama a abertura ou criação de arquivos ou bancos de dados SQLite no armazenamento + interno do seu dispositivo podem fornecer acesso de leitura e gravação a todos os outros aplicativos. Se você + usar um arquivo ou banco de dados interno como o repositório do provedor e conceder-lhe + acesso "legível para todos" ou "gravável por todos", as permissões definidas para o provedor + no manifesto não protegerão os dados. O acesso padrão de arquivos e bancos de dados + no armazenamento interno é "privado" e, para o repositório do provedor, não é recomendável alterar isso. +
    • +
    +

    + Se você deseja usar permissões do provedor de conteúdo para controlar o acesso aos dados, deve + armazená-los em arquivos internos, bancos de dados SQLite ou em "nuvem" (por exemplo, + em um servidor remoto) e mantê-los privados em relação ao aplicativo. +

    +

    Implementação de permissões

    +

    + Todos os aplicativos podem ler ou gravar no provedor, mesmo que os dados em questão + sejam privados porque, por padrão, o provedor não tem permissões definidas. Para mudar isso, + defina permissões do provedor no arquivo de manifesto por meio de atributos ou elementos + filho do elemento + <provider>. É possível definir permissões que se apliquem a todo o provedor, + a determinadas tabelas, a registros específicos ou a todos os três. +

    +

    + As permissões são definidas para o provedor com um ou mais + elementos + <permission> no arquivo de manifesto. Para tornar + a permissão exclusiva para o provedor, use escopo de estilo Java para + o atributo + android:name. Por exemplo: nomeie a permissão de leitura + com.example.app.provider.permission.READ_PROVIDER. + +

    +

    + A lista a seguir descreve o escopo de permissões do provedor, começando + com as permissões que se aplicam a todo o provedor e seguindo para as mais específicas. + Permissões mais específicas têm precedência sobre as de escopo maior: +

    +
    +
    + Única permissão de leitura e gravação no nível do provedor +
    +
    + Uma permissão que controla os acessos 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. São especificadas + com os atributos + android:readPermission + e + android:writePermission + do elemento + <provider>. Elas têm precedência sobre a permissão exigida + por + android:permission. +
    +
    + Permissão no nível do caminho +
    +
    + Permissão de leitura, gravação ou leitura/gravação para uma URI de conteúdo no provedor. Especifica-se + cada URI que se deseja controlar + com um elemento filho + <path-permission> + do elemento + <provider>. Para cada URI de conteúdo, pode-se especificar + uma permissão de leitura/gravação, uma permissão de leitura, uma permissão de gravação ou as três. As permissões + de leitura e gravação têm precedência sobre a permissão de leitura/gravação. Além disso, + permissões no nível do caminho têm precedência sobre permissões no nível do provedor. +
    +
    + Permissão temporária +
    +
    + Nível de permissão que concede acesso temporário a um aplicativo mesmo que ele + não tenha as permissões normalmente exigidas. O recurso de acesso + temporário reduz o número de permissões que um aplicativo deve solicitar + no manifesto. Ao ativar permissões temporárias, os únicos aplicativos que precisam de + permissões "permanentes" para seu provedor são os que têm acesso contínuo + a todos os dados. +

    + Considere as permissões necessárias para implementar um provedor e um aplicativo de e-mail + ao permitir um aplicativo visualizador de imagens externas para exibir anexos de fotos + do provedor. Para conceder o acesso necessário ao visualizador de imagens sem as permissões exigidas, + configure permissões temporárias das URIs de conteúdo de fotos. Projete o aplicativo de e-mail + para que, quando o usuário quiser exibir uma foto, o aplicativo envie uma intenção + com a URI de conteúdo da foto e sinalizadores de permissão para o visualizador de imagens. O visualizador de imagens poderá, + então, consultar o provedor de e-mail para recuperar a foto mesmo que ele + não tenha a permissão normal de leitura para o provedor. +

    +

    + Para ativar permissões temporárias, defina + o atributo + android:grantUriPermissions + do elemento + <provider> ou adicione um ou mais + elementos filhos + <grant-uri-permission> + ao elemento + <provider>. Se forem usadas permissões temporárias, será necessário chamar + {@link android.content.Context#revokeUriPermission(Uri, int) + Context.revokeUriPermission()} sempre que remover suporte a URI de conteúdo do + provedor, e a URI de conteúdo será associada a uma permissão temporária. +

    +

    + O valor do atributo determina o nível de acessibilidade do provedor. + Se o atributo estiver definido como true, o sistema concederá permissão + temporária a todo o provedor, sobrepondo todas as outras permissões exigidas + pelas permissões no nível do provedor ou no nível do caminho. +

    +

    + Se esse sinalizador estiver definido como false, será necessário adicionar + elementos filhos + <grant-uri-permission> + ao elemento + <provider>. Cada elemento filho especifica a URI ou URIs + de conteúdo para as quais o acesso temporário é concedido. +

    +

    + Para delegar o acesso temporário a um aplicativo, a intenção deve conter + os sinalizadores {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}, + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} ou ambos. Eles + são definidos com o método {@link android.content.Intent#setFlags(int) setFlags()}. +

    +

    + Se o atributo + android:grantUriPermissions não estiver presente, presume-se que ele seja + false. +

    +
    +
    + + + + +

    O elemento <provider>

    +

    + Como os componentes {@link android.app.Activity} e {@link android.app.Service}, + a subclasse de {@link android.content.ContentProvider} + deve ser definida no arquivo de manifesto do aplicativo + pelo elemento + <provider>. O sistema Android obtém as seguintes informações + do elemento: +

    +
    + Autoridade + ({@code + android:authorities}) +
    +
    + Nomes simbólicos que identificam todo o provedor dentro do sistema. Esse + atributo é descrito com mais detalhes na seção + Projeto de URIs de conteúdo. +
    +
    + Nome da classe do provedor + ( +android:name + ) +
    +
    + A classe que implementa {@link android.content.ContentProvider}. Esta classe é + abordada com mais detalhes na seção + Implementação da classe ContentProvider. +
    +
    + Permissões +
    +
    + Atributos que especificam as permissões que outros aplicativos precisam ter para acessar + os dados do provedor: + +

    + As permissões e os atributos correspondentes são abordados com mais + detalhes na seção + Implementação de 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, + as características do processo do provedor e outras configurações de tempo de execução: +
      +
    • + + android:enabled: sinalizador que permite ao sistema iniciar o provedor. +
    • +
    • + + android:exported: sinalizador que permite a outros aplicativos usarem esse provedor. +
    • +
    • + + android:initOrder: a ordem em que esse provedor deve ser iniciado, + relativa a outros provedores no mesmo processo. +
    • +
    • + + android:multiProcess: sinalizador que permite ao sistema iniciar o provedor + no mesmo processo que o cliente chama. +
    • +
    • + + android:process: o nome do processo em que o provedor deve + ser executado. +
    • +
    • + + android:syncable: sinalizador que indica que os dados do provedor devem ser + sincronizados com os dados em um servidor. +
    • +
    +

    + os atributos estão totalmente documentados no tópico do guia de desenvolvimento + do elemento + + <provider>. +

    +
    +
    + Atributos informacionais +
    +
    + Um ícone e um rótulo opcionais para o provedor: +
      +
    • + + android:icon: um recurso desenhável contendo um ícone para o provedor. + O ícone aparece próximo ao rótulo do provedor na lista de aplicativos + em Configurações > Aplicativos > Todos. +
    • +
    • + + android:label: um rótulo informacional que descreve o provedor, + seus dados ou ambos. O rótulo aparece na lista de aplicativos + em Configurações > Aplicativos > Todos. +
    • +
    +

    + Os atributos estão totalmente documentados no tópico do guia de desenvolvimento + do elemento + <provider>. +

    +
    +
    + + +

    Intenções e acesso a dados

    +

    + Os aplicativos podem acessar um provedor de conteúdo indiretamente com uma {@link android.content.Intent}. + O aplicativo não chama nenhum método de {@link android.content.ContentResolver} + nem de {@link android.content.ContentProvider}. Em vez disso, ele envia uma intenção que inicia uma atividade, + que, em geral, é parte do aplicativo do próprio provedor. A atividade de destino é responsável + pela recuperação e exibição dos dados na IU. Conforme a ação na intenção, + a atividade de destino também pode levar o usuário a realizar modificações nos dados do provedor. + Uma intenção também pode conter dados "extras" que a atividade de destino exibe + na IU; o usuário terá, então, a opção de alterar esses dados antes de usá-los para modificar + os dados no provedor. +

    +

    + +

    +

    + Pode ser necessário usar acesso de intenções para ajudar a garantir a integridade deles. O provedor pode depender + de ter dados inseridos, atualizados e excluídos de acordo com a lógica de negócio rigorosamente definida. Se + for o caso, a permissão para que outros aplicativos modifiquem os dados diretamente pode levar + à invalidação dos dados. Se você deseja que os desenvolvedores usem o acesso via intenções, certifique-se de documentá-lo minuciosamente. + Explique por que o acesso via intenções pela IU do aplicativo é melhor do que tentar + modificar os dados com códigos. +

    +

    + O tratamento das intenções recebidas com a intenção de modificar os dados do provedor não é diferente + de tratar de outras intenções. Para saber mais sobre o uso de intenções, leia o tópico + Intenções e filtros de intenções. +

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/content-providers.jd new file mode 100644 index 0000000000000000000000000000000000000000..c9574f6b90c6e7613e3eda060bcdec0b0e2275b6 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=Provedores de conteúdo +@jd:body + +

    + Provedores de conteúdo gerenciam o acesso a um conjunto estruturado de dados. Eles encapsulam os +dados e fornecem mecanismos para definir a segurança dos dados. Provedores de conteúdo são a interface + padrão que conecta dados em um processo com código em execução em outro processo. +

    +

    + Quando desejar acessar dados em um provedor de conteúdo, você usa o + objeto {@link android.content.ContentResolver} no + {@link android.content.Context} do aplicativo para se comunicar com o provedor como cliente. + O objeto {@link android.content.ContentResolver} se comunica com o objeto provedor, uma + instância de uma classe que implementa {@link android.content.ContentProvider}. O objeto + provedor recebe solicitações de dados de clientes, realiza a ação solicitada e + devolve os resultados. +

    +

    + Não é preciso desenvolver o próprio provedor se você não pretende compartilhar seus dados com + outros aplicativos. No entanto, precisará do próprio provedor para fornecer sugestões de pesquisa + personalizada em seu aplicativo. Também precisará do próprio provedor se quiser copiar e colar + dados complexos ou arquivos de seu aplicativo em outros aplicativos. +

    +

    + O Android propriamente dito inclui provedores de conteúdo que gerenciam dados como áudio, vídeo, imagens e + informações de contato pessoais. Alguns deles estão listados na documentação de + referência do + pacote android.provider + . Com algumas restrições, esses provedores podem ser acessados por qualquer aplicativo + Android. +

    + Os tópicos a seguir descrevem provedores de conteúdo em mais detalhes: +

    +
    +
    + + Preceitos do provedor de conteúdo +
    +
    + Como acessar dados em um provedor de conteúdo quando os dados estão organizados em tabelas. +
    +
    + +Criação de um Provedor de conteúdo +
    +
    + Como criar o próprio provedor de conteúdo. +
    +
    + +Provedor de agenda +
    +
    + Como acessar o Provedor de agenda que é parte da plataforma Android. +
    +
    + +Provedor de contatos +
    +
    + Como acessar o Provedor de contatos que é parte da plataforma Android. +
    +
    diff --git a/docs/html-intl/intl/pt-br/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/pt-br/guide/topics/providers/document-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..25aab7aa7a9d247261553094926e8639f25c51a6 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=Estrutura de acesso ao armazenamento +@jd:body + + + +

    O Android 4.4 (API de nível 19) introduz a Estrutura de Acesso ao Armazenamento (SAF). A SAF + simplifica para os usuários a busca e abertura de documentos, imagens e outros arquivos +dentre todos os provedores de armazenamento de documentos de preferência. A interface gráfica padrão é fácil de usar +e permite aos usuários buscar arquivos e acessar arquivos recentes de modo coerente em todos os aplicativos e provedores.

    + +

    Serviços de armazenamento local ou em nuvem podem participar desse ecossistema por meio da implementação +de um {@link android.provider.DocumentsProvider} que encapsula os serviços. Aplicativos +clientes que precisam acessar documentos de um provedor podem integrar-se com a SAF com apenas algumas +linhas de código.

    + +

    A SAF contém:

    + +
      +
    • Provedor de documentos — Provedor de conteúdo que oferece +um serviço de armazenamento (como o Google Drive) para exibir os arquivos que gerencia. O provedor de documentos +é implementado como uma subclasse da classe {@link android.provider.DocumentsProvider}. +O esquema do provedor de documento se baseia em uma hierarquia de arquivo tradicional, +embora o modo de armazenamento físico de dados do provedor de documentos seja definido pelo programador. +A plataforma do Android contém diversos provedores de documento embutidos, como +Downloads, Imagens e Vídeos.
    • + +
    • Aplicativo cliente — Aplicativo personalizado que chama a intenção +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} +e/ou {@link android.content.Intent#ACTION_CREATE_DOCUMENT} e recebe +os arquivos retornados pelos provedores de documentos.
    • + +
    • Seletor — IU de sistema que permite aos usuários acessar documentos de todos +os provedores de documentos que satisfazem os critérios de busca do aplicativo cliente.
    • +
    + +

    A seguir há alguns recursos oferecidos pela SAF:

    +
      +
    • Permitir que usuários busquem conteúdo de todos os provedores de documentos, não somente de um único aplicativo.
    • +
    • Possibilitar ao aplicativo a obtenção de acesso persistente e de longo prazo +a documentos de propriedade de um provedor de documentos. Por meio deste acesso, os usuários podem adicionar, editar, + salvar e excluir arquivos no provedor.
    • +
    • É compatível com diversas contas de usuário e raízes transitórias como provedores +de armazenamento USB, que só aparecem se o dispositivo estiver plugado.
    • +
    + +

    Visão geral

    + +

    A SAF consiste em um provedor de conteúdo que é uma +subclasse da classe {@link android.provider.DocumentsProvider}. Dentro de um provedor de documentos, os dados +são estruturados como uma hierarquia de arquivo tradicional:

    +

    data model

    +

    Figura 1. Modelo de dados do provedor de documentos Uma Raiz aponta para um único Documento, +que então inicia o fan-out de toda a árvore.

    + +

    Observe o seguinte:

    +
      + +
    • Cada provedor de documentos relata uma ou mais +"raízes", que são pontos de partida na exploração de uma árvore de documentos. +Cada raiz tem um {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID} exclusivo +e ele aponta para um documento (um diretório) +representando o conteúdo sob essa raiz. +As raízes têm um projeto dinâmico para oferecer compatibilidade a casos de uso como diversas contas, +dispositivos de armazenamento USB transitórios ou login/logout do usuário.
    • + +
    • Sob cada raiz há um documento único. Esse documento indica 1 a N documentos, +cada um deles, por sua vez, podem indicar 1 a N documentos.
    • + +
    • Cada back-end de armazenamento apresenta +arquivos e diretórios individuais referenciando-os com um +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} exclusivo. +IDs de documentos devem ser exclusivos e não podem mudar depois de emitidos, pois são usados para concessões persistentes +da URI em reinicializações do dispositivo.
    • + + +
    • Documentos podem ser um arquivo ou um diretório que pode ser aberto (com um tipo MIME específico) + contendo documentos adicionais (com +o tipo MIME{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR}).
    • + +
    • Cada documento tem diferentes recursos, como descrito por +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}. +Por exemplo,{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}, +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE} +e {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}. +O mesmo {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} pode ser +incluído em diversos diretórios.
    • +
    + +

    Controle de fluxo

    +

    Como indicado anteriormente, o modelo de dados do provedor de documentos se baseia +em uma hierarquia de arquivo tradicional. Contudo, é possível armazenar os dados fisicamente como quiser desde +que eles possam ser acessados pela API {@link android.provider.DocumentsProvider}. Por exemplo: seria +possível usar armazenamento em nuvem com base em tag para os dados.

    + +

    A figura 2 ilustra um exemplo de um aplicativo de foto que usa a SAF +para acessar dados armazenados:

    +

    app

    + +

    Figura 2. Fluxo da estrutura de acesso ao armazenamento

    + +

    Observe o seguinte:

    +
      + +
    • Na SAF, provedores e clientes não interagem +diretamente. O cliente solicita permissão para interagir +com arquivos (ou seja, para ler, editar, criar ou excluir arquivos).
    • + +
    • A interação começa quando um aplicativo (neste exemplo, um aplicativo de foto) dispara a intenção +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} ou {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. A intenção pode conter filtros +para refinar ainda mais os critérios — por exemplo: "quero todos os arquivos que podem ser abertos +e que tenham o tipo MIME de tal imagem".
    • + +
    • Ao disparar a intenção, o seletor do sistema contata cada provedor registrado +e exibe as raízes de conteúdo correspondentes ao usuário.
    • + +
    • O seletor fornece aos usuários uma interface padrão para acessar documentos, +embora os provedores de documentos subjacentes possam ser bem diferentes. Por exemplo: a figura 2 +exibe um provedor do Google Drive, um provedor USB e um provedor de nuvem.
    • +
    + +

    A figura 3 exibe um seletor em que um usuário em busca de imagens selecionou +uma conta do Google Drive:

    + +

    picker

    + +

    Figura 3. Seletor

    + +

    Quando o usuário seleciona o Google Drive, as imagens são exibidas como ilustrado +na figura 4. Desse momento em diante, o usuário poderá interagir com eles de todos os modos +compatíveis com o provedor e o aplicativo cliente. + +

    picker

    + +

    Figura 4. Imagens

    + +

    Programação de um aplicativo cliente

    + +

    No Android 4.3 e em versões anteriores, para o aplicativo recuperar um arquivo de outro +aplicativo, ele precisa chamar uma intenção como {@link android.content.Intent#ACTION_PICK} +ou {@link android.content.Intent#ACTION_GET_CONTENT}. Em seguida, o usuário deve selecionar +um único aplicativo do qual deseja retirar um arquivo e o aplicativo selecionado deve fornecer uma interface +do usuário para a busca e a seleção dos arquivos disponíveis.

    + +

    No Android 4.4 e em versões posteriores, existe a opção adicional de usar +a intenção {@link android.content.Intent#ACTION_OPEN_DOCUMENT}, +que exibe uma IU do seletor controlada pelo sistema que permite ao usuário +buscar todos os arquivos que outros aplicativos disponibilizaram. Nessa IU exclusiva, +o usuário pode selecionar um arquivo de qualquer aplicativo compatível.

    + +

    {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +não substitui {@link android.content.Intent#ACTION_GET_CONTENT}. +O uso de cada um deles depende da necessidade do aplicativo:

    + +
      +
    • Use {@link android.content.Intent#ACTION_GET_CONTENT} se deseja que o aplicativo +simplesmente leia ou importe dados. Nessa abordagem, o aplicativo importa uma cópia dos dados, +assim como um arquivo de imagem.
    • + +
    • Use {@link android.content.Intent#ACTION_OPEN_DOCUMENT} se deseja que o aplicativo +tenha acesso persistente e de longo prazo a documentos de propriedade de um provedor +de documentos. Um exemplo seria um aplicativo de edição de fotos que permite aos usuários editar +imagens armazenadas em um provedor de documentos.
    • + +
    + + +

    Esta seção descreve como programar aplicativos clientes com base nas intenções +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} +e {@link android.content.Intent#ACTION_CREATE_DOCUMENT}.

    + + + + +

    +O fragmento a seguir usa {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +para buscar provedores de documentos +que contenham arquivos de imagem:

    + +
    private static final int READ_REQUEST_CODE = 42;
    +...
    +/**
    + * Fires an intent to spin up the "file chooser" UI and select an image.
    + */
    +public void performFileSearch() {
    +
    +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    +    // browser.
    +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as a
    +    // file (as opposed to a list of contacts or timezones)
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Filter to show only images, using the image MIME data type.
    +    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
    +    // To search for all documents available via installed storage providers,
    +    // it would be "*/*".
    +    intent.setType("image/*");
    +
    +    startActivityForResult(intent, READ_REQUEST_CODE);
    +}
    + +

    Observe o seguinte:

    +
      +
    • Quando o aplicativo dispara a intenção +{@link android.content.Intent#ACTION_OPEN_DOCUMENT}, ele aciona um seletor que exibe todos os provedores de documentos compatíveis.
    • + +
    • A adição da categoria {@link android.content.Intent#CATEGORY_OPENABLE} +à intenção filtra os resultados, que exibem somente documentos que podem ser abertos, como os arquivos de imagem.
    • + +
    • A declaração {@code intent.setType("image/*")} filtra ainda mais +para exibir somente documentos que têm o tipo de dados MIME da imagem.
    • +
    + +

    Processamento de resultados

    + +

    Quando o usuário seleciona um documento no seletor, +{@link android.app.Activity#onActivityResult onActivityResult()} é chamado. +A URI direcionada ao documento selecionado está presente no parâmetro +{@code resultData}. Extraia a URI usando {@link android.content.Intent#getData getData()}. +Depois, será possível usá-la para recuperar o documento que o usuário deseja. Por +exemplo:

    + +
    @Override
    +public void onActivityResult(int requestCode, int resultCode,
    +        Intent resultData) {
    +
    +    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    +    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    +    // response to some other intent, and the code below shouldn't run at all.
    +
    +    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    +        // The document selected by the user won't be returned in the intent.
    +        // Instead, a URI to that document will be contained in the return intent
    +        // provided to this method as a parameter.
    +        // Pull that URI using resultData.getData().
    +        Uri uri = null;
    +        if (resultData != null) {
    +            uri = resultData.getData();
    +            Log.i(TAG, "Uri: " + uri.toString());
    +            showImage(uri);
    +        }
    +    }
    +}
    +
    + +

    Examinação de metadados de documentos

    + +

    Quando tiver a URI de um documento, você terá acesso aos seus metadados. Este +fragmento apanha os metadados de um documento especificado pela URI e registra-os:

    + +
    public void dumpImageMetaData(Uri uri) {
    +
    +    // The query, since it only applies to a single document, will only return
    +    // one row. There's no need to filter, sort, or select fields, since we want
    +    // all fields for one document.
    +    Cursor cursor = getActivity().getContentResolver()
    +            .query(uri, null, null, null, null, null);
    +
    +    try {
    +    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    +    // "if there's anything to look at, look at it" conditionals.
    +        if (cursor != null && cursor.moveToFirst()) {
    +
    +            // Note it's called "Display Name".  This is
    +            // provider-specific, and might not necessarily be the file name.
    +            String displayName = cursor.getString(
    +                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    +            Log.i(TAG, "Display Name: " + displayName);
    +
    +            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
    +            // If the size is unknown, the value stored is null.  But since an
    +            // int can't be null in Java, the behavior is implementation-specific,
    +            // which is just a fancy term for "unpredictable".  So as
    +            // a rule, check if it's null before assigning to an int.  This will
    +            // happen often:  The storage API allows for remote files, whose
    +            // size might not be locally known.
    +            String size = null;
    +            if (!cursor.isNull(sizeIndex)) {
    +                // Technically the column stores an int, but cursor.getString()
    +                // will do the conversion automatically.
    +                size = cursor.getString(sizeIndex);
    +            } else {
    +                size = "Unknown";
    +            }
    +            Log.i(TAG, "Size: " + size);
    +        }
    +    } finally {
    +        cursor.close();
    +    }
    +}
    +
    + +

    Abertura de um documento

    + +

    Assim que tiver a URI de um documento, você poderá abri-lo ou fazer o que +quiser com ele.

    + +

    Bitmap

    + +

    A seguir há um exemplo de como abrir um {@link android.graphics.Bitmap}:

    + +
    private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    +    ParcelFileDescriptor parcelFileDescriptor =
    +            getContentResolver().openFileDescriptor(uri, "r");
    +    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    +    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    +    parcelFileDescriptor.close();
    +    return image;
    +}
    +
    + +

    Observe que não se deve realizar essa operação no encadeamento da IU. Faça isso +em segundo plano usando {@link android.os.AsyncTask}. Assim que abrir o bitmap, você +poderá exibi-lo em uma {@link android.widget.ImageView}. +

    + +

    Obter uma InputStream

    + +

    Abaixo há um exemplo de como obter uma {@link java.io.InputStream} dessa URI. +Neste fragmento, as linhas do arquivo estão sendo lidas em uma string:

    + +
    private String readTextFromUri(Uri uri) throws IOException {
    +    InputStream inputStream = getContentResolver().openInputStream(uri);
    +    BufferedReader reader = new BufferedReader(new InputStreamReader(
    +            inputStream));
    +    StringBuilder stringBuilder = new StringBuilder();
    +    String line;
    +    while ((line = reader.readLine()) != null) {
    +        stringBuilder.append(line);
    +    }
    +    fileInputStream.close();
    +    parcelFileDescriptor.close();
    +    return stringBuilder.toString();
    +}
    +
    + +

    Criação de um novo documento

    + +

    O aplicativo pode criar um novo documento em um provedor de documentos usando +a intenção +{@link android.content.Intent#ACTION_CREATE_DOCUMENT}. Para criar um arquivo, deve-se fornecer um tipo MIME e um nome para o arquivo à intenção +e ativá-la com um código de solicitação exclusivo. O resto você não precisa fazer:

    + + +
    +// Here are some examples of how you might call this method.
    +// The first parameter is the MIME type, and the second parameter is the name
    +// of the file you are creating:
    +//
    +// createFile("text/plain", "foobar.txt");
    +// createFile("image/png", "mypicture.png");
    +
    +// Unique request code.
    +private static final int WRITE_REQUEST_CODE = 43;
    +...
    +private void createFile(String mimeType, String fileName) {
    +    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as
    +    // a file (as opposed to a list of contacts or timezones).
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Create a file with the requested MIME type.
    +    intent.setType(mimeType);
    +    intent.putExtra(Intent.EXTRA_TITLE, fileName);
    +    startActivityForResult(intent, WRITE_REQUEST_CODE);
    +}
    +
    + +

    Ao criar um novo documento, é possível obter sua URI +em {@link android.app.Activity#onActivityResult onActivityResult()} para que +seja possível continuar a gravar nele.

    + +

    Exclusão de um documento

    + +

    Se você tem uma URI de um documento +e os {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} do documento + contêm +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE}, +você pode excluir o documento. Por exemplo:

    + +
    +DocumentsContract.deleteDocument(getContentResolver(), uri);
    +
    + +

    Edição de um documento

    + +

    Você pode usar a SAF para editar o documento de texto no local em que está armazenado. +Esse fragmento dispara +a intenção {@link android.content.Intent#ACTION_OPEN_DOCUMENT} e usa +a categoria {@link android.content.Intent#CATEGORY_OPENABLE} para exibir somente +documentos que possam ser abertos. Ela filtra ainda mais para exibir somente arquivos de texto:

    + +
    +private static final int EDIT_REQUEST_CODE = 44;
    +/**
    + * Open a file for writing and append some text to it.
    + */
    + private void editDocument() {
    +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    +    // file browser.
    +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as a
    +    // file (as opposed to a list of contacts or timezones).
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Filter to show only text files.
    +    intent.setType("text/plain");
    +
    +    startActivityForResult(intent, EDIT_REQUEST_CODE);
    +}
    +
    + +

    Em seguida, de {@link android.app.Activity#onActivityResult onActivityResult()} +(consulte Processar resultados), você pode chamar o código para realizar a edição. +O fragmento a seguir obtém um {@link java.io.FileOutputStream} +do {@link android.content.ContentResolver}. Por padrão, ele usa o modo de "gravação". +Recomenda-se solicitar a menor quantidade de acesso necessária, portanto não solicite +acesso de leitura e programação se só for necessária a programação:

    + +
    private void alterDocument(Uri uri) {
    +    try {
    +        ParcelFileDescriptor pfd = getActivity().getContentResolver().
    +                openFileDescriptor(uri, "w");
    +        FileOutputStream fileOutputStream =
    +                new FileOutputStream(pfd.getFileDescriptor());
    +        fileOutputStream.write(("Overwritten by MyCloud at " +
    +                System.currentTimeMillis() + "\n").getBytes());
    +        // Let the document provider know you're done by closing the stream.
    +        fileOutputStream.close();
    +        pfd.close();
    +    } catch (FileNotFoundException e) {
    +        e.printStackTrace();
    +    } catch (IOException e) {
    +        e.printStackTrace();
    +    }
    +}
    + +

    Manutenção de permissões

    + +

    Quando o aplicativo abre um arquivo para leitura ou programação, o sistema lhe fornece +uma concessão da permissão da URI para tal arquivo. Ela vigora até a reinicialização do dispositivo do usuário. +Contudo, suponhamos que seu aplicativo seja de edição de imagens e você queira que os usuários +acessem as últimas 5 imagens que editaram diretamente do aplicativo. Se o dispositivo do usuário +reiniciou, você teria que enviar o usuário de volta ao seletor do sistema para encontrar +os arquivos, o que, obviamente, não é o ideal.

    + +

    Para evitar que isso aconteça, você pode manter as permissões que o sistema +forneceu ao aplicativo. Efetivamente, o aplicativo "toma" a concessão de permissão da URI persistente +que o sistema está oferecendo. Isso concede ao usuário um acesso contínuo aos arquivos +por meio do aplicativo mesmo se o dispositivo for reiniciado:

    + + +
    final int takeFlags = intent.getFlags()
    +            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
    +            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    +// Check for the freshest data.
    +getContentResolver().takePersistableUriPermission(uri, takeFlags);
    + +

    Há uma etapa final. Você pode ter salvo as URIs +mais recentes acessadas pelo seu aplicativo, mas elas não serão mais válidas — outro aplicativo +pode ter excluído ou modificado um documento. Portanto, deve-se sempre chamar +{@code getContentResolver().takePersistableUriPermission()} para verificar +se há dados mais recentes.

    + +

    Criação de um provedor de documentos personalizado

    + +

    +Se você está desenvolvimento um aplicativo que fornece serviços de armazenamento para arquivos (como +um serviço de armazenamento em nuvem), é possível disponibilizar os arquivos +pela SAF criando um provedor de documentos personalizado. Esta seção mostra +como fazê-lo.

    + + +

    Manifesto

    + +

    Para implementar um provedor de documentos personalizado, adicione ao manifesto +do aplicativo:

    +
      + +
    • Um alvo de API de nível 19 ou posterior.
    • + +
    • Um elemento <provider> que declare o provedor de armazenamento +personalizado.
    • + +
    • O nome do provedor, que é o nome da classe, inclusive o nome do pacote. +Por exemplo: com.example.android.storageprovider.MyCloudProvider.
    • + +
    • O nome da autoridade, que é o nome do pacote (neste exemplo, +com.example.android.storageprovider) e o tipo de provedor de conteúdo +(documents). Por exemplo: {@code com.example.android.storageprovider.documents}.
    • + +
    • O atributo android:exported definido como "true". +É necessário exportar o provedor para que outros aplicativos possam vê-lo.
    • + +
    • O atributo android:grantUriPermissions definido como +"true". Essa configuração permite ao sistema conceder acesso ao conteúdo +no provedor a outros aplicativos. Para ver como manter uma concessão +para determinado documento, consulte Manutenção de permissões.
    • + +
    • A permissão {@code MANAGE_DOCUMENTS}. Por padrão, um provedor está disponível +para todos. A adição dessa permissão restringirá o provedor em relação ao sistema. +Essa restrição é importante em termos de segurança.
    • + +
    • O atributo {@code android:enabled} definido como um valor booleano determinado em um arquivo +de recursos. Esse atributo visa desativar o provedor em dispositivos que executam o Android 4.3 ou versões anteriores. +Por exemplo: {@code android:enabled="@bool/atLeastKitKat"}. Além +disso, para incluir esse atributo no manifesto, deve-se fazer o seguinte: +
        +
      • No arquivo de recursos {@code bool.xml} em {@code res/values/}, adicione +esta linha:
        <bool name="atLeastKitKat">false</bool>
      • + +
      • No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione +esta linha:
        <bool name="atLeastKitKat">true</bool>
      • +
    • + +
    • Um filtro de intenções que contenha +a ação {@code android.content.action.DOCUMENTS_PROVIDER} para que o provedor +apareça no seletor quando o sistema procurar provedores.
    • + +
    +

    Abaixo há alguns excertos de amostra de um manifesto que contém um provedor:

    + +
    <manifest... >
    +    ...
    +    <uses-sdk
    +        android:minSdkVersion="19"
    +        android:targetSdkVersion="19" />
    +        ....
    +        <provider
    +            android:name="com.example.android.storageprovider.MyCloudProvider"
    +            android:authorities="com.example.android.storageprovider.documents"
    +            android:grantUriPermissions="true"
    +            android:exported="true"
    +            android:permission="android.permission.MANAGE_DOCUMENTS"
    +            android:enabled="@bool/atLeastKitKat">
    +            <intent-filter>
    +                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
    +            </intent-filter>
    +        </provider>
    +    </application>
    +
    +</manifest>
    + +

    Compatibilidade com dispositivos que executam Android 4.3 ou anterior

    + +

    +A intenção {@link android.content.Intent#ACTION_OPEN_DOCUMENT} está disponível somente +em dispositivos que executam o Android 4.4 ou posteriores. +Se você deseja que o aplicativo seja compatível com {@link android.content.Intent#ACTION_GET_CONTENT} +para adaptar-se a dispositivos que executam o Android 4.3 ou versões anteriores, é necessário +desativar o filtro de intenção {@link android.content.Intent#ACTION_GET_CONTENT} +no manifesto para dispositivos que executam Android 4.4 ou versões posteriores. +Um provedor de documentos e {@link android.content.Intent#ACTION_GET_CONTENT} devem ser avaliados +de forma mutuamente exclusiva. Se houver compatibilidade com ambos simultaneamente, o aplicativo +aparecerá duas vezes na IU do seletor do sistema, oferecendo dois meios de acesso +diferentes aos dados armazenados. Isso pode confundir os usuários.

    + +

    A seguir apresenta-se a forma recomendada de desativar +o filtro de intenções {@link android.content.Intent#ACTION_GET_CONTENT} para dispositivos +que executam o Android 4.4 ou versões posteriores:

    + +
      +
    1. No arquivo de recursos {@code bool.xml} em {@code res/values/}, adicione +esta linha:
      <bool name="atMostJellyBeanMR2">true</bool>
    2. + +
    3. No arquivo de recursos {@code bool.xml} em {@code res/values-v19/}, adicione +esta linha:
      <bool name="atMostJellyBeanMR2">false</bool>
    4. + +
    5. Adicione +um alias +de atividade para desativar o filtro de intenções +{@link android.content.Intent#ACTION_GET_CONTENT} para versões 4.4 (API de nível 19) e posteriores. Por exemplo: + +
      +<!-- This activity alias is added so that GET_CONTENT intent-filter
      +     can be disabled for builds on API level 19 and higher. -->
      +<activity-alias android:name="com.android.example.app.MyPicker"
      +        android:targetActivity="com.android.example.app.MyActivity"
      +        ...
      +        android:enabled="@bool/atMostJellyBeanMR2">
      +    <intent-filter>
      +        <action android:name="android.intent.action.GET_CONTENT" />
      +        <category android:name="android.intent.category.OPENABLE" />
      +        <category android:name="android.intent.category.DEFAULT" />
      +        <data android:mimeType="image/*" />
      +        <data android:mimeType="video/*" />
      +    </intent-filter>
      +</activity-alias>
      +
      +
    6. +
    +

    Contratos

    + +

    Normalmente, ao criar um provedor de conteúdo personalizado, uma das tarefas +é implementar classes de contrato, como descrito +no guia dos desenvolvedores de +Provedores de conteúdo. Classe de contrato é uma classe {@code public final} +que contém definições constantes das URIs, nomes de coluna, tipos MIME +e outros metadados que pertencem ao provedor. A SAF +fornece essas classes de contrato, portanto não é necessário +programá-las:

    + +
      +
    • {@link android.provider.DocumentsContract.Document}
    • +
    • {@link android.provider.DocumentsContract.Root}
    • +
    + +

    Por exemplo, eis as colunas que podem retornar em um cursor +ao consultar documentos ou a raiz do provedor de documentos:

    + +
    private static final String[] DEFAULT_ROOT_PROJECTION =
    +        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
    +        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
    +        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
    +        Root.COLUMN_AVAILABLE_BYTES,};
    +private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
    +        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
    +        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
    +        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
    +
    + +

    Subclasse DocumentsProvider

    + +

    A próxima etapa na criação de um provedor de documentos personalizado é atribuir uma subclasse à +classe {@link android.provider.DocumentsProvider} abstrata. No mínimo, é necessário +implementar os seguintes métodos:

    + +
      +
    • {@link android.provider.DocumentsProvider#queryRoots queryRoots()}
    • + +
    • {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
    • + +
    • {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    • + +
    • {@link android.provider.DocumentsProvider#openDocument openDocument()}
    • +
    + +

    Esses são os únicos métodos que obrigatoriamente devem ser implementados, mas há +muitos outros que podem ser usados. Consulte {@link android.provider.DocumentsProvider} +para ver mais detalhes.

    + +

    Implementação de queryRoots

    + +

    A implementação de {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} deve retornar um {@link android.database.Cursor} que aponta para todos +os diretórios raiz dos provedores de documentos, usando colunas definidas +em {@link android.provider.DocumentsContract.Root}.

    + +

    No fragmento a seguir, o parâmetro {@code projection} representa +os campos específicos que o autor da chamada quer receber de volta. O fragmento cria um novo cursor +e adiciona-lhe uma linha — uma raiz, um diretório de nível superior, como +Downloads ou Imagens. A maioria dos provedores tem somente uma raiz. É possível ter mais de uma, +por exemplo, no caso de diversas contas de usuário. Nesse caso, adicione somente +uma segunda linha ao cursor.

    + +
    +@Override
    +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    +
    +    // Create a cursor with either the requested fields, or the default
    +    // projection if "projection" is null.
    +    final MatrixCursor result =
    +            new MatrixCursor(resolveRootProjection(projection));
    +
    +    // If user is not logged in, return an empty root cursor.  This removes our
    +    // provider from the list entirely.
    +    if (!isUserLoggedIn()) {
    +        return result;
    +    }
    +
    +    // It's possible to have multiple roots (e.g. for multiple accounts in the
    +    // same app) -- just add multiple cursor rows.
    +    // Construct one row for a root called "MyCloud".
    +    final MatrixCursor.RowBuilder row = result.newRow();
    +    row.add(Root.COLUMN_ROOT_ID, ROOT);
    +    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
    +
    +    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
    +    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
    +    // recently used documents will show up in the "Recents" category.
    +    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
    +    // shares.
    +    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
    +            Root.FLAG_SUPPORTS_RECENTS |
    +            Root.FLAG_SUPPORTS_SEARCH);
    +
    +    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
    +    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
    +
    +    // This document id cannot change once it's shared.
    +    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
    +
    +    // The child MIME types are used to filter the roots and only present to the
    +    //  user roots that contain the desired type somewhere in their file hierarchy.
    +    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
    +    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
    +    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
    +
    +    return result;
    +}
    + +

    Implementação de queryChildDocuments

    + +

    A implementação +de {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} +deve retornar um {@link android.database.Cursor} que aponta para todos os arquivos +no diretório especificado com colunas definidas em +{@link android.provider.DocumentsContract.Document}.

    + +

    Esse método é chamado quando uma raiz do aplicativo é escolhida na IU do seletor. +Ele coleta os documentos filhos de um diretório na raiz. Ele pode ser chamado em qualquer nível +na hierarquia de arquivos, não somente na raiz. Esse fragmento +cria um novo cursor com as colunas solicitadas e, em seguida, adiciona informações ao cursor +sobre cada filho imediato no diretório pai. +O filho pode ser uma imagem, outro diretório — qualquer arquivo:

    + +
    @Override
    +public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    +                              String sortOrder) throws FileNotFoundException {
    +
    +    final MatrixCursor result = new
    +            MatrixCursor(resolveDocumentProjection(projection));
    +    final File parent = getFileForDocId(parentDocumentId);
    +    for (File file : parent.listFiles()) {
    +        // Adds the file's display name, MIME type, size, and so on.
    +        includeFile(result, null, file);
    +    }
    +    return result;
    +}
    +
    + +

    Implementação de queryDocument

    + +

    A implementação de +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} +deve retornar um {@link android.database.Cursor} que aponta para o arquivo especificado +com colunas definidas em {@link android.provider.DocumentsContract.Document}. +

    + +

    O método {@link android.provider.DocumentsProvider#queryDocument queryDocument()} +retorna as mesmas informações passadas em +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}, +mas para um arquivo específico.

    + + +
    @Override
    +public Cursor queryDocument(String documentId, String[] projection) throws
    +        FileNotFoundException {
    +
    +    // Create a cursor with the requested projection, or the default projection.
    +    final MatrixCursor result = new
    +            MatrixCursor(resolveDocumentProjection(projection));
    +    includeFile(result, documentId, null);
    +    return result;
    +}
    +
    + +

    Implementação de openDocument

    + +

    Deve-se implementar {@link android.provider.DocumentsProvider#openDocument +openDocument()} para retornar um {@link android.os.ParcelFileDescriptor} que represente +o arquivo especificado. Outros aplicativos podem usar o {@link android.os.ParcelFileDescriptor} retornado +para transmitir dados. O sistema chama esse método quando o usuário seleciona um arquivo +e o aplicativo cliente solicita acesso a ele chamando +{@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}. +Por exemplo:

    + +
    @Override
    +public ParcelFileDescriptor openDocument(final String documentId,
    +                                         final String mode,
    +                                         CancellationSignal signal) throws
    +        FileNotFoundException {
    +    Log.v(TAG, "openDocument, mode: " + mode);
    +    // It's OK to do network operations in this method to download the document,
    +    // as long as you periodically check the CancellationSignal. If you have an
    +    // extremely large file to transfer from the network, a better solution may
    +    // be pipes or sockets (see ParcelFileDescriptor for helper methods).
    +
    +    final File file = getFileForDocId(documentId);
    +
    +    final boolean isWrite = (mode.indexOf('w') != -1);
    +    if(isWrite) {
    +        // Attach a close listener if the document is opened in write mode.
    +        try {
    +            Handler handler = new Handler(getContext().getMainLooper());
    +            return ParcelFileDescriptor.open(file, accessMode, handler,
    +                        new ParcelFileDescriptor.OnCloseListener() {
    +                @Override
    +                public void onClose(IOException e) {
    +
    +                    // Update the file with the cloud server. The client is done
    +                    // writing.
    +                    Log.i(TAG, "A file with id " +
    +                    documentId + " has been closed!
    +                    Time to " +
    +                    "update the server.");
    +                }
    +
    +            });
    +        } catch (IOException e) {
    +            throw new FileNotFoundException("Failed to open document with id "
    +            + documentId + " and mode " + mode);
    +        }
    +    } else {
    +        return ParcelFileDescriptor.open(file, accessMode);
    +    }
    +}
    +
    + +

    Segurança

    + +

    Suponha que o provedor de documentos seja um serviço de armazenamento em nuvem protegido por senha +e que você queira certificar-se de que os usuários estejam conectados antes de iniciar o compartilhamento dos arquivos. +O que o aplicativo deve fazer se o usuário não estiver conectado? A solução é retornar +zero raiz na implementação de {@link android.provider.DocumentsProvider#queryRoots +queryRoots()}, ou seja, um cursor de raiz vazio:

    + +
    +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    +...
    +    // If user is not logged in, return an empty root cursor.  This removes our
    +    // provider from the list entirely.
    +    if (!isUserLoggedIn()) {
    +        return result;
    +}
    +
    + +

    A outra etapa é chamar {@code getContentResolver().notifyChange()}. +Lembra-se do {@link android.provider.DocumentsContract}? Estamos usando-o para criar +esta URI. O fragmento a seguir pede ao sistema que consulte as raízes +do provedor de documentos sempre que o status de login do usuário mudar. Se o usuário não estiver +conectado, uma chamada de {@link android.provider.DocumentsProvider#queryRoots queryRoots()} retornará um +cursor vazio, como exibido anteriormente. Isso garante que os documentos do provedor estejam +disponíveis somente se o usuário tiver acesso ao provedor.

    + +
    private void onLoginButtonClick() {
    +    loginOrLogout();
    +    getContentResolver().notifyChange(DocumentsContract
    +            .buildRootsUri(AUTHORITY), null);
    +}
    +
    \ No newline at end of file diff --git a/docs/html-intl/intl/pt-br/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/pt-br/guide/topics/resources/accessing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..f196dfeae779f9f81baac742054fbd065a6ffad5 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=Acesso aos recursos +parent.title=Recursos de aplicativos +parent.link=index.html +@jd:body + +
    +
    +

    Visualização rápida

    +
      +
    • Recursos podem ser referenciados a partir do código usando números inteiros de {@code R.java}, como +{@code R.drawable.myimage}
    • +
    • Recursos podem ser referenciados de recursos usando uma sintaxe XML especial, como {@code +@drawable/myimage}
    • +
    • Também é possível acessar os recursos do aplicativo com métodos em +{@link android.content.res.Resources}
    • +
    + +

    Classes principais

    +
      +
    1. {@link android.content.res.Resources}
    2. +
    + +

    Neste documento

    +
      +
    1. Acesso aos recursos do código
    2. +
    3. Acesso aos recursos do XML +
        +
      1. Referência a atributos de estilo
      2. +
      +
    4. +
    5. Acesso aos recursos da plataforma
    6. +
    + +

    Veja também

    +
      +
    1. Fornecimento de recursos
    2. +
    3. Tipos de recursos
    4. +
    +
    +
    + + + + +

    Depois de fornecer um recurso no aplicativo (discutido em Fornecimento de recursos), é possível aplicá-lo +referenciando seu ID de recurso. Todos os IDs de recursos são definidos na classe {@code R} do projeto, que +a ferramenta {@code aapt} gera automaticamente.

    + +

    Quando o aplicativo é compilado, {@code aapt} gera a classe {@code R}, que contém +IDs de recursos para todos os recursos no diretório {@code +res/}. Para cada tipo de recurso, há uma subclasse {@code R} (por exemplo, +{@code R.drawable} para todos os recursos desenháveis) e, para cada recurso daquele tipo, há um número inteiro +estático (por exemplo, {@code R.drawable.icon}). Esse número inteiro é o ID do recurso que pode ser usado para +recuperá-lo.

    + +

    Apesar de a classe {@code R} ser o local onde os IDs de recursos são especificados, não deve nunca ser necessário +verificá-la para descobrir um ID de recurso. Ele é sempre composto de:

    +
      +
    • O tipo de recurso: cada recurso é agrupado em um "tipo", como {@code +string}, {@code drawable} e {@code layout}. Para saber mais sobre os diferentes tipos, consulte Tipos de recursos. +
    • +
    • O nome do recurso, que é: o nome do arquivo, +excluindo a extensão; ou o valor no atributo {@code android:name} do XML, se o +recurso for um valor simples (como uma string).
    • +
    + +

    Há duas formas de acessar um recurso:

    +
      +
    • No código: Usando um número inteiro estático de uma subclasse de sua classe {@code R}, +como: +
      R.string.hello
      +

      {@code string} é o tipo de recurso e {@code hello} é o nome do recurso. Há muitas +APIs do Android que podem acessar os seus recursos quando você fornece um ID de recurso nesse formato. Consulte +Acesso aos recursos no código.

      +
    • +
    • No XML: usando uma sintaxe XML especial que também corresponde ao +ID de recurso definido em sua classe {@code R}, como: +
      @string/hello
      +

      {@code string} é o tipo de recurso e {@code hello} é o nome do recurso. Você pode usar essa +sintaxe em um recurso XML em qualquer lugar em que um valor é esperado e que seja fornecido em um recurso. Consulte Acesso aos recursos do XML.

      +
    • +
    + + + +

    Acesso aos recursos no código

    + +

    Você pode usar um recurso no código passando o ID do recurso como um parâmetro do método. Por +exemplo, é possível definir uma {@link android.widget.ImageView} para usar o recurso {@code res/drawable/myimage.png} +usando {@link android.widget.ImageView#setImageResource(int) setImageResource()}:

    +
    +ImageView imageView = (ImageView) findViewById(R.id.myimageview);
    +imageView.setImageResource(R.drawable.myimage);
    +
    + +

    Também é possível recuperar recursos individuais usando métodos em {@link +android.content.res.Resources}, dos quais é possível obter uma instância +com {@link android.content.Context#getResources()}.

    + + + + +

    Sintaxe

    + +

    Esta é a sintaxe para referenciar um recurso no código:

    + +
    +[<package_name>.]R.<resource_type>.<resource_name>
    +
    + +
      +
    • {@code <package_name>} é o nome do pacote no qual o recurso está localizado (não +é obrigatório ao referenciar recursos de seu próprio pacote).
    • +
    • {@code <resource_type>} é a subclasse {@code R} do tipo de recurso.
    • +
    • {@code <resource_name>} é o nome do arquivo do recurso +sem a extensão ou o valor do atributo {@code android:name} no elemento XML (para valores +simples).
    • +
    +

    Consulte Tipos de recursos para +obter mais informações sobre cada tipo de recurso e como referenciá-los.

    + + +

    Casos de uso

    + +

    Há muitos métodos que aceitam um parâmetro de ID de recurso e você pode recuperar recursos usando +métodos em {@link android.content.res.Resources}. É possível obter uma instância de {@link +android.content.res.Resources} com {@link android.content.Context#getResources +Context.getResources()}.

    + + +

    Abaixo há alguns exemplos do acesso aos recursos no código:

    + +
    +// Load a background for the current screen from a drawable resource
    +{@link android.app.Activity#getWindow()}.{@link
    +android.view.Window#setBackgroundDrawableResource(int)
    +setBackgroundDrawableResource}(R.drawable.my_background_image) ;
    +
    +// Set the Activity title by getting a string from the Resources object, because
    +//  this method requires a CharSequence rather than a resource ID
    +{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence)
    +setTitle}(getResources().{@link android.content.res.Resources#getText(int)
    +getText}(R.string.main_title));
    +
    +// Load a custom layout for the current screen
    +{@link android.app.Activity#setContentView(int)
    +setContentView}(R.layout.main_screen);
    +
    +// Set a slide in animation by getting an Animation from the Resources object
    +mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation)
    +setInAnimation}(AnimationUtils.loadAnimation(this,
    +        R.anim.hyperspace_in));
    +
    +// Set the text on a TextView object using a resource ID
    +TextView msgTextView = (TextView) findViewById(R.id.msg);
    +msgTextView.{@link android.widget.TextView#setText(int)
    +setText}(R.string.hello_message);
    +
    + + +

    Atenção: nunca modifique o arquivo {@code +R.java} manualmente — ele é gerado pela ferramenta {@code aapt} quando o projeto é +compilado. As alterações serão sobrepostas na próxima compilação.

    + + + +

    Acesso aos recursos do XML

    + +

    Você pode definir valores para alguns atributos e elementos XML usando uma +referência a um recurso existente. Isso será feito com frequência ao criar arquivos de layout +para fornecer strings e imagens para os widgets.

    + +

    Por enviar mensagem de texto, se você adicionar um {@link android.widget.Button} ao layout, deverá usar +um recurso de string para o texto do botão:

    + +
    +<Button
    +    android:layout_width="fill_parent"
    +    android:layout_height="wrap_content"
    +    android:text="@string/submit" />
    +
    + + +

    Sintaxe

    + +

    Esta é a sintaxe para referenciar um recurso em um recurso XML:

    + +
    +@[<package_name>:]<resource_type>/<resource_name>
    +
    + +
      +
    • {@code <package_name>} é o nome do pacote no qual o recurso está localizado (não +é obrigatório ao referenciar recursos do mesmo pacote).
    • +
    • {@code <resource_type>} é a subclasse +{@code R} do tipo de recurso.
    • +
    • {@code <resource_name>} é o nome do arquivo do recurso +sem a extensão ou o valor do atributo {@code android:name} no elemento XML (para valores +simples).
    • +
    + +

    Consulte Tipos de recursos para +obter mais informações sobre cada tipo de recurso e como referenciá-los.

    + + +

    Casos de uso

    + +

    Em alguns casos, é preciso usar um recurso para um valor em XML (por exemplo, para aplicar uma imagem desenhável +a um widget), mas você também pode usar um recurso em XML em qualquer lugar que aceite um valor simples. Por +exemplo, se você tiver o seguinte arquivo de recursos que inclui um recurso de cor e um recurso de string:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +   <color name="opaque_red">#f00</color>
    +   <string name="hello">Hello!</string>
    +</resources>
    +
    + +

    É possível usar esses recursos no arquivo de layout a seguir para definir a cor to texto e a +string do texto:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:textColor="@color/opaque_red"
    +    android:text="@string/hello" />
    +
    + +

    Nesse caso, não é preciso especificar o nome do pacote na referência do recurso porque os +recursos são de seu próprio pacote. Para +referenciar um recurso do sistema, é preciso incluir o nome do pacote. Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:textColor="@android:color/secondary_text_dark"
    +    android:text="@string/hello" />
    +
    + +

    Observação: você deve usar recursos de string +o tempo inteiro para que o seu aplicativo possa ser localizado para outros idiomas. +Para obter informações sobre a criação de recursos +alternativos (como strings localizadas), consulte Fornecimento de recursos +alternativos. Para obter um guia completo para localizar o aplicativo para outros idiomas, +consulte Localização.

    + +

    Você pode até mesmo usar recursos em XML para criar alias. Por exemplo, é possível criar um recurso +desenhável que seja um alias para outro recurso desenhável:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:src="@drawable/other_drawable" />
    +
    + +

    Isso parece redundante, mas pode ser muito útil ao usar um recurso alternativo. Leia mais sobre +Criação de recursos de alias.

    + + + +

    Referência a atributos de estilo

    + +

    Um recurso de atributo de estilo permite referenciar o valor +de um atributo no tema atualmente aplicado. Referenciar um atributo de estilo permite +personalizar a aparência de elementos da IU deixando-os com estilo que corresponda a variações padrão fornecidas pelo +tema atual, em vez de fornecer um valor codificado. Referenciar um atributo de estilo +essencialmente significa "usar o estilo que é definido por esse atributo no tema atual".

    + +

    Para referenciar um atributo de estilo, a sintaxe do nome é quase idêntica ao formato normal de recurso, +mas, em vez de o símbolo arroba ({@code @}), use um ponto de interrogação ({@code ?}). Além disso, +a parte do tipo de recurso é opcional. Por exemplo:

    + +
    +?[<package_name>:][<resource_type>/]<resource_name>
    +
    + +

    Por exemplo, abaixo apresenta-se como você pode referenciar um atributo para definir a cor do texto para que corresponda à +cor "principal" do texto do tema do sistema:

    + +
    +<EditText id="text"
    +    android:layout_width="fill_parent"
    +    android:layout_height="wrap_content"
    +    android:textColor="?android:textColorSecondary"
    +    android:text="@string/hello_world" />
    +
    + +

    Aqui, o atributo {@code android:textColor} especifica o nome de um atributo de estilo +no tema atual. O Android agora usa o valor aplicado ao atributo de estilo {@code android:textColorSecondary} +como o valor para {@code android:textColor} nesse widget. Como a ferramenta de recursos +do sistema sabe que um recurso de atributo é esperado nesse contexto, +não é preciso declarar explicitamente o tipo (que seria +?android:attr/textColorSecondary) — você pode excluir o tipo de {@code attr}.

    + + + + +

    Acesso aos recursos da plataforma

    + +

    O Android contém uma série de recursos padrão, como estilos, temas e layouts. Para +acessá-los, qualifique a referência de recurso com o +nome do pacote android. Por exemplo, o Android fornece um recurso de layout que pode ser usado para +listar itens em um {@link android.widget.ListAdapter}:

    + +
    +{@link android.app.ListActivity#setListAdapter(ListAdapter)
    +setListAdapter}(new {@link
    +android.widget.ArrayAdapter}<String>(this, android.R.layout.simple_list_item_1, myarray));
    +
    + +

    Nesse exemplo, {@link android.R.layout#simple_list_item_1} é um recurso de layout definido pela +plataforma para itens em uma {@link android.widget.ListView}. Você pode usá-lo em vez de criar o +próprio layout para itens de lista. Para obter mais informações, consulte o +guia do desenvolvedor de Vista de lista.

    + diff --git a/docs/html-intl/intl/pt-br/guide/topics/resources/overview.jd b/docs/html-intl/intl/pt-br/guide/topics/resources/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..5bf37e691376c2ca7ace746aa463be9a7ed4dad0 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=Visão geral dos recursos +@jd:body + + + + +

    Deve-se sempre exteriorizar os recursos do aplicativo, como imagens e strings do código do aplicativo, +para que você possa mantê-los independentemente. Exteriorizar os recursos +também permite fornecer recursos alternativos que sejam compatíveis com configurações +de dispositivos específicos, como idiomas ou tamanhos de tela diferentes, que se tornam cada vez +mais importantes à medida que mais dispositivos com Android são disponibilizados com configurações diferentes. Para fornecer +compatibilidade com diferentes configurações, é preciso organizar recursos no +diretório {@code res/} de seu projeto usando vários subdiretórios que agrupem recursos por tipo e +configuração.

    + +
    + +

    +Figura 1. Dois dispositivos diferentes, cada um usando o layout padrão +(o aplicativo não fornece layouts alternativos).

    +
    + +
    + +

    +Figura 2. Dois dispositivos diferentes, cada um usando um layout diferente fornecido +para diferentes tamanhos de tela.

    +
    + +

    Para qualquer tipo de recurso, é possível especificar recursos padrão e vários recursos +alternativos para o aplicativo:

    +
      +
    • Recursos padrão são aqueles que devem ser usados independentemente +da configuração do dispositivo ou quando não há recursos alternativos que correspondam à +configuração atual.
    • +
    • Recursos alternativos são aqueles projetados para uso com uma configuração +específica. Para definir que um grupo de recursos é para uma configuração específica, +anexe um qualificador de configuração apropriado ao nome do diretório.
    • +
    + +

    Por exemplo, enquanto o layout da IU padrão +é salvo no diretório {@code res/layout/}, é possível especificar um layout diferente +a ser usado quando a tela está na orientação de paisagem salvando-o no diretório {@code res/layout-land/} +. O Android automaticamente aplica os recursos adequados correspondendo +a configuração atual do dispositivo com os nomes de diretórios de recursos.

    + +

    A figura 1 ilustra como o sistema aplica o mesmo layout para dois +dispositivos diferentes quando não há recursos alternativos disponíveis. A figura 2 mostra +o mesmo aplicativo quando é adicionado um recurso de layout alternativo para telas maiores.

    + +

    Os documentos a seguir fornecem um guia completo sobre como organizar os recursos do aplicativo, +especificar recursos alternativos, acessá-los no aplicativo e muito mais:

    + +
    +
    Como fornecer recursos
    +
    Os tipos de recursos que você pode fornecer no aplicativo, onde salvá-los e como criar +recursos alternativos para configurações específicas de dispositivos.
    +
    Acesso aos recursos
    +
    Como usar os recursos que você forneceu referenciando-os no código do aplicativo +ou de outros recursos XML.
    +
    Tratar alterações no tempo de execução
    +
    Como gerenciar alterações de configuração que ocorrem enquanto a Atividade está em execução.
    +
    Localização
    +
    Um guia ascendente para localizar o aplicativo usando recursos alternativos. Apesar de esse ser +apenas um uso específico de recursos alternativos, ele é muito importante para atingir mais +usuários.
    +
    Tipos de recursos
    +
    Uma referência a vários tipos de recursos que você pode fornecer, descrevendo os elementos XML, +os atributos e a sintaxe. Por exemplo, esta referência mostra como criar um recurso +para menus do aplicativo, desenháveis, animações e mais.
    +
    + + diff --git a/docs/html-intl/intl/pt-br/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/pt-br/guide/topics/resources/providing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..1118fd536b32e90658cd5550636b3ba783e8e69a --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=Fornecimento de recursos +parent.title=Recursos de aplicativo +parent.link=index.html +@jd:body + +
    +
    +

    Visualização rápida

    +
      +
    • Tipos diferentes de recursos pertencem a subdiretórios diferentes de {@code res/}
    • +
    • Recursos alternativos fornecem arquivos de recurso específicos de configuração
    • +
    • Sempre inclua recursos padrão para que o aplicativo não dependa de configurações +específicas do dispositivo
    • +
    +

    Neste documento

    +
      +
    1. Agrupamento de tipos de recursos
    2. +
    3. Fornecimento de recursos alternativos +
        +
      1. Regras de nome do qualificador
      2. +
      3. Criação de recursos de alias
      4. +
      +
    4. +
    5. Fornecimento da melhor compatibilidade de dispositivo com recursos
    6. +
    7. Como o Android encontra o melhor recurso compatível
    8. +
    + +

    Veja também

    +
      +
    1. Acesso aos recursos
    2. +
    3. Tipos de recursos
    4. +
    5. Compatibilidade com +várias telas
    6. +
    +
    +
    + +

    Deve-se sempre exteriorizar os recursos do aplicativo, como imagens e strings do código, +para que você possa mantê-los independentemente. Deve-se também fornecer recursos alternativos para +configurações específicas do dispositivo, agrupando-os em diretórios de recursos especialmente nomeados. Em +tempo de execução, o Android usa o recurso adequado com base na configuração atual. Por +exemplo, você pode querer fornecer um layout de IU diferente dependendo do tamanho da tela ou +strings diferentes dependendo da configuração de idioma.

    + +

    Ao exteriorizar os recursos do aplicativo, é possível acessá-los +usando IDs de recurso que são gerados na classe {@code R} do projeto. O procedimento para usar +recursos no aplicativo é discutido em Acesso aos +recursos. Este documento mostra como agrupar os recursos no projeto do Android +e fornecer recursos alternativos para configurações específicas do dispositivo.

    + + +

    Agrupamento de tipos de recursos

    + +

    Você deve posicionar cada tipo de recurso em um subdiretório específico do diretório +{@code res/} do projeto. Por exemplo, abaixo está a hierarquia de arquivos para um projeto simples:

    + +
    +MyProject/
    +    src/  
    +        MyActivity.java  
    +    res/
    +        drawable/  
    +            graphic.png  
    +        layout/  
    +            main.xml
    +            info.xml
    +        mipmap/  
    +            icon.png 
    +        values/  
    +            strings.xml  
    +
    + +

    Como pode ver neste exemplo, o diretório {@code res/} contém todos os recursos (em subdiretórios): +um recurso de imagem, dois recursos de layout, diretórios {@code mipmap/} para ícones de +inicialização e um arquivo de recurso de string. Os nomes dos diretórios +de recursos são importantes e são descritos na tabela 1.

    + +

    Observação: para obter mais informações sobre o uso de pastas de mipmap, consulte +Visão geral do gerenciamento de projetos.

    + +

    Tabela 1. Os diretórios de recursos +compatíveis dentro do diretório {@code res/} do projeto.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    DiretórioTipo de recurso
    animator/Arquivos XML que definem as animações +da propriedade.
    anim/Arquivos XML que definem as animações +intermediárias. (As animações de propriedade também podem ser salvas neste diretório, +mas o diretório {@code animator/} é o preferencial para animações de propriedade para distinguir os dois +tipos.)
    color/Arquivos XML que definem uma lista de estado de cores. Consulte Recurso de +lista de estado de cores
    drawable/

    Os arquivos Bitmap ({@code .png}, {@code .9.png}, {@code .jpg}, {@code .gif}) ou arquivos XML +são compilados nos seguintes subtipos de recurso desenhável:

    +
      +
    • Arquivos Bitmap
    • +
    • Nine-Patch (bitmaps redimensionáveis)
    • +
    • Listas de estado
    • +
    • Formatos
    • +
    • Desenháveis de animação
    • +
    • Outros desenháveis
    • +
    +

    Veja Recursos desenháveis.

    +
    mipmap/Arquivos desenháveis para diferentes densidades do ícone do inicializador. Para obter mais informações sobre o gerenciamento de + ícones do inicializador com pastas {@code mipmap/}, consulte + Visão geral do gerenciamento de projetos.
    layout/Arquivos XML que definem um layout de interface do usuário. + Consulte Recurso de layout.
    menu/Arquivos XML que definem os menus do aplicativo, como Menu de opções, Menu de contexto +ou Submenu. Consulte Recurso de menu.
    raw/

    Arquivos arbitrários para salvar na forma bruta. Para abrir esses recursos +com {@link java.io.InputStream} bruto, chame {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()} com o ID do recurso, que é {@code R.raw.filename}.

    +

    No entanto, caso precise de acesso aos nomes e à hierarquia dos arquivos originais, considere salvar +alguns recursos no diretório {@code +assets/} (em vez de {@code res/raw/}). Os arquivos em {@code assets/} não recebem um ID de recurso, +então é possível lê-los usando apenas o {@link android.content.res.AssetManager}.

    values/

    Arquivos XML que contêm valores simples, como strings, números inteiros e cores.

    +

    Enquanto os arquivos de recurso XML estiverem em outros subdiretórios {@code res/}, defina um único recurso +com base no nome do arquivo XML, os arquivos no diretório {@code values/} descrevem vários recursos. +Para cada arquivo neste diretório, cada filho do elemento {@code <resources>} define um único +recurso. Por exemplo, um elemento {@code <string>} cria +um recurso {@code R.string} e um elemento {@code <color>} cria um recurso +{@code R.color}.

    +

    Como cada recurso é definido com seu próprio elemento XML, é possível nomear o arquivo +da forma que quiser e colocar tipos de recurso variados em um arquivo. No entanto, para esclarecer, você pode +querer colocar tipos de recursos únicos em arquivos diferentes. Por exemplo, a seguir há algumas convenções +de nome de arquivo para recursos que podem ser criados neste diretório:

    + +

    Consulte Recursos de string, + Recurso de estilo + e Mais tipos de recursos.

    +
    xml/Arquivos arbitrários XML que podem ser lidos em tempo de execução chamando {@link +android.content.res.Resources#getXml(int) Resources.getXML()}. Vários arquivos de configuração XML +devem ser salvos aqui, como uma configuração buscável. +
    + +

    Atenção: nunca salve arquivos de recurso diretamente no diretório +{@code res/} — isto provocará um erro ao compilador.

    + +

    Para obter mais informações sobre determinados tipos de recursos, consulte a documentação Tipos de recursos.

    + +

    Os recursos salvos nos subdiretórios definidos na tabela 1 são os recursos +"padrão". Ou seja, esses recursos definem projeto e conteúdo padrão para o aplicativo. +No entanto, tipos diferentes de dispositivos com Android podem chamar diferentes tipos de recursos. +Por exemplo: se um dispositivo tiver uma tela maior do que o normal, deve-se fornecer +recursos de layout diferentes que aproveitem o espaço extra na tela. Ou, se um dispositivo tiver +uma configuração de idioma diferente, deve-se fornecer recursos de string diferentes que traduzam +o texto na interface do usuário. Para fornecer esses diferentes recursos para configurações de dispositivo +diferentes, você precisa fornecer recursos alternativos, além dos recursos +padrão.

    + + +

    Fornecimento de recursos alternativos

    + + +
    + +

    +Figura 1. Dois dispositivos diferentes, cada um usando recursos diferentes de layout.

    +
    + +

    Quase todos os aplicativos devem fornecer recursos alternativos para suportar +configurações específicas do dispositivo. Por exemplo: deve-se incluir recursos desenháveis alternativos para densidades +de tela diferentes e recursos alternativos de string para idiomas diferentes. Em tempo de execução, +o Android detecta a configuração atual do dispositivo e carrega os recursos +adequados para o aplicativo.

    + +

    Para especificar as alternativas de configuração específica para um conjunto de recursos:

    +
      +
    1. Crie um novo diretório no {@code res/} nomeado na forma de {@code +<resources_name>-<config_qualifier>}. +
        +
      • {@code <resources_name>} é o nome do diretório dos recursos padrão correspondentes +(definido na tabela 1).
      • +
      • {@code <qualifier>} é um nome que especifica uma configuração individual +para a qual esses recursos destinam-se (definido na tabela 2).
      • +
      +

      É possível anexar mais de um {@code <qualifier>}. Separe cada +um com um travessão.

      +

      Atenção: ao anexar vários qualificadores, deve-se +colocá-los na mesma ordem em que foram listados na tabela 2. Se os qualificadores forem ordenados +de forma incorreta, os recursos serão ignorados.

      +
    2. +
    3. Salve os respectivos recursos alternativos neste novo diretório. Os arquivos de recurso devem ser +nomeados exatamente da mesma forma que os arquivos de recurso padrão.
    4. +
    + +

    Por exemplo, a seguir há alguns recursos alternativos e outros padrão:

    + +
    +res/
    +    drawable/   
    +        icon.png
    +        background.png    
    +    drawable-hdpi/  
    +        icon.png
    +        background.png  
    +
    + +

    O qualificador {@code hdpi} indica que os recursos neste diretório são para os dispositivos +com tela de alta densidade. As imagens em cada um desses diretórios desenháveis são dimensionadas para uma densidade de tela +específica, mas os nomes dos arquivos +são exatamente os mesmos. Desta maneira, o ID de recurso usado para referenciar {@code icon.png} ou a imagem {@code +background.png} é sempre o mesmo, mas o Android seleciona +a versão de cada arquivo que melhor compatibiliza-se com o dispositivo atual, comparando as informações de configuração +com os qualificadores no nome do diretório do recurso.

    + +

    O Android é compatível com vários qualificadores de configuração e é possível +adicionar vários qualificadores a um nome de diretório separando cada qualificador com um travessão. A tabela 2 +lista os qualificadores de configuração válidos, em ordem de precedência — caso use vários +qualificadores para um diretório de recursos, você deve adicioná-los ao nome do diretório na ordem que foram listados +na tabela.

    + + +

    Tabela 2. Nomes de qualificadores +de configuração.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ConfiguraçãoValores do qualificadorDescrição
    MCC e MNCExemplos:
    + mcc310
    + mcc310-mnc004
    + mcc208-mnc00
    + etc. +
    +

    O código de dispositivos móveis do país (MCC), opcionalmente seguido do código de rede móvel (MNC) + do cartão SIM no dispositivo. Por exemplo, mcc310 é dos E.U.A. em qualquer operadora, + mcc310-mnc004 é dos E.U.A., em Verizon, e mcc208-mnc00 é da França, + em Orange.

    +

    Se o dispositivo usar uma conexão de rádio (telefone GSM), os valores MCC e MNC serão + os do cartão SIM.

    +

    Você também pode usar somente o MCC (por exemplo, para incluir recursos legais específicos do país +no aplicativo). Caso precise especificar com base somente no idioma, use o qualificador +de idioma e região (discutido a seguir). Caso decida usar o qualificador +de MCC e MNC, tome cuidado e teste o seu funcionamento.

    +

    Veja também os campos de configuração {@link +android.content.res.Configuration#mcc} e {@link +android.content.res.Configuration#mnc}, que indicam o código de dispositivos móveis do país atual +e o código de rede móvel, respectivamente.

    +
    Idioma e regiãoExemplos:
    + en
    + fr
    + en-rUS
    + fr-rFR
    + fr-rCA
    + etc. +

    O idioma é definido por um código de idiomaISO + 639-1 de duas letras, opcionalmente seguido por um código da região + ISO + 3166-1-alpha-2 (precedido de "{@code r}" em minúsculo). +

    + Os códigos não diferenciam maiúsculas e minúsculas; o prefixo {@code r} é usado + para distinguir a parte da região. + Não é possível especificar uma região só.

    +

    Isto pode mudar durante a vida útil +do aplicativo se o usuário mudar o idioma nas configurações do sistema. Consulte Tratamento de alterações em tempo de execução para obter informações +sobre como isto pode afetar o aplicativo em tempo de execução.

    +

    Consulte Localização para obter um guia completo para localizar +os aplicativos para outros idiomas.

    +

    Veja também o campo de configuração {@link android.content.res.Configuration#locale}, +que indica a localidade atual.

    +
    Direção do layoutldrtl
    + ldltr
    +

    A direção do layout do aplicativo. {@code ldrtl} significa "layout-direction-right-to-left" (direção do layout da direita para a esquerda). + {@code ldltr} significa "layout-direction-left-to-right" (direção do layout da esquerda para a direita) e é o valor implícito padrão. +

    +

    Isto pode aplicar-se a qualquer recurso, como layouts, desenháveis ou valores. +

    +

    Por exemplo, caso queira fornecer um layout específico para o idioma arábico e + layouts genéricos para outros idiomas que seguem a leitura da direita para a esquerda, como os idiomas hebreu e persa, então você teria: +

    +
    +res/
    +    layout/   
    +        main.xml  (Default layout)
    +    layout-ar/  
    +        main.xml  (Specific layout for Arabic)
    +    layout-ldrtl/  
    +        main.xml  (Any "right-to-left" language, except
    +                  for Arabic, because the "ar" language qualifier
    +                  has a higher precedence.)
    +
    +

    Observação: para ativar recursos de layout de leitura da direita para a esquerda + para o aplicativo, você deve definir {@code + supportsRtl} como {@code "true"} e {@code targetSdkVersion} como 17 ou maior.

    +

    Adicionado à API de nível 17.

    +
    smallestWidthsw<N>dp

    + Exemplos:
    + sw320dp
    + sw600dp
    + sw720dp
    + etc. +
    +

    O tamanho fundamental de uma tela, como indicado pela menor dimensão da área da tela +disponível. Especificamente, o valor smallestWidth do dispositivo é o menor da altura e largura +da tela (pode-se também interpretar isso como a "menor largura possível" da tela). É possível +usar este qualificador para garantir que, independentemente da orientação atual da tela, +o aplicativo tenha pelo menos {@code <N>} dps de largura disponível para a IU.

    +

    Por exemplo, se o seu layout exigir que a menor dimensão da área da tela seja de pelo menos +600 dp, é possível usar o seguinte qualificador para criar os recursos do layout: {@code +res/layout-sw600dp/}. O sistema usará esses recursos somente quando a menor dimensão +da tela disponível for de pelo menos 600 dp, independentemente se o lado de 600 dp é a altura ou a largura +percebida pelo usuário. O valor smallestWidth é uma característica fixa do tamanho da tela do dispositivo;o valor smallestWidth +do dispositivo não altera quando a orientação da tela muda.

    +

    O smallestWidth de um dispositivo considera a IU do sistema e as decorações da tela. Por exemplo, +se o dispositivo tiver alguns elementos de IU persistentes na tela que considera o espaço ao longo do eixo +de smallestWidth, o sistema declara que smallestWidth é menor do que o tamanho +atual da tela, pois são pixels de tela não disponíveis para a IU. Portanto, o valor usado +deve ser a dimensão real menor necessária para o layout (geralmente, este valor +é a "menor largura" compatível com o layout, independente da orientação atual da tela).

    +

    Alguns dos valores que você pode usar para tamanhos de tela comuns:

    +
      +
    • 320, para dispositivos com configurações de tela como: +
        +
      • 240 x 320 ldpi (celular QVGA)
      • +
      • 320 x 480 mdpi (celular)
      • +
      • 480 x 800 hdpi (celular de alta densidade)
      • +
      +
    • +
    • 480, para telas como 480 x 800 mdpi (tablet/celular).
    • +
    • 600, para telas como 600 x 1024 mdpi (tablet de 7 polegadas).
    • +
    • 720, para telas como 720 x 1280 mdpi (tablet de 10 polegadas).
    • +
    +

    Quando o aplicativo fornece vários diretórios de recursos com valores diferentes + para o qualificador smallestWidth, o sistema usa o mais próximo (sem exceder) ao +de smallestWidth do dispositivo.

    +

    Adicionado à API de nível 13.

    +

    Veja também o atributo {@code +android:requiresSmallestWidthDp}, que declara a smallestWidth mínima compatível +com o aplicativo e o campo de configuração {@link +android.content.res.Configuration#smallestScreenWidthDp}, que retém +o valor de smallestWidth do dispositivo.

    +

    Para obter mais informações sobre como projetar para telas diferentes e usar +este qualificador, consulte o guia do desenvolvedor Compatibilidade com +várias telas.

    +
    Largura disponívelw<N>dp

    + Exemplos:
    + w720dp
    + w1024dp
    + etc. +
    +

    Especifica uma largura mínima disponível da tela, em unidades {@code dp} em que o recurso + deve ser usado — definido pelo valor <N>. Este valor + de configuração mudará quando a orientação + alternar entre paisagem e retrato para corresponder à largura atual.

    +

    Quando o aplicativo fornece vários diretórios de recurso com valores diferentes + para esta configuração, o sistema usa o mais próximo (sem exceder) + da largura atual da tela do dispositivo. O + valor aqui considera as decorações da tela. Portanto, se o dispositivo tiver alguns + elementos de IU persistentes na borda esquerda ou direita da tela, ele usa + um valor para a largura menor do que o tamanho atual da tela, considerando + esses elementos de IU e reduzindo o espaço disponível do aplicativo.

    +

    Adicionado à API de nível 13.

    +

    Veja também o campo de configuração {@link android.content.res.Configuration#screenWidthDp}, + que possui a largura atual da tela.

    +

    Para obter mais informações sobre como projetar para telas diferentes e usar +este qualificador, consulte o guia do desenvolvedor Compatibilidade com +várias telas.

    +
    Altura disponívelh<N>dp

    + Exemplos:
    + h720dp
    + h1024dp
    + etc. +
    +

    Especifica uma altura mínima disponível da tela, em unidades "dp" em que o recurso + deve ser usado — definido pelo valor <N>. Este valor + de configuração mudará quando a orientação + alternar entre paisagem e retrato para corresponder à altura atual.

    +

    Quando o aplicativo fornece vários diretórios de recursos com valores diferentes + para esta configuração, o sistema usa o mais próximo (sem exceder) + da altura atual da tela do dispositivo. O + valor aqui considera as decorações da tela. Portanto, se o dispositivo tiver alguns + elementos de IU persistentes na borda superior ou inferior da tela, ele usa + um valor para a altura menor do que o tamanho atual da tela, considerando + esses elementos da IU e reduzindo o espaço disponível do aplicativo. As decorações da tela + que não forem fixas (como uma barra de status do telefone que pode ser + ocultada com tela cheia) não são consideradas aqui, assim como + as decorações da janela, como a barra de título ou a barra de ação. Portanto, os aplicativos devem ser preparados para + lidar com o espaço um pouco menor do que especificam. +

    Adicionado à API de nível 13.

    +

    Veja também o campo de configuração {@link android.content.res.Configuration#screenHeightDp}, + que possui a largura atual da tela.

    +

    Para obter mais informações sobre como projetar para telas diferentes e usar +este qualificador, consulte o guia do desenvolvedor Compatibilidade com +várias telas.

    +
    Tamanho da tela + small
    + normal
    + large
    + xlarge +
    +
      +
    • {@code small}: Telas de tamanho semelhante + à tela de pouca densidade QVGA. O tamanho mínimo do layout para uma tela pequena + é de aproximadamente 320 x 426 unidades dp. Exemplos são QVGA de pouca densidade e VGA de alta + densidade.
    • +
    • {@code normal}: Telas de tamanho semelhante + à tela de média densidade HVGA. O tamanho mínimo do layout para uma tela normal + é de aproximadamente 320 x 470 unidades dp. Exemplos + de tais delas são as WQVGA de pouca densidade, HVGA de média densidade e WVGA + de alta densidade.
    • +
    • {@code large}: Telas de tamanho semelhante + à tela de média densidade VGA. + O tamanho mínimo do layout para uma tela grande é de aproximadamente 480 x 640 unidades dp. + Exemplos são as telas de densidade média VGA e WVGA.
    • +
    • {@code xlarge}: Telas que são consideravelmente maiores do que a + tela tradicional de média densidade HVGA. O tamanho mínimo do layout para uma tela muito grande + é de aproximadamente 720x960 unidades dp. Na maioria dos casos, dispositivos com telas + muito grandes seriam grandes demais para serem carregados em bolsos e, provavelmente, + seriam dispositivos no estilo tablet. Adicionado à API de nível 9.
    • +
    +

    Observação: usar um qualificador de tamanho não significa +que os recursos sejam apenas para telas deste tamanho. Caso não forneça recursos +alternativos com qualificadores que melhor correspondem à configuração atual do dispositivo, o sistema poderá usar +quaisquer recursos que representarem a melhor correspondência.

    +

    Atenção: se todos os recursos usarem um qualificador de tamanho +maior do que a tela atual, o sistema não os usará +e o aplicativo apresentará um erro em tempo de execução (por exemplo, se todos os recursos de layout receberem tag com o qualificador {@code +xlarge}, mas o dispositivo tiver uma tela de tamanho normal).

    +

    Adicionado à API de nível 4.

    + +

    Consulte Compatibilidade com +várias telas para obter mais informações.

    +

    Consulte também o campo de configuração {@link android.content.res.Configuration#screenLayout}, +que indica se a tela é pequena, normal +ou grande.

    +
    Aspecto da tela + long
    + notlong +
    +
      +
    • {@code long}: Telas grandes, como WQVGA, WVGA, FWVGA
    • +
    • {@code notlong}: Telas que não são grandes, como QVGA, HVGA e VGA
    • +
    +

    Adicionado à API de nível 4.

    +

    Isto baseia-se puramente na relação de aspecto da tela (uma tela "grande" é mais larga). Isto +não está relacionado à orientação da tela.

    +

    Consulte também o campo de configuração {@link android.content.res.Configuration#screenLayout}, +que indica se a tela é grande.

    +
    Orientação da tela + port
    + land +
    +
      +
    • {@code port}: O dispositivo está na orientação de retrato (vertical)
    • +
    • {@code land}: O dispositivo está na orientação de paisagem (horizontal)
    • + +
    +

    Isto pode mudar durante a vida útil do aplicativo se o usuário girar +a tela. Consulte Tratamento de alterações em tempo de execução para obter informações +sobre como isto pode afetar o aplicativo em tempo de execução.

    +

    Veja também o campo de configuração {@link android.content.res.Configuration#orientation}, +que indica a orientação atual do dispositivo.

    +
    Modo de IU + car
    + desk
    + television
    + appliance + watch +
    +
      +
    • {@code car}: O dispositivo está exibindo em uma estação de acoplamento de carro
    • +
    • {@code desk}: O dispositivo está exibindo em uma estação de acoplamento de mesa
    • +
    • {@code television}: O dispositivo está exibindo em uma televisão, fornecendo + uma experiência à distância, onde a IU é em tela grande, + o usuário está longe, orientado principalmente por um controle direcional ou por outro tipo de + interação sem indicador
    • +
    • {@code appliance}: O dispositivo está servindo como uma aplicação, + sem tela
    • +
    • {@code watch}: O dispositivo tem uma tela que é usada no braço
    • +
    +

    Adicionado à API de nível 8, televisão adicionada à API de nível 13 e relógio adicionado à API de nível 20.

    +

    Para obter informações sobre como o aplicativo pode responder quando o dispositivo é inserido + ou removido de um dock, consulte Determinação +e monitoramento do tipo e do estado do dock.

    +

    Isto pode mudar durante a vida útil do aplicativo se o usuário colocar o dispositivo +em um dock. É possível ativar ou desativar alguns desses modos usando {@link +android.app.UiModeManager}. Consulte Tratamento de alterações em tempo de execução para obter informações +sobre como isto pode afetar o aplicativo em tempo de execução.

    +
    Modo noturno + night
    + notnight +
    +
      +
    • {@code night}: Noite
    • +
    • {@code notnight}: Dia
    • +
    +

    Adicionado à API de nível 8.

    +

    Isto pode mudar durante a vida útil do aplicativo se o modo noturno for deixado +no modo automático (padrão), em que o modo altera-se com base no horário. É possível ativar +ou desativar este modo usando {@link android.app.UiModeManager}. Consulte Tratamento de alterações em tempo de execução para obter informações +sobre como isto pode afetar o aplicativo em tempo de execução.

    +
    Densidade de pixel da tela (dpi) + ldpi
    + mdpi
    + hdpi
    + xhdpi
    + xxhdpi
    + xxxhdpi
    + nodpi
    + tvdpi +
    +
      +
    • {@code ldpi}: Telas de pouca densidade, aproximadamente 120 dpi.
    • +
    • {@code mdpi}: Telas de média densidade (em HVGA tradicional); aproximadamente +160 dpi.
    • +
    • {@code hdpi}: Telas de alta densidade, aproximadamente 240 dpi.
    • +
    • {@code xhdpi}: Telas de densidade extra-alta, aproximadamente 320 dpi. Adicionado à API de +nível 8
    • +
    • {@code xxhdpi}: Telas de densidade extra-extra-alta, aproximadamente 480 dpi. Adicionado à API de +nível 16
    • +
    • {@code xxxhdpi}: Usos de densidade extra-extra-extra-alta (somente ícone do inicializador, consulte a + observação + em Compatibilidade com várias telas), aproximadamente 640 dpi. Adicionado à API de +nível 18
    • +
    • {@code nodpi}: Isto pode ser usado para recursos de bitmap que você não deseja dimensionar +para corresponder à densidade do dispositivo.
    • +
    • {@code tvdpi}: Telas entre mdpi e hdpi, aproximadamente 213 dpi. Não é considerado +um grupo de densidade "principal". Geralmente usado para televisões +e a maioria dos aplicativos não precisam — fornecer recursos mdpi e hdpi é o suficiente para a maioria dos aplicativos +e o sistema dimensionará de forma adequada. Este qualificador foi introduzido com a API de nível 13.
    • +
    +

    Há uma razão de dimensionamento de 3:4:6:8:12:16 entre as seis densidades principais (ignorando +a densidade tvdpi). Então, um bitmap de 9 x 9 em ldpi é 12 x 12 em mdpi, 18 x 18 em hdpi, 24 x 24 em xhdpi e por aí em diante. +

    +

    Caso decida que os recursos de imagem não parecem suficientemente bons para uma televisão +ou outros dispositivos e queira testar recursos tvdpi, o fator de dimensionamento é 1,33*mdpi. Por exemplo: +uma imagem de 100 px x 100 px para telas mdpi deve ser de 133 px x 133 px para tvdpi.

    +

    Observação: usar um qualificador de densidade não significa +que os recursos sejam apenas para telas desta densidade. Caso não forneça recursos +alternativos com qualificadores que melhor correspondem à configuração atual do dispositivo, o sistema poderá usar +quaisquer recursos que representarem a melhor correspondência.

    +

    Consulte Compatibilidade com +várias telas para obter mais informações sobre como lidar com as diferentes densidades de tela e como o Android +pode dimensionar os bitmaps para encaixá-los na densidade atual.

    +
    Tipo de tela sensível ao toque + notouch
    + finger +
    +
      +
    • {@code notouch}: Os dispositivos não têm uma tela sensível ao toque.
    • +
    • {@code finger}: O dispositivo tem uma tela sensível ao toque que destina-se + à interação direcional do dedo do usuário.
    • +
    +

    Veja também o campo de configuração {@link android.content.res.Configuration#touchscreen}, +que indica o tipo de tela sensível ao toque no dispositivo.

    +
    Disponibilidade de teclado + keysexposed
    + keyshidden
    + keyssoft +
    +
      +
    • {@code keysexposed}: O dispositivo tem um teclado disponível. Se o dispositivo tiver um teclado de software +ativo (o que é provável), ele deve ser usado mesmo quando o teclado de hardware +não estiver exposto ao usuário, mesmo se o dispositivo não tiver teclado de hardware. Caso nenhum teclado de software +seja fornecido ou esteja desativado, então isto será usado apenas quando um teclado de hardware +for exposto.
    • +
    • {@code keyshidden}: O dispositivo tem um teclado de hardware disponível, +mas está oculto e o dispositivo não tem um teclado de software ativo.
    • +
    • {@code keyssoft}: O dispositivo tem um teclado de software ativo, +visível ou não.
    • +
    +

    Se você fornecer os recursos keysexposed, mas não os recursos keyssoft, + o sistema usará os recursos keysexposed independente da visibilidade +do teclado, contanto que o sistema tenha um teclado de software ativo.

    +

    Isto pode mudar durante a vida útil do aplicativo se o usuário abrir um teclado +de hardware. Consulte Tratamento de alterações em tempo de execução para obter informações +sobre como isto pode afetar o aplicativo em tempo de execução.

    +

    Veja também os campos de configuração {@link +android.content.res.Configuration#hardKeyboardHidden} e {@link +android.content.res.Configuration#keyboardHidden}, que indicam a visibilidade de um teclado de hardware +e a visibilidade de qualquer tipo de teclado (incluindo software), respectivamente.

    +
    Método principal de entrada de texto + nokeys
    + qwerty
    + 12key +
    +
      +
    • {@code nokeys}: O dispositivo não tem teclas de hardware para entradas de texto.
    • +
    • {@code qwerty}: O dispositivo tem um teclado QWERTY de hardware, esteja ele visível ao +usuário +ou não.
    • +
    • {@code 12key}: O dispositivo tem um teclado de hardware de 12 teclas, esteja ele visível ao usuário +ou não.
    • +
    +

    Veja também o campo de configuração {@link android.content.res.Configuration#keyboard}, +que indica o método de entrada de texto principal disponível.

    +
    Versão da plataforma (nível de API)Exemplos:
    + v3
    + v4
    + v7
    + etc.
    +

    O nível de API suportado pelo dispositivo. Por exemplo, v1 para a API de nível 1 +(dispositivos com Android 1.0 ou mais recente) e v4 para API de nível 4 (dispositivos com Android +1.6 ou mais recente). Veja o documento Níveis de API do Android para obter mais informações +sobre esses valores.

    +
    + + +

    Observação: alguns qualificadores de configuração foram adicionados desde o Android 1.0, + então nem todas as versões do Android suportam todos eles. Usar um novo qualificador +adiciona implicitamente um qualificador da versão de plataforma, então dispositivos mais antigos com certeza o ignorarão. Por exemplo, usar +um qualificador w600dp incluirá automaticamente o qualificador v13, +pois o qualificador de largura disponível era novo na API de nível 13. Para evitar quaisquer problemas, sempre inclua um conjunto +de recursos padrão (um conjunto de recursos sem qualificadores). Para obter mais informações, consulte +a seção Fornecimento da melhor compatibilidade de dispositivo com +recursos.

    + + + +

    Regras de nome do qualificador

    + +

    A seguir há algumas regras sobre como usar nomes de qualificador de configuração:

    + +
      +
    • É possível especificar vários qualificadores para um único conjunto de recursos, separados por travessões. Por exemplo, +drawable-en-rUS-land aplica-se aos dispositivos em inglês dos E.U.A. +na orientação de paisagem.
    • +
    • Os qualificador devem estar na ordem listada na tabela 2. Por +exemplo: +
        +
      • Incorreto: drawable-hdpi-port/
      • +
      • Correto: drawable-port-hdpi/
      • +
      +
    • +
    • Os diretórios de recursos alternativos não podem ser aninhados. Por exemplo, não é possível ter +res/drawable/drawable-en/.
    • +
    • Os valores não diferenciam letras maiúsculas e minúsculas. O compilador de recursos converte nomes de diretório + para letras minúsculas antes de processar para evitar problemas nos sistemas de arquivo + que não diferenciam maiúsculas e minúsculas. Qualquer letra maiúscula nos nomes é apenas para o benefício da leitura.
    • +
    • Somente um valor para cada tipo de qualificador é suportado. Por exemplo, se quiser usar +os mesmos arquivos desenháveis para Espanha e França, não é possível ter um diretório chamado +drawable-rES-rFR/. Em vez disso, você precisa de dois diretórios de recursos, como +drawable-rES/ e drawable-rFR/, que contenham arquivos adequados. +No entanto, não é necessário duplicar os mesmos arquivos em ambos os locais. Em vez disso, +é possível criar um alias para um recurso. Consulte Criação de recursos +de alias abaixo.
    • +
    + +

    Após salvar os recursos alternativos nos diretórios nomeados +com esses qualificadores, o Android aplicará automaticamente os recursos no aplicativo com base +na configuração atual do dispositivo. Sempre que um recurso for solicitado, o Android verificará diretórios de recursos alternativos +que contenham o arquivo de recurso solicitado e, em seguida,encontrará o melhor +recurso correspondente (discutido abaixo). Se não houver recursos alternativos que correspondam +a uma configuração de dispositivo específica, o Android usará os recursos padrão correspondentes +(o conjunto de recursos para um tipo de recurso específico que não inclua um qualificador +de configuração).

    + + + +

    Criação de recursos de alias

    + +

    Quando estiver com um recurso que gostaria de usar para mais +de uma configuração de dispositivo, mas não quer fornecê-lo como um recurso padrão), não será necessário usar o mesmo +recurso em mais de um diretório de recursos alternativos. Em vez disso, é possível (em alguns casos) criar um +recurso +alternativo que age como um alias para um recurso salvo no diretório de recurso padrão.

    + +

    Observação: nem todos os recursos oferecem um mecanismo que possibilita +criar um alias para outro recurso. Em particular, recursos de animação, de menu, brutos +e de outros tipos no diretório {@code xml/} não oferecem esta função.

    + +

    Por exemplo, imagine que você possui um ícone do aplicativo, {@code icon.png}, e precisa da versão exclusiva +para diferentes localidades. No entanto, duas localidades, inglês canadense e francês canadense, +precisam usar a mesma versão. Você pode presumir que precisa copiar a mesma imagem +para o diretório do recurso do inglês canadense e do francês canadense, +mas não é verdade. Em vez disso, é possível salvar a imagem que é usada para ambos como {@code icon_ca.png} (qualquer +nome que não seja {@code icon.png}) e colocá-la +no diretório {@code res/drawable/} padrão. Em seguida, crie um arquivo {@code icon.xml} em {@code +res/drawable-en-rCA/} e em {@code res/drawable-fr-rCA/} que mencione o recurso {@code icon_ca.png} +usando o elemento {@code <bitmap>}. Isto permite que você armazene apenas uma versão do arquivo +PNG e dois arquivos XML pequenos que apontam para ele. (Um exemplo de arquivo XML é exibido abaixo)

    + + +

    Desenhável

    + +

    Para criar um alias para um desenhável existente, use o elemento {@code <bitmap>}. +Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:src="@drawable/icon_ca" />
    +
    + +

    Se salvar esse arquivo como {@code icon.xml} (em um diretório de recursos alternativos, +como {@code res/drawable-en-rCA/}), ele será compilado em um recurso +que pode ser mencionado como {@code R.drawable.icon}, mas é um alias para o recurso {@code +R.drawable.icon_ca}, que é salvo em{@code res/drawable/}.

    + + +

    Layout

    + +

    Para criar um alias para um layout existente, use o elemento {@code <include>} +, agrupado em um {@code <merge>}. Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<merge>
    +    <include layout="@layout/main_ltr"/>
    +</merge>
    +
    + +

    Se salvar esse arquivo como {@code main.xml}, ele será compilado em um recurso +que pode ser mencionado como {@code R.layout.main}, mas é um alias para o recurso {@code R.layout.main_ltr} +.

    + + +

    Strings e outros valores simples

    + +

    Para criar um alias para uma string existente, basta usar o ID de recurso da string +desejado como o valor para a nova string. Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +    <string name="hello">Hello</string>
    +    <string name="hi">@string/hello</string>
    +</resources>
    +
    + +

    O recurso {@code R.string.hi} é agora um alias para {@code R.string.hello}.

    + +

    Outros valores simples funcionam +da mesma forma. Por exemplo, uma cor:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +    <color name="yellow">#f00</color>
    +    <color name="highlight">@color/red</color>
    +</resources>
    +
    + + + + +

    Fornecimento da melhor compatibilidade de dispositivo com recursos

    + +

    Para o aplicativo suportar várias configurações de dispositivo, é muito importante +que você sempre forneça recursos padrão para cada tipo de recurso que o aplicativo usar.

    + +

    Por exemplo, se o aplicativo suportar vários idiomas, sempre inclua um diretório {@code +values/} (em que as strings sejam salvas) sem um qualificador de região e idioma. Se colocar todos os arquivos de string +em diretórios que têm qualificadores de região e idioma, o aplicativo apresentará erros ao entrar em execução +em dispositivo configurado para um idioma que as strings não suportem. Mas, contanto que você forneça recursos +{@code values/} padrão, o aplicativo será executado sem problemas (mesmo que o usuário +não entenda o idioma — é melhor do que apresentar erros).

    + +

    Do mesmo modo, se você fornecer recursos de layout diferentes com base na orientação da tela, deve +escolher uma orientação como a padrão. Por exemplo, em vez de fornecer recursos de layout em {@code +layout-land/} para paisagem e {@code layout-port/} para retrato, deixe uma como padrão, como +{@code layout/} para paisagem e {@code layout-port/} para retrato.

    + +

    Fornecer recursos padrão é importante não só porque o aplicativo pode ser executado +em uma configuração que você não tenha antecipado, mas também as novas versões do Android, às vezes, adicionam +qualificadores de configuração que as versões mais antigas não suportam. Se usar um novo qualificador de recurso, +mas mantiver a compatibilidade do código com versões mais antigas do Android, quando uma versão mais antiga +do Android executar seu aplicativo, ocorrerá um erro caso você não forneça os recursos padrão, pois ele +não poderá usar os recursos nomeados com o novo qualificador. Por exemplo, se {@code +minSdkVersion} estiver definido como 4 e você qualificar todos os recursos desenháveis usando o modo noturno ({@code night} ou {@code notnight}, que foram adicionados à API de nível 8), +então o dispositivo com API de nível 4 não poderá acessar os recursos desenháveis e apresentará erro. Neste caso, +você provavelmente quererá que {@code notnight} seja o recurso padrão, então deverá excluir esse qualificador +para que os recursos desenháveis fiquem em {@code drawable/} ou {@code drawable-night/}.

    + +

    Então, para fornecer a melhor compatibilidade de dispositivo, sempre forneça os recursos +padrão para os recursos imprescindíveis para o aplicativo para obter o desempenho adequado. Em seguida, +crie recursos para configurações específicas de dispositivo usando os qualificadores de configuração.

    + +

    Há uma exceção a esta regra: Se a {@code minSdkVersion} do aplicativo for 4 +ou maior, você não precisará de recursos desenháveis padrão ao fornecer recursos desenháveis alternativos +com o qualificador de densidade da tela. Mesmo sem os recursos desenháveis +padrão, o Android poderá encontrar a melhor correspondência dentre as densidades de tela alternativas e dimensionar +os bitmaps conforme necessário. No entanto, para obter a melhor experiência em todos os tipos de dispositivo, +você deve fornecer desenháveis alternativos para todos os três tipos de densidade.

    + + + +

    Como o Android encontra o melhor recurso correspondente

    + +

    Ao solicitar um recurso para o qual você fornece alternativas, o Android seleciona +quais recursos alternativos usar em tempo de execução, dependendo da configuração do dispositivo atual. Para demonstrar +como o Android seleciona um recurso alternativo, presuma que os seguintes diretórios desenháveis +contenham versões diferentes das mesmas imagens:

    + +
    +drawable/
    +drawable-en/
    +drawable-fr-rCA/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    + +

    E presuma que a configuração do dispositivo é a seguinte:

    + +

    +Localidade = en-GB
    +Orientação da tela = port
    +Densidade de pixel da tela = hdpi
    +Tipo de tela sensível ao toque = notouch
    +Método principal de entrada de texto = 12key +

    + +

    Ao comparar a configuração do dispositivo com os recursos alternativos disponíveis, o Android seleciona +desenháveis de {@code drawable-en-port}.

    + +

    O sistema chega à conclusão de quais recursos deve usar +com a seguinte lógica:

    + + +
    + +

    Figura 2. Fluxograma de como o Android +encontra o melhor recurso correspondente.

    +
    + + +
      +
    1. Elimine os arquivos de recurso que contradizem a configuração do dispositivo. +

      O diretório drawable-fr-rCA/ é eliminado, +pois contradiz a localidade en-GB.

      +
      +drawable/
      +drawable-en/
      +drawable-fr-rCA/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +drawable-port-ldpi/
      +drawable-port-notouch-12key/
      +
      +

      Exceção: a densidade de pixel da tela é a que o qualificador +não eliminou devido a uma contradição. Apesar de a densidade da tela do dispositivo ser hdpi, +drawable-port-ldpi/ não é eliminado, pois todas as densidades de telas +são consideradas uma correspondência neste ponto. Obtenha mais informações no documento Compatibilidade com +várias telas.

    2. + +
    3. Escolha o (próximo) qualificador de maior precedência na lista (tabela 2). +(Comece com MCC e, em seguida, siga para baixo.)
    4. +
    5. Algum dos diretórios de recurso incluem este qualificador?
    6. +
        +
      • Se não, volte à etapa 2 e veja o próximo qualificador. (Neste exemplo, + a resposta é "não" até que o qualificador de idioma seja alcançado.)
      • +
      • Se sim, prossiga para a etapa 4.
      • +
      + + +
    7. Elimine os diretórios de recurso que não incluem este qualificador. No exemplo, o sistema +elimina todos os diretórios que não incluem um qualificador de idioma:
    8. +
      +drawable/
      +drawable-en/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +drawable-port-ldpi/
      +drawable-port-notouch-12key/
      +
      +

      Exceção: se o qualificador em questão for a densidade de pixel da tensidade da tela +do dispositivo de forma mais aproximada. +Geralmente, o Android prefere dimensionar uma imagem original maior +em vez de uma maior. Consulte Compatibilidade com +várias telas.

      + + +
    9. Volte e repita as etapas 2, 3 e 4 até que reste apenas um diretório. No exemplo, a orientação da tela +é o próximo qualificador, onde há várias correspondências. +Portanto, os recursos que não especificarem uma orientação de tela serão eliminados: +
      +drawable-en/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +
      +

      O diretório restante é {@code drawable-en-port}.

      +
    10. +
    + +

    Apesar de este processo ser executado para cada recurso solicitado, o sistema posteriormente aprimora +alguns aspectos. Tal otimização, quando a configuração do dispositivo é conhecida, +pode eliminar os recursos alternativos que nunca correspondem. Por exemplo, se o idioma +da configuração for inglês ("en"), então qualquer diretório de recurso que tiver um qualificador de idioma definido para +outro idioma que não seja inglês nunca será incluído no conjunto de recursos verificados (apesar de um +diretório de recursos sem o qualificador de idioma ainda ser incluído).

    + +

    Ao selecionar os recursos com base nos qualificadores de tamanho da tela, o sistema usará os recursos +projetados para uma tela menor do que a tela atual, caso não tenha recursos que correspondam de forma mais eficaz +(por exemplo: uma tela de tamanho grande usará os recursos de tela de tamanho normal se necessário). No entanto, +se os únicos recursos disponíveis forem maiores do que a tela atual, o sistema +não os usará e o aplicativo apresentará erros se nenhum outro recurso corresponder à configuração +do dispositivo (por exemplo, se todos os recursos de layout estiverem com a tag do qualificador {@code xlarge}, +mas o dispositivo tiver uma tela de tamanho normal).

    + +

    Observação: a precedência do qualificador (na tabela 2) é mais importante +do que o número de qualificadores que correspondem exatamente ao dispositivo. Por exemplo, na etapa 4 acima, a última +escolha na lista inclui três qualificadores que correspondem exatamente ao dispositivo (orientação, tipo de +tela sensível ao toque e método de entrada), enquanto que drawable-en possui apenas um parâmetro que corresponde +(idioma). No entanto, o idioma tem uma precedência maior que esses outros qualificadores, então +drawable-port-notouch-12key está fora.

    + +

    Para obter mais informações sobre como usar os recursos no aplicativo, acesse Acesso aos recursos.

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/pt-br/guide/topics/resources/runtime-changes.jd new file mode 100644 index 0000000000000000000000000000000000000000..366ce0d2c0728e8a8a94f8b75b904132988ac758 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=Tratar alterações no tempo de execução +page.tags=atividade,ciclo de vida +@jd:body + + + +

    Algumas configurações do dispositivo podem mudar durante o tempo de execução +(como orientação de tela, disponibilidade do teclado e idioma). Quando ocorre uma alteração, +o Android precisa reiniciar a execução +de {@link android.app.Activity} ({@link android.app.Activity#onDestroy()} é chamado, seguido de {@link +android.app.Activity#onCreate(Bundle) onCreate()}). O comportamento de reinício foi projetado para ajudar +o aplicativo a se adaptar a novas configurações recarregando automaticamente o aplicativo +com recursos alternativos que correspondam com a configuração do dispositivo.

    + +

    Para tratar adequadamente um reinício, é importante que a atividade se restaure +ao estado anterior por meio do ciclo de vida +da atividade, no qual o Android chama +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} antes de destruir +a atividade para que seja possível salvar os dados acerca do estado do aplicativo. Em seguida, é possível restaurar o estado +durante {@link android.app.Activity#onCreate(Bundle) onCreate()} ou {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.

    + +

    Para testar se o aplicativo se reinicia com o estado de aplicativo intacto, deve-se +invocar as alterações de configuração (como alterações na orientação da tela) enquanto executa diversas +tarefas no aplicativo. O aplicativo deve ser capaz de reiniciar a qualquer momento sem perda +de dados do usuário ou estado para tratar eventos como alterações de configuração ou quando o usuário recebe +uma chamada telefônica e, em seguida, retorna ao aplicativo bem depois +de destruído o processo do aplicativo. Para ver como restaurar o estado da atividade, leia sobre o Ciclo de vida da atividade.

    + +

    No entanto, pode-se encontrar uma situação em que o reinício do aplicativo +e a restauração representem quantidades significativas de dados que podem ser custosos e prejudicar a experiência do usuário. Nessa situação, +temos duas opções:

    + +
      +
    1. Reter um objeto durante uma alteração de configuração +

      Permita que a atividade reinicie quando uma configuração muda, mas transporte um objeto +de estado para a nova instância da atividade.

      + +
    2. +
    3. Tratar você mesmo da alteração de configuração +

      Evite que o sistema reinicie a atividade durante certas alterações +de configuração, mas receba um retorno de chamada quando as configurações se alteram, para que você atualize manualmente +a atividade conforme necessário.

      +
    4. +
    + + +

    Retenção de um objeto durante uma alteração de configuração

    + +

    Se a retenção da atividade exigir a recuperação de grandes conjuntos de dados, restabelecer uma conexão +de rede ou executar outras operações intensivas, um reinício completo devido a uma alteração +de configuração pode prejudicar a experiência do usuário. Além disso, pode não ser possível restaurar completamente +o estado da atividade com o {@link android.os.Bundle} que o sistema salva com o retorno de chamada {@link +android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} — ele +não foi projetado para transportar objetos grandes (como bitmaps) e os dados contidos devem ser serializados +e, em seguida, desserializados, o que pode consumir muita memória e retardar a alteração de configuração. Nesse caso, +para aliviar o peso de reinicializar a atividade, pode-se reter um {@link +android.app.Fragment} quando a atividade for reiniciada devido a uma alteração de configuração. Esse fragmento +pode conter referências a objetos com estado que seja preciso reter.

    + +

    Quando o sistema Android encerra a atividade devido a uma alteração de configuração, os fragmentos +da atividade marcados para serem retidos não são destruídos. É possível adicionar esses fragmentos +à atividade para preservar objetos de estado.

    + +

    Para reter objetos de estado em um fragmento durante uma alteração de configuração em tempo de execução:

    + +
      +
    1. Estenda a classe {@link android.app.Fragment} e declare referências aos objetos +de estado.
    2. +
    3. Chame {@link android.app.Fragment#setRetainInstance(boolean)} quando o fragmento for criado. +
    4. +
    5. Acrescente o fragmento à atividade.
    6. +
    7. Use {@link android.app.FragmentManager} para recuperar o fragmento quando a atividade for +reiniciada.
    8. +
    + +

    Por exemplo: defina o fragmento da seguinte forma:

    + +
    +public class RetainedFragment extends Fragment {
    +
    +    // data object we want to retain
    +    private MyDataObject data;
    +
    +    // this method is only called once for this fragment
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // retain this fragment
    +        setRetainInstance(true);
    +    }
    +
    +    public void setData(MyDataObject data) {
    +        this.data = data;
    +    }
    +
    +    public MyDataObject getData() {
    +        return data;
    +    }
    +}
    +
    + +

    Atenção: ao restaurar qualquer objeto, +não se deve nunca passar um objeto vinculado a {@link android.app.Activity}, como um {@link +android.graphics.drawable.Drawable}, um {@link android.widget.Adapter}, um {@link android.view.View} +ou qualquer outro objeto associado a um {@link android.content.Context}. Se o fizer, +ele vazará todas as vistas e recursos da instância da atividade original (vazar recursos +significa que o aplicativo mantém a retenção deles, que não podem ser recolhidos, o que +causa perda de memória).

    + +

    Em seguida, use {@link android.app.FragmentManager} para adicionar o fragmento à atividade. +É possível obter o objeto de dados do fragmento quando a atividade reiniciar durante as alterações +de configuração em tempo de execução. Por exemplo: defina a atividade da seguinte forma:

    + +
    +public class MyActivity extends Activity {
    +
    +    private RetainedFragment dataFragment;
    +
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +
    +        // find the retained fragment on activity restarts
    +        FragmentManager fm = getFragmentManager();
    +        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
    +
    +        // create the fragment and data the first time
    +        if (dataFragment == null) {
    +            // add the fragment
    +            dataFragment = new DataFragment();
    +            fm.beginTransaction().add(dataFragment, “data”).commit();
    +            // load the data from the web
    +            dataFragment.setData(loadMyData());
    +        }
    +
    +        // the data is available in dataFragment.getData()
    +        ...
    +    }
    +
    +    @Override
    +    public void onDestroy() {
    +        super.onDestroy();
    +        // store the data in the fragment
    +        dataFragment.setData(collectMyLoadedData());
    +    }
    +}
    +
    + +

    Nesse exemplo, {@link android.app.Activity#onCreate(Bundle) onCreate()} adiciona um fragmento +ou restaura uma referência a ele. {@link android.app.Activity#onCreate(Bundle) onCreate()} também +armazena o objeto de estado dentro da instância de fragmento. +{@link android.app.Activity#onDestroy() onDestroy()} atualiza o objeto de estado dentro +da instância de fragmento retida.

    + + + + + +

    Tratar você mesmo da alteração de configuração

    + +

    Se o aplicativo não tiver que atualizar recursos durante uma alteração de configuração específica +e se houver alguma limitação de desempenho que +impeça a atividade de reiniciar, pode-se declarar que a atividade trata ela mesma da alteração de configuração, +o que evita que o sistema reinicie a atividade.

    + +

    Observação: Tratar você mesmo da alteração de configuração +pode dificultar muito o uso de recursos alternativos, pois o sistema não os aplicará +automaticamente. Esta técnica deve ser considerada um último recurso, quando é preciso evitar reinícios +devido a uma alteração de configuração e não é recomendada para a maioria dos aplicativos.

    + +

    Para declarar que a atividade manipula uma alteração de configuração, edite o elemento {@code <activity>} +apropriado no arquivo de manifesto para que inclua o atributo {@code +android:configChanges} com um valor que represente a configuração +a tratar. Os valores possíveis estão listados na documentação do atributo {@code +android:configChanges} (os valores mais comumente usados são {@code "orientation"}, para +impedir reinícios durante alterações na orientação da tela, e {@code "keyboardHidden"} para impedir +reinícios quando a disponibilidade do teclado muda). Para declarar vários valores de configuração +no atributo, usa-se um separador na forma de caractere barra reta {@code |}.

    + +

    Por exemplo: o código de manifesto a seguir declara uma atividade que trata tanto +da alteração de orientação da tela quanto da disponibilidade do teclado:

    + +
    +<activity android:name=".MyActivity"
    +          android:configChanges="orientation|keyboardHidden"
    +          android:label="@string/app_name">
    +
    + +

    Agora, quando uma dessas configurações mudar, {@code MyActivity} não reiniciará. +Em vez disso, a {@code MyActivity} recebe uma chamada para {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Um objeto +{@link android.content.res.Configuration} é passado a esse método e especifica +a nova configuração do dispositivo. Ao ler os campos em {@link android.content.res.Configuration}, +pode-se determinar a nova configuração e atualizar os recursos na interface para fazer +as alterações adequadas. No momento +em que o método é chamado, o objeto {@link android.content.res.Resources} da atividade é atualizado +para retornar recursos com base na nova configuração, o que facilita +a redefinição de elementos da IU sem que o sistema reinicie a atividade.

    + +

    Atenção: a partir do Android 3.2 (nível da API 13), o "tamanho +da tela" também muda quando o dispositivo alterna entre as orientações retrato +e paisagem. Assim, se você deseja evitar que o tempo de execução reinicie devido a uma mudança da orientação +ao desenvolver uma API nível 13 ou posterior (conforme declarado pelos atributos {@code minSdkVersion} e {@code targetSdkVersion}), +é preciso incluir o valor {@code "screenSize"} além do valor {@code +"orientation"}. Ou seja, é preciso declarar {@code +android:configChanges="orientation|screenSize"}. No entanto, se o aplicativo tem como alvo uma API nível +12 ou inferior, a atividade sempre trata ela mesma a alteração de configuração (essa mudança +de configuração não reinicia a atividade, mesmo em execução em Android 3.2 ou dispositivo posterior).

    + +

    Por exemplo: a implementação a seguir {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} verifica +a orientação de dispositivo atual:

    + +
    +@Override
    +public void onConfigurationChanged(Configuration newConfig) {
    +    super.onConfigurationChanged(newConfig);
    +
    +    // Checks the orientation of the screen
    +    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    +        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    +    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
    +        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    +    }
    +}
    +
    + +

    O objeto {@link android.content.res.Configuration} representa todas as configurações +atuais, não somente as que foram alteradas. Na maior parte do tempo, não importa como +a configuração foi alterada; basta reatribuir todos os recursos que apresentam alternativas +à configuração que estão sendo tratadas. Por exemplo: como o objeto {@link +android.content.res.Resources} está atualizado, pode-se redefinir +qualquer {@link android.widget.ImageView} com {@link android.widget.ImageView#setImageResource(int) +setImageResource()} +e será usado o recurso adequado à nova configuração (conforme descrito em Como fornecer recursos).

    + +

    Observe que os valores dos campos de {@link +android.content.res.Configuration} são inteiros que correspondem a constantes específicas +da classe {@link android.content.res.Configuration}. Para ver a documentação sobre as constantes +a usar em cada campo, consulte o campo em questão na referência sobre {@link +android.content.res.Configuration}.

    + +

    Lembre-se: ao declarar a atividade para tratar uma alteração +de configuração, você é responsável por redefinir todos os elementos que fornecem alternativas. Se você +declarar a atividade para tratar a alteração de orientação e tiver imagens que alterariam +entre paisagem e retrato, é preciso reatribuir cada recurso a cada elemento durante {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.

    + +

    Se não for necessário atualizar o aplicativo com base nessas alterações +de configuração, pode-se não implementar {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Nesse +caso, todos os recursos usados antes da alteração de configuração ainda são usados +e somente o reinício da atividade é evitado. No entanto, o aplicativo deve sempre ser capaz +de se encerrar e reiniciar com seu estado anterior intacto, portanto essa técnica não deve +ser considerada uma fuga da retenção do estado durante o ciclo de vida normal da atividade, Não somente porque +há outras alterações de configuração impossíveis de evitar que reiniciem o aplicativo, +mas também porque devem-se tratar eventos como o do usuário que sai do aplicativo e ele é destruído +antes de o usuário voltar a ele.

    + +

    Para obter mais informações sobre as alterações de configuração que devem ser tratadas na atividade, consulte a documentação sobre {@code +android:configChanges} e a classe +{@link android.content.res.Configuration}.

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/controls.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/controls.jd new file mode 100644 index 0000000000000000000000000000000000000000..58a4fcdbe5a06d2cc6e1305a1d1bde7117282a4f --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=Controles de entrada +parent.title=Interface do usuário +parent.link=index.html +@jd:body + +
    + +
    + +

    Controles de entrada são os componentes interativos na interface do usuário de seu aplicativo. O Android oferece +uma ampla variedade de controles que podem ser usados na IU, como botões, campos de texto, barras de busca, +caixas de seleção, botões de zoom, botões de alternância e muito mais.

    + +

    Adicionar um controle de entrada à IU é tão simples quanto adicionar um elemento XML ao layout XML. Por exemplo, a seguir apresenta-se +um layout com um campo de texto e um botão:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:orientation="horizontal">
    +    <EditText android:id="@+id/edit_message"
    +        android:layout_weight="1"
    +        android:layout_width="0dp"
    +        android:layout_height="wrap_content"
    +        android:hint="@string/edit_message" />
    +    <Button android:id="@+id/button_send"
    +        android:layout_width="wrap_content"
    +        android:layout_height="wrap_content"
    +        android:text="@string/button_send"
    +        android:onClick="sendMessage" />
    +</LinearLayout>
    +
    + +

    Cada controle de entrada tem suporte para um conjunto específico de eventos de entrada, portanto, você pode tratar eventos como quando +o usuário digita texto ou toca em um botão.

    + + +

    Controles comuns

    +

    A seguir há uma lista de alguns controles comuns que você pode usar no aplicativo. Siga os links para saber +mais sobre o uso de cada um.

    + +

    Observação: o Android fornece muitos outros controles além dos listados +aqui. Navegue no pacote {@link android.widget} para descobrir mais. Se o aplicativo precisa +de um tipo específico de controle de entrada, você pode criar os próprios componentes personalizados.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Tipo de controleDescriçãoClasses relacionadas
    BotãoUm botão de pressão que pode ser pressionado ou clicado pelo usuário para realizar uma ação.{@link android.widget.Button Button}
    Campo de textoUm campo de texto editável. É possível usar o widget AutoCompleteTextView para criar um widget de entrada de texto que forneça sugestões para preenchimento automático{@link android.widget.EditText EditText}, {@link android.widget.AutoCompleteTextView}
    Caixa de seleçãoUma chave liga/desliga que pode ser alternada pelo usuário. Use caixas de seleção ao apresentar aos usuários um grupo de opções selecionáveis que não sejam mutualmente exclusivas.{@link android.widget.CheckBox CheckBox}
    Botão de opçãoSimilar às caixas de seleção, exceto que somente uma opção pode ser selecionada no grupo.{@link android.widget.RadioGroup RadioGroup} +
    {@link android.widget.RadioButton RadioButton}
    Botão de alternânciaUm botão liga/desliga com um indicador de luz.{@link android.widget.ToggleButton ToggleButton}
    Controle giratórioUma lista suspensa que permite que os usuários selecionem um valor de um conjunto.{@link android.widget.Spinner Spinner}
    SeletoresUma caixa de diálogo para que os usuários selecionem um valor para um conjunto usando botões para cima/para baixo ou via gesto de deslizar. Use um widget DatePickercode> para inserir os valores para a data (mês, dia, ano) ou um widget TimePicker para inserir valores para um horário (hora, minuto, AM/PM), que será formatado automaticamente para a localidade do usuário.{@link android.widget.DatePicker}, {@link android.widget.TimePicker}
    diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000000000000000000000000000000..09dbd2cee275a66d220497cd0456d23a37b20ede --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=Layouts +page.tags=vista,grupodevistas +@jd:body + +
    +
    +

    Neste documento

    +
      +
    1. Programação do XML
    2. +
    3. Carregamento do recurso XML
    4. +
    5. Atributos +
        +
      1. ID
      2. +
      3. Parâmetros do layout
      4. +
      +
    6. +
    7. Posição do layout
    8. +
    9. Tamanho, preenchimento e margens
    10. +
    11. Layouts comuns
    12. +
    13. Criação de layouts com um adaptador +
        +
      1. Preenchimento da vista de um adaptador com dados
      2. +
      3. Tratamento de eventos de clique
      4. +
      +
    14. +
    + +

    Classes principais

    +
      +
    1. {@link android.view.View}
    2. +
    3. {@link android.view.ViewGroup}
    4. +
    5. {@link android.view.ViewGroup.LayoutParams}
    6. +
    + +

    Veja também

    +
      +
    1. Construção de uma interface do usuário +simples
    +
    + +

    O layout define a estrutura visual para uma interface do usuário, como a IU de uma atividade ou de um widget de aplicativo. +É possível declarar um layout de dois modos:

    +
      +
    • Declarar elementos da IU em XML. O Android fornece um vocabulário XML +direto que corresponde às classes e subclasses de View, como as de widgets e layouts.
    • +
    • Instanciar elementos do layout em tempo de execução. +O aplicativo pode criar objetos de View e ViewGroup (e manipular suas propriedades) programaticamente.
    • +
    + +

    A estrutura do Android é flexível para usar um destes métodos ou ambos para declarar e gerenciar a IU do seu aplicativo. Por exemplo: você pode declarar um layout padrão do aplicativo em XML, e incluir os elementos da tela que aparecerão neles e suas propriedades. Em seguida, você poderia adicionar códigos ao aplicativo que modificariam o estado dos objetos da tela, inclusive os declarados em XML, em tempo de execução.

    + + + +

    A vantagem de declarar a IU em XML é separar melhor a apresentação do aplicativo a partir do código que controla seu comportamento. As descrições da IU são externas ao código do aplicativo, ou seja, é possível modificá-la ou adaptá-la sem modificar o código-fonte e recompilar. Por exemplo: é possível criar layouts XML para diferentes orientações de tela, diferentes tamanhos de tela de dispositivos e diferentes idiomas. Além disso, a declaração de layout em XML facilita a exibição da estrutura da sua IU, o que facilita a depuração de problemas. Assim sendo, este documento se concentrará em ensinar a declarar o layout em XML. Se você +estiver interessado em instanciar objetos View em tempo de execução, consulte as referências das classes +{@link android.view.ViewGroup} e {@link android.view.View}.

    + +

    Em geral, o vocabulário XML para declarar elementos da IU segue rigorosamente a estrutura e a atribuição de nome às classes e aos métodos, em que os nomes de elementos correspondem a nomes de classe e nomes de atributos correspondem a métodos. Na verdade, a correspondência normalmente é tão direta que é possível supor qual atributo XML corresponde a determinado método de classe ou supor qual classe corresponde a certo elemento XML. Contudo, observe que nem todo vocabulário é idêntico. Em alguns casos, há pequenas diferenças de nome. Por exemplo: +o elemento EditText tem um atributo text que corresponde +a EditText.setText().

    + +

    Dica: Veja os diferentes tipos de layout em Objetos +de layout comuns. Também há uma coleção de tutoriais sobre a criação de diversos layouts +no guia de tutorial Vistas de boas-vindas.

    + +

    Programação do XML

    + +

    Usando o vocabulário XML do Android, é possível projetar rapidamente layouts de IU e os elementos de tela intrínsecos do mesmo modo que se cria páginas web em HTML — com uma série de elementos aninhados.

    + +

    Cada arquivo de layout deve conter exatamente um elemento raiz, que deve ser um objeto View ou ViewGroup. Com o elemento raiz definido, é possível adicionar objetos ou widgets de layout extras como elementos filho para construir gradualmente uma hierarquia de View que define o layout. Por exemplo: eis um layout XML que usa um {@link android.widget.LinearLayout} vertical +para conter uma {@link android.widget.TextView} e um {@link android.widget.Button}:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +              android:layout_width="match_parent"
    +              android:layout_height="match_parent"
    +              android:orientation="vertical" >
    +    <TextView android:id="@+id/text"
    +              android:layout_width="wrap_content"
    +              android:layout_height="wrap_content"
    +              android:text="Hello, I am a TextView" />
    +    <Button android:id="@+id/button"
    +            android:layout_width="wrap_content"
    +            android:layout_height="wrap_content"
    +            android:text="Hello, I am a Button" />
    +</LinearLayout>
    +
    + +

    Após declarar o layout no XML, salve o arquivo com uma extensão .xml +no diretório res/layout/ do projeto do Android para compilá-lo adequadamente.

    + +

    Veja mais informações sobre a sintaxe de um arquivo XML de layout no documento Recursos de layout.

    + +

    Carregamento do recurso XML

    + +

    Ao compilar o aplicativo, cada arquivo de layout XML é compilado +em um recurso {@link android.view.View}. Deve-se carregar o recurso de layout do código do aplicativo +na implementação de retorno de chamada {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()}. +Para isso, chame {@link android.app.Activity#setContentView(int) setContentView()}, +passando a referência para o recurso de layout na forma: +R.layout.layout_file_name. +Por exemplo: se o layout XML for salvo como main_layout.xml, será necessário carregá-lo +para a Atividade desta forma:

    +
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.main_layout);
    +}
    +
    + +

    O método de retorno de chamada onCreate() na Atividade é chamado pela estrutura do Android quando +ela é inicializada (veja a discussão sobre ciclos de vida no documento +Atividades +).

    + + +

    Atributos

    + +

    Cada objeto View e ViewGroup aceita uma variedade própria de atributos XML. +Alguns atributos são específicos de um objeto View (por exemplo, TextView aceita o atributo +textSize), mas esses atributos também são herdados de objetos View que possam estender essa classe. +Alguns são comuns a todos os objetos View porque são herdados da classe View raiz (como +o atributo id). Além disso, outros atributos são considerados "parâmetros do layout", que são +os que descrevem determinadas orientações de layout do objeto View conforme definido pelo objeto +ViewGroup pai daquele objeto.

    + +

    ID

    + +

    Qualquer objeto View pode ter um ID de número inteiro associado para identificar exclusivamente o View dentro da árvore. +Ao compilar o aplicativo, esse ID é referenciado como um número inteiro, mas o ID normalmente +é atribuído no arquivo XML do layout como uma string, no atributo id. +É um atributo XML comum a todos os objetos View +(definido pela classe {@link android.view.View}) e você o usará com frequência. +A sintaxe de um ID, dentro de uma tag XML, é:

    +
    android:id="@+id/my_button"
    + +

    Um símbolo de arroba (@) no início da string indica que o analisador XML deve analisar e expandir o restante +da string de ID e identificá-la como um recurso de ID. O símbolo de mais (+) significa que é um novo nome de recurso que precisa +ser criado e adicionado aos recursos (no arquivo R.java). Há diversos outros recursos de ID +oferecidos pela estrutura do Android. Ao referenciar um ID de recurso do Android, não é necessário ter o símbolo de mais, +mas deve-se adicionar o conjunto de nomes (namespace) do pacote android da seguinte maneira:

    +
    android:id="@android:id/empty"
    +

    Com o conjunto de nomes (namespace) do pacote android em vigor, podemos referenciar um ID da classe +de recursos android.R em vez de um da classe de recursos locais.

    + +

    Para criar vistas e referenciá-las a partir do aplicativo, um modo padrão comum é:

    +
      +
    1. Definir uma visualizaçãovistawidget no arquivo de layout e atribuir um ID exclusivo a ele/ela: +
      +<Button android:id="@+id/my_button"
      +        android:layout_width="wrap_content"
      +        android:layout_height="wrap_content"
      +        android:text="@string/my_button_text"/>
      +
      +
    2. +
    3. Em seguida, crie uma instância do objeto de vista e capture-a do layout +(normalmente no método {@link android.app.Activity#onCreate(Bundle) onCreate()}): +
      +Button myButton = (Button) findViewById(R.id.my_button);
      +
      +
    4. +
    +

    Definir IDs para objetos de vista é importante ao criar um {@link android.widget.RelativeLayout}. +Em um layout relativo, vistas irmãs podem definir o layout relativo para outra vista irmã +referenciada pelo ID exclusivo.

    +

    Os Ids não precisam ser exclusivo por toda a árvore, mas devem ser +exclusivos dentro da parte da árvore em que se está procurando (que pode, com frequência, ser toda a árvore, portanto é preferível +ser totalmente exclusivo sempre que possível).

    + + +

    Parâmetros do layout

    + +

    Os atributos do layout XML chamados layout_something definem +parâmetros para a View que for apropriado para a ViewGroup em que reside.

    + +

    Cada classe ViewGroup implementa uma classe aninhada que estende {@link +android.view.ViewGroup.LayoutParams}. Essa subclasse +contém tipos de propriedade que definem o tamanho e a posição de cada vista filha, conforme +o necessário para o grupo de vistas. Como se pode ver na figura 1, um grupo de vistas +pais define parâmetros de layout para cada vista filha (incluindo o grupo de vistas filhas).

    + + +

    Figura 1. Vista de uma hierarquia de vistas com parâmetros +de layout associados a cada uma delas.

    + +

    Observe que cada subclasse LayoutParams tem a própria sintaxe para definir +valores. Cada elemento filho deve definir LayoutParams apropriados para seu pai, +embora possa também definir diferentes LayoutParams para os próprios filhos.

    + +

    Todos os grupos de vistas contêm largura e altura (layout_width e +layout_height) e cada vista é obrigatória para defini-las. Muitos +LayoutParams também contêm margens e bordas opcionais.

    + +

    É possível especificar largura e altura com medidas exatas, embora não seja +recomendável na maioria dos casos. Em geral, usa-se uma destas constantes para +definir a largura e a altura:

    + +
      +
    • wrap_content instrui a vista a dimensionar-se de acordo com +as medidas exigidas pelo conteúdo.
    • +
    • match_parent (de nome fill_parent antes da API de nível 8) +instrui a vista a tornar-se tão grande quanto o grupo de vistas pais permitir.
    • +
    + +

    Em geral, a especificação de largura e altura de um layout com unidades absolutas, como +pixels, não é recomendada. Em vez disso, usam-se medidas relativas como +unidades de pixel independentes de densidade (dp), wrap_contentou +match_parent, é uma abordagem melhor porque ajuda a garantir +que o aplicativo exiba o conteúdo adequadamente dentre diversos tamanhos de tela de dispositivos. +Os tipos de medidas aceitos são definidos no documento + +Recursos disponíveis.

    + + +

    Posição do layout

    +

    + A geometria de uma vista de um retângulo. As vistas têm uma localização, + expressa como um par de coordenadas esquerda e topo + e duas dimensões, expressas como largura e altura. A unidade de localização + e de dimensões é o pixel. +

    + +

    + É possível recuperar a localização de uma vista chamando os métodos + {@link android.view.View#getLeft()} e {@link android.view.View#getTop()}. O primeiro retorna a coordenada + esquerda, ou X, do retângulo que representa a vista. O último retorna a coordenada + topo, ou Y, do retângulo que representa a vista. Esses métodos + retornam a localização da vista em relação ao pai correspondente. Por exemplo: + quando getLeft() retorna 20, significa que a vista se localiza 20 pixels à + direita da borda esquerda do seu pai direto. +

    + +

    + Adicionalmente, diversos métodos de conveniência são oferecidos para evitar cálculos +desnecessárias, chamados {@link android.view.View#getRight()} e {@link android.view.View#getBottom()}. + Esses métodos retornam as coordenadas das bordas direita e de baixo + do retângulo que representa a vista. Por exemplo: chamar {@link android.view.View#getRight()} + é semelhante ao seguinte cálculo: getLeft() + getWidth(). +

    + + +

    Tamanho, preenchimento e margens

    +

    + O tamanho de uma vista é expresso por largura e altura. As vistas, na verdade, + têm dois pares de valores de largura e altura. +

    + +

    + O primeiro par é conhecido como largura medida + e altura medida. Essas dimensões definem o tamanho que a vista terá + dentro da vista pai. + Para obter as dimensões medidas, chamam-se {@link android.view.View#getMeasuredWidth()} + e {@link android.view.View#getMeasuredHeight()}. +

    + +

    + O segundo par é simplesmente conhecido como largura e altura ou, + às vezes, largura do desenho e altura do desenho. Essas + dimensões definem o tamanho real da vista na tela, em tempo de desenho e + após o layout. Esses valores podem diferir da largura + e da altura medidas. Para obter os valores de largura e altura, chamam-se + {@link android.view.View#getWidth()} e {@link android.view.View#getHeight()}. +

    + +

    + Para medir as dimensões, a vista leva em conta o preenchimento. O preenchimento + é expresso em pixels para a esquerda, a direita e as partes de cima e de baixo da vista. + O preenchimento pode ser usado para compensar o conteúdo da vista por um número específico + de pixels. Por exemplo: um preenchimento à esquerda de 2 empurrará o conteúdo da vista + em 2 pixels para a direita da borda esquerda. Para definir o preenchimento, usa-se + o método {@link android.view.View#setPadding(int, int, int, int)} e consulta-se com as chamadas + {@link android.view.View#getPaddingLeft()}, {@link android.view.View#getPaddingTop()}, + {@link android.view.View#getPaddingRight()} e {@link android.view.View#getPaddingBottom()}. +

    + +

    + Mesmo que cada vista possa definir um preenchimento, ela não fornece nenhuma compatibilidade + com margens. No entanto, os grupos de vistas oferecem essa compatibilidade. Consulte + {@link android.view.ViewGroup} + e {@link android.view.ViewGroup.MarginLayoutParams} para ver mais informações. +

    + +

    Para obter mais informações sobre dimensões, consulte + Valores de dimensões. +

    + + + + + + + + + + + +

    Layouts comuns

    + +

    Cada subclasse da classe {@link android.view.ViewGroup} fornece um modo exclusivo de exibir +as vistas aninhadas dentro dela. Eis alguns dos tipos de layout mais comuns criados +na plataforma Android.

    + +

    Observação: Embora seja possível aninhar um ou mais layouts em outro +layout para obter o projeto de IU, deve-se procurar manter a hierarquia do layout a menos profunda +possível. O layout carrega mais rápido se tiver menos layouts aninhados (uma hierarquia de vistas grande é +melhor do que uma hierarquia de vistas profunda).

    + + + + +
    +

    Layout linear

    + +

    Layout que organiza os filhos em uma única linha horizontal ou vertical. Ele + cria uma barra de rolagem se o comprimento da janela exceder o comprimento da tela.

    +
    + +
    +

    Layout relativo

    + +

    Permite especificar a localização de objetos filhos relativos entre si (filho A +à esquerda do filho B) ou relativos aos pais (alinhados no topo do pai).

    +
    + +
    +

    Vista web

    + +

    Exibe páginas da web.

    +
    + + + + +

    Criação de layouts com um adaptador

    + +

    Quando o conteúdo do layout é dinâmico ou não predeterminado, é possível usar um layout que +torne {@link android.widget.AdapterView} uma subclasse para preencher o layout com vistas em tempo de execução. +Uma subclasse da classe {@link android.widget.AdapterView} usa um {@link android.widget.Adapter} +para agrupar dados ao seu layout. O {@link android.widget.Adapter} se comporta como um intermediário entre a fonte +dos dados e o layout do {@link android.widget.AdapterView} — o {@link android.widget.Adapter} +recupera os dados (de uma fonte como uma matriz ou uma consulta de banco de dados) e converte cada entrada +em uma vista que pode ser adicionada ao layout do {@link android.widget.AdapterView}.

    + +

    Alguns layouts comuns retornados por um adaptador:

    + +
    +

    Vista em lista

    + +

    Exibe uma lista de rolagem de coluna única.

    +
    + +
    +

    Vista em grade

    + +

    Exibe uma grade de rolagem de colunas e linhas.

    +
    + + + +

    Preenchimento da vista de adaptador com dados

    + +

    É possível preencher um {@link android.widget.AdapterView} como {@link android.widget.ListView} +ou {@link android.widget.GridView} agrupando-se a instância do {@link android.widget.AdapterView} +a um {@link android.widget.Adapter}, o que recupera dados de uma fonte externa e cria uma {@link +android.view.View} que representa cada entrada de dados.

    + +

    O Android oferece diversas subclasses de {@link android.widget.Adapter} que são úteis para +recuperar diferentes tipos de dados e criar vistas de um {@link android.widget.AdapterView}. +Os dois adaptadores mais comuns são:

    + +
    +
    {@link android.widget.ArrayAdapter}
    +
    Use esse adaptador quando a fonte de dados for uma matriz. Por padrão, {@link +android.widget.ArrayAdapter} cria uma vista para cada item de matriz chamando {@link +java.lang.Object#toString()} em cada item e posicionando o conteúdo em uma {@link +android.widget.TextView}. +

    Por exemplo: se você tiver uma matriz de strings que deseja exibir em uma {@link +android.widget.ListView}, inicialize um novo {@link android.widget.ArrayAdapter} com +um construtor para especificar o layout de cada string e a matriz de strings:

    +
    +ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
    +        android.R.layout.simple_list_item_1, myStringArray);
    +
    +

    Os argumentos desse construtor são:

    +
      +
    • O {@link android.content.Context} do aplicativo
    • +
    • O layout que contém uma {@link android.widget.TextView} para cada string na matriz
    • +
    • A matriz de strings
    • +
    +

    Em seguida, simplesmente chame +{@link android.widget.ListView#setAdapter setAdapter()} na {@link android.widget.ListView}:

    +
    +ListView listView = (ListView) findViewById(R.id.listview);
    +listView.setAdapter(adapter);
    +
    + +

    Para personalizar a aparência de cada item, é possível suspender o método {@link +java.lang.Object#toString()} para os objetos na matriz. Ou, para criar uma vista para cada +item diferente de uma {@link android.widget.TextView} (por exemplo, se você quiser +uma {@link android.widget.ImageView} para cada item da matriz), estenda a classe {@link +android.widget.ArrayAdapter} e suspenda {@link android.widget.ArrayAdapter#getView +getView()} para retornar o tipo de vista que deseja para cada item.

    + +
    + +
    {@link android.widget.SimpleCursorAdapter}
    +
    Use este adaptador quando os dados vierem de um {@link android.database.Cursor}. +Ao usar {@link android.widget.SimpleCursorAdapter}, é necessário especificar um layout a usar para cada +linha no {@link android.database.Cursor} e que colunas no {@link android.database.Cursor} +devem ser inseridas em determinadas vistas do layout. Por exemplo: se você deseja criar uma lista +de nome e número de telefone de pessoas, pode-se realizar uma consulta que retorna um {@link +android.database.Cursor} que contém uma linha para cada pessoa e colunas para os nomes +e números. Assim, cria-se uma matriz de strings especificando quais colunas do {@link +android.database.Cursor} estarão no layout para cada resultado e uma matriz de números inteiros especificando +as vistas correspondentes em que cada coluna deve ser colocada:

    +
    +String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
    +                        ContactsContract.CommonDataKinds.Phone.NUMBER};
    +int[] toViews = {R.id.display_name, R.id.phone_number};
    +
    +

    Ao instanciar o {@link android.widget.SimpleCursorAdapter}, passe o layout a usar +para cada resultado, o {@link android.database.Cursor} contendo os resultados e estas duas matrizes:

    +
    +SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
    +        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    +ListView listView = getListView();
    +listView.setAdapter(adapter);
    +
    +

    Em seguida, o {@link android.widget.SimpleCursorAdapter} cria uma vista de cada linha +no{@link android.database.Cursor} usando o layout fornecido por meio da inserção de cada item de {@code +fromColumns} na vista {@code toViews} correspondente.

    .
    +
    + + +

    Se durante o curso de vida do aplicativo, você mudar os dados subjacentes lidos +pelo adaptador, chame {@link android.widget.ArrayAdapter#notifyDataSetChanged()}. Isso +notificará à vista anexada que os dados foram alterados e que ela deve se atualizar.

    + + + +

    Tratamento de eventos de clique

    + +

    Para responder a eventos de clique em cada item em um {@link android.widget.AdapterView}, +implementa-se a interface {@link android.widget.AdapterView.OnItemClickListener}. Por exemplo:

    + +
    +// Create a message handling object as an anonymous class.
    +private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
    +    public void onItemClick(AdapterView parent, View v, int position, long id) {
    +        // Do something in response to the click
    +    }
    +};
    +
    +listView.setOnItemClickListener(mMessageClickedHandler);
    +
    + + + diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/dialogs.jd new file mode 100644 index 0000000000000000000000000000000000000000..2cbedbebdebf3c37e1f5c61d801c3e4b6c89d5b5 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=Caixas de diálogo +page.tags=diálogodealerta,fragmentodediálogo + +@jd:body + + + + + +

    As caixas de diálogo são pequenas janelas que levam o usuário +a tomar uma decisão ou inserir informações adicionais. Elas não ocupam toda a tela e são +normalmente susada para eventos modais que exijam que usuários realizem uma ação antes de continuar.

    + +
    +

    Projeto de caixas de diálogo

    +

    Para obter mais informações sobre como projetar caixas de diálogo, inclusive sobre recomendações + de idioma, leia o guia de projeto Caixas de diálogo.

    +
    + + + +

    A classe {@link android.app.Dialog} é a classe de base para caixas de diálogo, mas +deve-se evitar instanciar {@link android.app.Dialog} diretamente. +Em vez disso, use uma das subclasses a seguir:

    +
    +
    {@link android.app.AlertDialog}
    +
    Caixa de diálogo que pode exibir um título, até três botões, uma lista + de itens selecionáveis ou um layout personalizado.
    +
    {@link android.app.DatePickerDialog} ou {@link android.app.TimePickerDialog}
    +
    Caixa de diálogo com uma IU predefinida que permite ao usuário selecionar uma data ou hora.
    +
    + + + +

    Essas classes definem o estilo e a estrutura da caixa de diálogo, mas deve-se +usar um {@link android.support.v4.app.DialogFragment} como um contêiner para a caixa de diálogo. +A classe {@link android.support.v4.app.DialogFragment} fornece todos os controles +necessários para criar uma caixa de diálogo e gerenciar sua aparência em vez de chamar métodos +no objeto {@link android.app.Dialog}.

    + +

    O uso de {@link android.support.v4.app.DialogFragment} para gerenciar a caixa de diálogo +garante que ela trate corretamente os eventos de ciclos de vida, +como quando o usuário pressiona o botão Voltar ou gira a tela. A classe {@link +android.support.v4.app.DialogFragment} também permite a reutilização da IU da caixa de diálogo como +um componente incorporável em uma IU maior, assim como um {@link +android.support.v4.app.Fragment} tradicional (como nas vezes em que se deseja que a IU da caixa de diálogo apareça de modos diferentes +em telas grandes e pequenas).

    + +

    As seções a seguir neste guia descrevem como usar um {@link +android.support.v4.app.DialogFragment} combinado com um objeto +{@link android.app.AlertDialog}. Se você quiser criar um seletor de data ou hora, leia o guia +Seletores.

    + +

    Observação: +como a classe {@link android.app.DialogFragment} foi adicionada originalmente com +o Android 3.0 (API de nível 11), este documento descreve como usar a classe {@link +android.support.v4.app.DialogFragment} fornecida com a Biblioteca de Suporte. Ao adicionar essa biblioteca +ao aplicativo, pode-se usar {@link android.support.v4.app.DialogFragment} e uma variedade de outras +APIs em dispositivos que executam o Android 1.6 ou versão posterior. Se a versão mais antiga com a qual seu aplicativo é compatível +for a API de nível 11 ou posterior, é possível usar a versão de estrutura de {@link +android.app.DialogFragment}, mas esteja ciente de que os links neste documento são para APIs +de biblioteca de suporte. Ao usar a biblioteca de suporte, +certifique-se de importar a classe android.support.v4.app.DialogFragment +e não a android.app.DialogFragment.

    + + +

    Criação de um fragmento de caixa de diálogo

    + +

    É possível realizar uma grande variedade de projetos de caixas de diálogo — inclusive +layouts personalizados e outros descritos no guia de projeto Caixas de diálogo + —, estendendo +{@link android.support.v4.app.DialogFragment} e criando uma{@link android.app.AlertDialog} +no método de retorno de chamada {@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()}.

    + +

    Por exemplo, a seguir há um {@link android.app.AlertDialog} básico gerenciado dentro +de um {@link android.support.v4.app.DialogFragment}:

    + +
    +public class FireMissilesDialogFragment extends DialogFragment {
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // Use the Builder class for convenient dialog construction
    +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +        builder.setMessage(R.string.dialog_fire_missiles)
    +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // FIRE ZE MISSILES!
    +                   }
    +               })
    +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // User cancelled the dialog
    +                   }
    +               });
    +        // Create the AlertDialog object and return it
    +        return builder.create();
    +    }
    +}
    +
    + +
    + +

    Figura 1. +Caixa de diálogo com uma mensagem e dois botões de ação.

    +
    + +

    Ao criar uma instância dessa classe e chamar {@link +android.support.v4.app.DialogFragment#show show()} nesse objeto, a caixa de diálogo será exibida como +ilustrado na figura 1.

    + +

    A próxima seção descreve em mais detalhes o uso das APIs{@link android.app.AlertDialog.Builder} +para a criação de uma caixa de diálogo.

    + +

    Conforme o nível de complexidade da caixa de diálogo, é possível implementar diversos outros métodos +de retorno de chamada no {@link android.support.v4.app.DialogFragment}, inclusive todos os +métodos de ciclo de vida de fragmentos básicos. + + + + + +

    Construção de uma caixa de diálogo de alerta

    + + +

    A classe {@link android.app.AlertDialog} permite a criação de diversos projetos de caixa de diálogo +e normalmente é a única classe de caixa de diálogo necessária. +Como ilustrado na figura 2, há três regiões de uma caixa de diálogo de alerta:

    + +
    + +

    Figura 2. Layout de uma caixa de diálogo.

    +
    + +
      +
    1. Título +

      É opcional e deve ser usado somente quando a área do conteúdo + estiver ocupada por uma mensagem detalhada, uma lista ou layout personalizado. Se for necessário declarar + uma mensagem ou pergunta simples (como a caixa de diálogo na figura 1), o título não é necessário.

    2. +
    3. Área do conteúdo +

      Pode exibir uma mensagem, uma lista ou outro layout personalizado.

    4. +
    5. Botões de ação +

      Não deve haver mais de três botões em uma caixa de diálogo.

    6. +
    + +

    A classe {@link android.app.AlertDialog.Builder} + fornece APIs que permitem a criação de uma {@link android.app.AlertDialog} + com esses tipos de conteúdo, inclusive um layout personalizado.

    + +

    Para criar uma {@link android.app.AlertDialog}:

    + +
    +// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor
    +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +
    +// 2. Chain together various setter methods to set the dialog characteristics
    +builder.setMessage(R.string.dialog_message)
    +       .setTitle(R.string.dialog_title);
    +
    +// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}
    +AlertDialog dialog = builder.create();
    +
    + +

    Os tópicos a seguir mostram como definir diversos atributos de caixas de diálogo +com a classe {@link android.app.AlertDialog.Builder}.

    + + + + +

    Adição de botões

    + +

    Para adicionar botões de ação como os da figura 2, +chame os métodos {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} e +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()}:

    + +
    +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +// Add the buttons
    +builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    +           public void onClick(DialogInterface dialog, int id) {
    +               // User clicked OK button
    +           }
    +       });
    +builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +           public void onClick(DialogInterface dialog, int id) {
    +               // User cancelled the dialog
    +           }
    +       });
    +// Set other dialog properties
    +...
    +
    +// Create the AlertDialog
    +AlertDialog dialog = builder.create();
    +
    + +

    Os métodos set...Button() exigem um título para o botão (fornecido +por um recurso de string) e um +{@link android.content.DialogInterface.OnClickListener} que defina a ação a realizar +quando o usuário pressionar o botão.

    + +

    Há três botões de ação diferente que podem ser adicionados:

    +
    +
    Positivo
    +
    É o que se deve usar para aceitar e continuar a ação (a ação "OK").
    +
    Negativo
    +
    É o que se deve usar para cancelar a ação.
    +
    Neutro
    +
    É o que se deve usar quando houver a opção de o usuário não querer continuar a ação, + mas não necessariamente cancelá-la. Ele aparece entre os botões positivo + e negativo. Por exemplo: a ação pode ser "Notifique-me mais tarde".
    +
    + +

    É possível adicionar somente um de cada tipo de botão a uma {@link +android.app.AlertDialog}, ou seja, não é possível ter mais de um botão "positivo".

    + + + +
    + +

    Figura 3. +Caixa de diálogo com um título e uma lista.

    +
    + +

    Adição de listas

    + +

    Há três tipos de listas disponíveis nas APIs {@link android.app.AlertDialog}:

    +
      +
    • Lista de escolha única tradicional
    • +
    • Lista de escolha única persistente (botões de opção)
    • +
    • Lista de escolhas múltiplas persistentes (caixas de seleção)
    • +
    + +

    Para criar uma lista de escolha única como a da figura 3, +use o método {@link android.app.AlertDialog.Builder#setItems setItems()}:

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    builder.setTitle(R.string.pick_color)
    +           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
    +               public void onClick(DialogInterface dialog, int which) {
    +               // The 'which' argument contains the index position
    +               // of the selected item
    +           }
    +    });
    +    return builder.create();
    +}
    +
    + +

    Como a lista aparece na área do conteúdo da caixa de diálogo, +a caixa não pode exibir uma mensagem e uma lista, e será preciso definir um título +para ela com {@link android.app.AlertDialog.Builder#setTitle setTitle()}. +Para especificar os itens da lista, chame {@link +android.app.AlertDialog.Builder#setItems setItems()} passando uma matriz. +Alternativamente, é possível especificar uma lista com {@link +android.app.AlertDialog.Builder#setAdapter setAdapter()}. Isso permite retroceder a lista +com dados dinâmicos (como os de um banco de dados) com um {@link android.widget.ListAdapter}.

    + +

    Se você optar por retroceder a lista com um {@link android.widget.ListAdapter}, +sempre use um {@link android.support.v4.content.Loader} para que o conteúdo carregue +assincronamente. Veja mais detalhes sobre isso nos guias +Criação de layouts +com um adaptador e +Carregadores.

    + +

    Observação: por padrão, o toque em um item de lista dispensa a caixa de diálogo +a menos que você esteja usando uma das listas de escolha persistentes a seguir.

    + +
    + +

    Figura 4. +Lista de itens de múltipla escolha.

    +
    + + +

    Adição de uma lista de escolha única ou de múltipla escolha persistente

    + +

    Para adicionar uma lista de itens de múltipla escolha (caixas de seleção) +ou itens de escolha única (botões de rádio), use os métodos +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} ou +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} respectivamente.

    + +

    Por exemplo, a seguir apresenta-se como criar uma lista de múltipla escolha como +a ilustrada na figura 4, que salva os itens +selecionados em uma {@link java.util.ArrayList}:

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    mSelectedItems = new ArrayList();  // Where we track the selected items
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    // Set the dialog title
    +    builder.setTitle(R.string.pick_toppings)
    +    // Specify the list array, the items to be selected by default (null for none),
    +    // and the listener through which to receive callbacks when items are selected
    +           .setMultiChoiceItems(R.array.toppings, null,
    +                      new DialogInterface.OnMultiChoiceClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int which,
    +                       boolean isChecked) {
    +                   if (isChecked) {
    +                       // If the user checked the item, add it to the selected items
    +                       mSelectedItems.add(which);
    +                   } else if (mSelectedItems.contains(which)) {
    +                       // Else, if the item is already in the array, remove it 
    +                       mSelectedItems.remove(Integer.valueOf(which));
    +                   }
    +               }
    +           })
    +    // Set the action buttons
    +           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   // User clicked OK, so save the mSelectedItems results somewhere
    +                   // or return them to the component that opened the dialog
    +                   ...
    +               }
    +           })
    +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   ...
    +               }
    +           });
    +
    +    return builder.create();
    +}
    +
    + +

    Embora a lista tradicional e uma lista com botões de opção +forneçam uma ação de "escolha única", deve-se usar {@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} se você desejar manter a escolha do usuário. +Ou seja, se a caixa de diálogo abrir novamente mais tarde, deve indicar qual é a escolha atual do usuário, +portanto deve-se criar uma lista com botões de opção.

    + + + + + +

    Criação de layout personalizado

    + +
    + +

    Figura 5. Layout personalizado da caixa de diálogo.

    +
    + +

    Se você deseja um layout personalizado em uma caixa de diálogo, crie um layout e adicione-o a uma +{@link android.app.AlertDialog} chamando {@link +android.app.AlertDialog.Builder#setView setView()} no objeto {@link +android.app.AlertDialog.Builder}.

    + +

    Por padrão, o layout personalizado preenche a janela da caixa de diálogo, mas ainda é possível +usar métodos {@link android.app.AlertDialog.Builder} para adicionar botões e um título.

    + +

    Por exemplo, a seguir há o arquivo de layout para a caixa de diálogo na figura 5:

    + +

    res/layout/dialog_signin.xml

    +
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:orientation="vertical"
    +    android:layout_width="wrap_content"
    +    android:layout_height="wrap_content">
    +    <ImageView
    +        android:src="@drawable/header_logo"
    +        android:layout_width="match_parent"
    +        android:layout_height="64dp"
    +        android:scaleType="center"
    +        android:background="#FFFFBB33"
    +        android:contentDescription="@string/app_name" />
    +    <EditText
    +        android:id="@+id/username"
    +        android:inputType="textEmailAddress"
    +        android:layout_width="match_parent"
    +        android:layout_height="wrap_content"
    +        android:layout_marginTop="16dp"
    +        android:layout_marginLeft="4dp"
    +        android:layout_marginRight="4dp"
    +        android:layout_marginBottom="4dp"
    +        android:hint="@string/username" />
    +    <EditText
    +        android:id="@+id/password"
    +        android:inputType="textPassword"
    +        android:layout_width="match_parent"
    +        android:layout_height="wrap_content"
    +        android:layout_marginTop="4dp"
    +        android:layout_marginLeft="4dp"
    +        android:layout_marginRight="4dp"
    +        android:layout_marginBottom="16dp"
    +        android:fontFamily="sans-serif"
    +        android:hint="@string/password"/>
    +</LinearLayout>
    +
    + +

    Dica: por padrão, ao definir um elemento +{@link android.widget.EditText} para usar o tipo de entrada {@code "textPassword"}, a família da fonte é definida como de espaço único, portanto +deve-se alterar a família da fonte para {@code "sans-serif"} para que os campos de texto usem +um estilo de fonte compatível.

    + +

    Para inflar o layout no {@link android.support.v4.app.DialogFragment}, +obtenha um {@link android.view.LayoutInflater} com +{@link android.app.Activity#getLayoutInflater()} e chame +{@link android.view.LayoutInflater#inflate inflate()}, em que o primeiro parâmetro +é o ID de recurso do layout e o segundo é uma vista pai do layout. +Em seguida, pode-se chamar {@link android.app.AlertDialog#setView setView()} +para posicionar o layout na caixa de diálogo.

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    // Get the layout inflater
    +    LayoutInflater inflater = getActivity().getLayoutInflater();
    +
    +    // Inflate and set the layout for the dialog
    +    // Pass null as the parent view because its going in the dialog layout
    +    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
    +    // Add action buttons
    +           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   // sign in the user ...
    +               }
    +           })
    +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +               public void onClick(DialogInterface dialog, int id) {
    +                   LoginDialogFragment.this.getDialog().cancel();
    +               }
    +           });      
    +    return builder.create();
    +}
    +
    + +
    +

    Dica: se você deseja uma caixa de diálogo personalizada, +pode exibir uma {@link android.app.Activity} como uma caixa de diálogo +em vez de usar as APIs {@link android.app.Dialog}. Basta criar uma atividade e definir seu tema como +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog} +no elemento {@code +<activity>} do manifesto:

    + +
    +<activity android:theme="@android:style/Theme.Holo.Dialog" >
    +
    +

    Pronto. Agora a atividade é exibida em uma janela da caixa de diálogo em vez de tela cheia.

    +
    + + + +

    Direcionamento de eventos de volta ao host da caixa de diálogo

    + +

    Quando o usuário toca em um dos botões de ação da caixa de diálogo ou seleciona um item de sua lista, +o {@link android.support.v4.app.DialogFragment} pode realizar a ação +necessária sozinho, mas normalmente será necessário fornecer o evento à atividade ou ao fragmento +que abriu a caixa de diálogo. Para isso, defina uma interface com um método para cada tipo de evento de clique. +Em seguida, implemente essa interface no componente do host +que receberá os eventos de ação da caixa de diálogo.

    + +

    Por exemplo, a seguir há um {@link android.support.v4.app.DialogFragment} que define +uma interface por meio da qual entrega os eventos de volta à atividade do host:

    + +
    +public class NoticeDialogFragment extends DialogFragment {
    +    
    +    /* The activity that creates an instance of this dialog fragment must
    +     * implement this interface in order to receive event callbacks.
    +     * Each method passes the DialogFragment in case the host needs to query it. */
    +    public interface NoticeDialogListener {
    +        public void onDialogPositiveClick(DialogFragment dialog);
    +        public void onDialogNegativeClick(DialogFragment dialog);
    +    }
    +    
    +    // Use this instance of the interface to deliver action events
    +    NoticeDialogListener mListener;
    +    
    +    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    +    @Override
    +    public void onAttach(Activity activity) {
    +        super.onAttach(activity);
    +        // Verify that the host activity implements the callback interface
    +        try {
    +            // Instantiate the NoticeDialogListener so we can send events to the host
    +            mListener = (NoticeDialogListener) activity;
    +        } catch (ClassCastException e) {
    +            // The activity doesn't implement the interface, throw exception
    +            throw new ClassCastException(activity.toString()
    +                    + " must implement NoticeDialogListener");
    +        }
    +    }
    +    ...
    +}
    +
    + +

    A atividade que hospeda a caixa de diálogo cria uma instância da caixa +com o construtor do fragmento da caixa de diálogo e recebe os eventos +dela por meio de uma implementação da interface {@code NoticeDialogListener}:

    + +
    +public class MainActivity extends FragmentActivity
    +                          implements NoticeDialogFragment.NoticeDialogListener{
    +    ...
    +    
    +    public void showNoticeDialog() {
    +        // Create an instance of the dialog fragment and show it
    +        DialogFragment dialog = new NoticeDialogFragment();
    +        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
    +    }
    +
    +    // The dialog fragment receives a reference to this Activity through the
    +    // Fragment.onAttach() callback, which it uses to call the following methods
    +    // defined by the NoticeDialogFragment.NoticeDialogListener interface
    +    @Override
    +    public void onDialogPositiveClick(DialogFragment dialog) {
    +        // User touched the dialog's positive button
    +        ...
    +    }
    +
    +    @Override
    +    public void onDialogNegativeClick(DialogFragment dialog) {
    +        // User touched the dialog's negative button
    +        ...
    +    }
    +}
    +
    + +

    Como a atividade do host implementa o {@code NoticeDialogListener} — que é +forçado pelo método de retorno de chamada {@link android.support.v4.app.Fragment#onAttach onAttach()} +exibido acima —, o fragmento da caixa de diálogo pode usar +os métodos de retorno de chamada da interface para entregar eventos de clique à atividade:

    + +
    +public class NoticeDialogFragment extends DialogFragment {
    +    ...
    +
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // Build the dialog and set up the button click handlers
    +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +        builder.setMessage(R.string.dialog_fire_missiles)
    +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // Send the positive button event back to the host activity
    +                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
    +                   }
    +               })
    +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // Send the negative button event back to the host activity
    +                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
    +                   }
    +               });
    +        return builder.create();
    +    }
    +}
    +
    + + + +

    Exibição de uma caixa de diálogo

    + +

    Para exibir a caixa de diálogo, crie uma instância do {@link +android.support.v4.app.DialogFragment} e chame {@link android.support.v4.app.DialogFragment#show +show()} passando o {@link android.support.v4.app.FragmentManager} e um nome de tag +para o fragmento da caixa de diálogo.

    + +

    Para obter o {@link android.support.v4.app.FragmentManager}, chama-se +{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} +da {@link android.support.v4.app.FragmentActivity} ou {@link +android.support.v4.app.Fragment#getFragmentManager()} de um {@link +android.support.v4.app.Fragment}. Por exemplo:

    + +
    +public void confirmFireMissiles() {
    +    DialogFragment newFragment = new FireMissilesDialogFragment();
    +    newFragment.show(getSupportFragmentManager(), "missiles");
    +}
    +
    + +

    O segundo argumento, {@code "missiles"}, é um nome de tag exclusivo que o sistema usa para salvar +e restaurar o estado do fragmento quando necessário. Para que a tag obtenha um identificador +do fragmento, chama-se {@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

    + + + + +

    Exibição de uma caixa de diálogo em tela cheia ou como um fragmento incorporado

    + +

    Pode-se ter um projeto de IU em que uma parte da IU apareça como uma caixa de diálogo em determinadas +situações, mas como tela cheia ou fragmento incorporado em outras (dependendo, talvez, +do tamanho da tela do dispositivo). A classe {@link android.support.v4.app.DialogFragment} +oferece esta flexibilidade porque ainda pode comportar-se como um {@link +android.support.v4.app.Fragment} incorporável.

    + +

    Contudo, não é possível usar {@link android.app.AlertDialog.Builder AlertDialog.Builder} +nem outros objetos {@link android.app.Dialog} para criar a caixa de diálogo nesse caso. Se +você deseja que {@link android.support.v4.app.DialogFragment} seja +incorporável, defina a IU da caixa de diálogo em um layout e carregue ou layout no retorno de chamada +{@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()}.

    + +

    A seguir há um exemplo de {@link android.support.v4.app.DialogFragment} que pode aparecer tanto como +caixa de diálogo quanto como fragmento incorporável (usando um layout chamado purchase_items.xml):

    + +
    +public class CustomDialogFragment extends DialogFragment {
    +    /** The system calls this to get the DialogFragment's layout, regardless
    +        of whether it's being displayed as a dialog or an embedded fragment. */
    +    @Override
    +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    +            Bundle savedInstanceState) {
    +        // Inflate the layout to use as dialog or embedded fragment
    +        return inflater.inflate(R.layout.purchase_items, container, false);
    +    }
    +  
    +    /** The system calls this only when creating the layout in a dialog. */
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // The only reason you might override this method when using onCreateView() is
    +        // to modify any dialog characteristics. For example, the dialog includes a
    +        // title by default, but your custom layout might not need it. So here you can
    +        // remove the dialog title, but you must call the superclass to get the Dialog.
    +        Dialog dialog = super.onCreateDialog(savedInstanceState);
    +        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    +        return dialog;
    +    }
    +}
    +
    + +

    A seguir, veja alguns códigos que decidem por exibir o fragmento como uma caixa de diálogo +ou como uma IU de tela cheia com base no tamanho da tela:

    + +
    +public void showDialog() {
    +    FragmentManager fragmentManager = getSupportFragmentManager();
    +    CustomDialogFragment newFragment = new CustomDialogFragment();
    +    
    +    if (mIsLargeLayout) {
    +        // The device is using a large layout, so show the fragment as a dialog
    +        newFragment.show(fragmentManager, "dialog");
    +    } else {
    +        // The device is smaller, so show the fragment fullscreen
    +        FragmentTransaction transaction = fragmentManager.beginTransaction();
    +        // For a little polish, specify a transition animation
    +        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    +        // To make it fullscreen, use the 'content' root view as the container
    +        // for the fragment, which is always the root view for the activity
    +        transaction.add(android.R.id.content, newFragment)
    +                   .addToBackStack(null).commit();
    +    }
    +}
    +
    + +

    Para obter mais informações sobre a realização de operações de fragmentos, consulte o guia +Fragmentos.

    + +

    Nesse exemplo, o booleano mIsLargeLayout especifica se o dispositivo atual +deve usar o projeto de layout grande do aplicativo (e, portanto, exibir esse fragmento como uma caixa de diálogo em +vez de tela cheia). O melhor modo de definir esse tipo de booleano é declarar +um valor de recurso bool +com um valor de recurso alternativo para diferentes tamanhos de tela. Por exemplo, a seguir há duas +versões de recurso bool para diferentes tamanhos de tela:

    + +

    res/values/bools.xml

    +
    +<!-- Default boolean values -->
    +<resources>
    +    <bool name="large_layout">false</bool>
    +</resources>
    +
    + +

    res/values-large/bools.xml

    +
    +<!-- Large screen boolean values -->
    +<resources>
    +    <bool name="large_layout">true</bool>
    +</resources>
    +
    + +

    Assim, é possível inicializar o valor {@code mIsLargeLayout} durante o método +{@link android.app.Activity#onCreate onCreate()} da atividade:

    + +
    +boolean mIsLargeLayout;
    +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.activity_main);
    +
    +    mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
    +}
    +
    + + + +

    Exibição de uma atividade como uma caixa de diálogo em telas grandes

    + +

    Em vez de exibir uma caixa de diálogo como uma IU de tela cheia em telas pequenas, é possível obter +o mesmo resultado exibindo uma {@link android.app.Activity} como uma caixa de diálogo +em telas grandes. A abordagem escolhida depende do projeto do aplicativo, +mas a exibição de uma atividade como caixa de diálogo normalmente é útil quando o aplicativo já está projetado +para telas pequenas e é preciso melhorar a experiência em tablets exibindo uma atividade de vida curta +como uma caixa de diálogo.

    + +

    Para exibir uma atividade como uma caixa de diálogo somente em telas grandes, +aplique o tema {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} +no elemento {@code +<activity>} do manifesto:

    + +
    +<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
    +
    + +

    Para obter mais informações sobre estilizar as atividades com temas, consulte o guia Estilos e temas.

    + + + +

    Dispensa de uma caixa de diálogo

    + +

    Quando o usuário toca em qualquer botão de ação criado +com um {@link android.app.AlertDialog.Builder}, o sistema dispensa a caixa de diálogo.

    + +

    O sistema também dispensa a caixa de diálogo quando o usuário toca em um item em uma lista da caixa de diálogo, exceto +quanto a lista usa botões de opção ou caixas de seleção. Caso contrário, é possível dispensá-la manualmente +chamando {@link android.support.v4.app.DialogFragment#dismiss()} no {@link +android.support.v4.app.DialogFragment}.

    + +

    Se for necessário realizar determinadas +ações quando a caixa de diálogo é dispensada, é possível implementar o método {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} no {@link +android.support.v4.app.DialogFragment}.

    + +

    Também é possível cancelar uma caixa de diálogo. Trata-se de um evento especial que indica que o usuário +se retirou explicitamente da caixa de diálogo sem concluir a tarefa. Isso ocorre se o usuário pressionar o botão +Voltar, tocar na tela fora da área da caixa de diálogo +ou se você chamar {@link android.app.Dialog#cancel()} explicitamente no {@link +android.app.Dialog} (como em resposta a um botão "Cancelar" na caixa de diálogo).

    + +

    Como mostrado no exemplo acima, é possível responder ao evento de cancelamento implementando +{@link android.support.v4.app.DialogFragment#onCancel onCancel()} na classe {@link +android.support.v4.app.DialogFragment}.

    + +

    Observação: o sistema chama +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} para cada evento que +chama o retorno de chamada {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Entretanto, +se você chamar {@link android.app.Dialog#dismiss Dialog.dismiss()} ou {@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()}, + o sistema chamará {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}, mas +não {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Portanto, geralmente, deve-se +chamar {@link android.support.v4.app.DialogFragment#dismiss dismiss()} quando o usuário pressiona +o botão positivo na caixa de diálogo para removê-la da vista.

    + + diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/menus.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/menus.jd new file mode 100644 index 0000000000000000000000000000000000000000..833f8966b4391bfe7a3105d0e2acdc80dd1593e5 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=Menus +parent.title=Interface do usuário +parent.link=index.html +@jd:body + + + +

    Menus são componentes comuns da interface do usuário em diversos tipos de aplicativos. Para fornecer uma experiência +familiar e consistente ao usuário, você deve usar APIs de {@link android.view.Menu} para apresentar +ações de usuário e outras opções em suas atividades.

    + +

    Começando com Android 3.0 (API de nível 11), dispositivos Android não são mais necessários +para fornecer um botão de Menu dedicado. Com esta alteração, os aplicativos do Android devem migrar de uma dependência +no painel de menu 6 itens tradicional para fornecer uma barra de ação para apresentar as ações comuns +de usuário.

    + +

    Apesar de o design e a experiência do usuário para alguns dos itens do menu terem passado por mudanças, a semântica para definir +um conjunto de ações e opções ainda baseia-se em APIs de {@link android.view.Menu} . Este guia +mostra como criar os três tipos fundamentais de menus ou apresentações de ação +em todas as versões do Android:

    + +
    +
    Menu de opções e barra de ação
    +
    O menu de opções é a coleção principal de itens de menu +para uma atividade. É onde deve-se colocar as ações que têm impacto global no aplicativo, +como "Buscar", "Escrever e-mail" e "Configurações". +

    Se estiver desenvolvendo para Android 2.3 ou anterior, os usuários +podem revelar o painel do menu de opções pressionando o botão Menu.

    +

    No Android 3.0 ou em posteriores, os itens do menu de opções são apresentados pela barra de ação como uma combinação de itens +de ação na tela e opções de estouro. A partir do Android 3.0, o botão Menu é censurado (alguns +dispositivos +não têm), então você deve migrar usando a barra de ação para fornecer acesso a ações +e outras opções.

    +

    Consulte a seção Criação de um menu de opções.

    +
    + +
    Modo de ação contextual e menu de contexto
    + +
    Um menu de contexto é um menu flutuante que aparece quando +o usuário realiza um clique longo em um elemento. Ele fornece ações que afetam o conteúdo selecionado +ou a estrutura do contexto. +

    Ao desenvolver para Android 3.0 ou posterior, você deve usar o modo de ação contextual para ativar as ações no conteúdo selecionado. Este modo exibe os itens de ação +que afetam o conteúdo selecionado em uma barra no topo da tela e permite que o usuário +selecione vários itens.

    +

    Consulte a seção Criação de menus contextuais.

    +
    + +
    Menu pop-up
    +
    Um menu pop-up exibe itens em uma lista vertical ancorada à vista +que apresentou o menu. É bom para fornecer um estouro de ações relacionado a conteúdo específico +ou opções de fornecimento de uma segunda parte de um comando. As ações em um menu pop-up +não devem afetar diretamente o conteúdo correspondente — é para isso que servem +as ações contextuais. Preferivelmente, o menu pop-up serve para ações estendidas que relacionam as regiões de conteúdo +na atividade. +

    Consulte a seção criar um menu pop-up.

    +
    +
    + + + +

    Definição de um menu em XML

    + +

    Para todos os tipos de menu, o Android fornece um formato XML padrão para definir os itens de menu. +Em vez de criar um menu no código da atividade, você deve definir um menu e todos os seus itens +em um recurso de menu XML. É possível, assim, +inflar o recurso do menu (carregá-lo como um objeto {@link android.view.Menu}) na atividade, ou +no fragmento.

    + +

    Usar um recurso de menu é uma boa prática por alguns motivos:

    +
      +
    • É mais fácil para visualizar a estrutura do menu em XML.
    • +
    • Ele separa o conteúdo do menu do código comportamental do aplicativo.
    • +
    • Ele permite criar configurações alternativas de menu para versões diferentes de plataforma, +de tamanhos de tela e de outras configurações aproveitando a estrutura dos recursos do aplicativo.
    • +
    + +

    Para definir o menu, crie um arquivo XML dentro do diretório res/menu/ +do projeto e crie o menu com os seguintes elementos:

    +
    +
    <menu>
    +
    Define um {@link android.view.Menu}, que é um recipiente para os itens de menu. Um elemento +<menu> deve ser o nódulo raiz para o arquivo e pode reter um ou mais elementos +<item> e <group>.
    + +
    <item>
    +
    Cria um {@link android.view.MenuItem}, que representa um único item em um menu. Este +elemento pode conter um elemento <menu> aninhado para criar um submenu.
    + +
    <group>
    +
    Um recipiente invisível e opcional para os elementos {@code <item>}. Ele permite que você categorize +itens de menu para que eles compartilhem propriedades como estado ativo e visibilidade. Para obter mais informações, +consulte a seção Criação de grupos de menu.
    +
    + + +

    A seguir há um exemplo de menu chamado game_menu.xml:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/new_game"
    +          android:icon="@drawable/ic_new_game"
    +          android:title="@string/new_game"
    +          android:showAsAction="ifRoom"/>
    +    <item android:id="@+id/help"
    +          android:icon="@drawable/ic_help"
    +          android:title="@string/help" />
    +</menu>
    +
    + +

    O elemento <item> é compatível com vários atributos que você pode usar para definir a aparência ou o comportamento +de um item. Os itens no menu acima incluem os seguintes atributos:

    + +
    +
    {@code android:id}
    +
    Um ID de recurso que é único para o item. Ele permite que o aplicativo reconheça o item +quando o usuário o seleciona.
    +
    {@code android:icon}
    +
    Uma referência a um desenhável para usar como o ícone do item.
    +
    {@code android:title}
    +
    Uma referência a uma string para usar como o título do item.
    +
    {@code android:showAsAction}
    +
    Especifica quando e como este item deve aparecer como um item de ação na barra de ação.
    +
    + +

    Esses são os atributos mais importantes que devem ser usados, mas há vários outros disponíveis. +Para obter informações sobre todos os atributos compatíveis, consulte o documento Recurso de menu.

    + +

    É possível adicionar um submenu a um item em qualquer menu (exceto a um submenu) adicionando um elemento {@code <menu>} +como filho de um {@code <item>}. Os submenus são úteis quando o aplicativo +tem várias funções que podem ser organizadas em tópicos, como itens em uma barra de menu do aplicativo do PC (arquivo, +editar, visualizar etc.). Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/file"
    +          android:title="@string/file" >
    +        <!-- "file" submenu -->
    +        <menu>
    +            <item android:id="@+id/create_new"
    +                  android:title="@string/create_new" />
    +            <item android:id="@+id/open"
    +                  android:title="@string/open" />
    +        </menu>
    +    </item>
    +</menu>
    +
    + +

    Para usar o menu em sua atividade, você precisa inflar o recurso do menu (converter o recurso +XML em um objeto programável) usando {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()}. Nas seções a seguir, você verá como inflar um menu para cada +tipo de menu.

    + + + +

    Criação de um menu de opções

    + +
    + +

    Figura 1. Menu de opções +no navegador, no Android 2.3.

    +
    + +

    O menu de opções é onde você deve incluir ações e outras opções que são relevantes +para o contexto de atividade atual, como "Buscar", "Escrever e-mail" e "Configurações".

    + +

    O local onde os itens no menu de opções aparecem na tela depende da versão em que o aplicativo +foi desenvolvido:

    + +
      +
    • Caso tenha desenvolvido o aplicativo para Android 2.3.x (API de nível 10) ou +inferior, os conteúdos do menu de opções aparecerão na parte inferior da tela, quando o usuário +pressionar o botão Menu, como exibido na figura 1. Quando aberto, a primeira parte visível é +o menu +de ícones, que possui até seis itens de menu. Se o menu incluir mais de seis itens, o Android +colocará o sexto item e o resto em um menu flutuante, que o usuário poderá abrir selecionando +Mais.
    • + +
    • Se você desenvolveu o aplicativo para Android 3.0 (API de nível 11) ou +superior, os itens do menu de opções estão disponíveis na barra de ação. Por padrão, o sistema +posiciona todos os itens na ação de estouro, que o usuário pode revelar com o ícone de estouro de ação +no lado direito da barra de ação (ou pressionando o botão Menu, se disponível). Para +ativar +o acesso rápido a ações importantes, é possível promover alguns itens para aparecerem na barra de ação adicionando +{@code android:showAsAction="ifRoom"} aos elementos {@code <item>} correspondentes (veja a figura +2).

      Para obter mais informações sobre os itens de ação e outros comportamentos da barra de ação, consulte o guia Barra de ação.

      +

      Observação: Mesmo se não estiver desenvolvendo para Android 3.0 ou +posteriores, é possível compilar seu próprio layout de barra de ação para obter um efeito semelhante. Para obter um exemplo de como é possível +suportar versões mais antigas do Android com uma barra de ação, consulte o exemplo Compatibilidade da barra de ação +.

      +
    • +
    + + +

    Figura 2. Barra de ação do aplicativo Honeycomb Gallery, exibindo +guias de navegação e um item de ação de câmera (além do botão de estouro de ação).

    + +

    É possível declarar itens para o menu de opções da subclasse {@link android.app.Activity} +ou de uma subclasse {@link android.app.Fragment}. Se a atividade e os fragmentos +declararem itens para o menu de opções, eles estarão combinados na IU. O item da atividade aparece primeiro, +seguido de cada um desses fragmentos na ordem em que são adicionados +à atividade. Se necessário, é possível reorganizar os itens do menu com o atributo {@code android:orderInCategory} +em cada {@code <item>} que precisar mover.

    + +

    Para especificar o menu de opções para uma atividade, substitua {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} (os fragmentos fornecem +o próprio retorno de chamada de {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}). Neste método +, é possível inflar o recurso de menu (definido no XML) em um {@link +android.view.Menu} fornecido no retorno de chamada. Por exemplo:

    + +
    +@Override
    +public boolean onCreateOptionsMenu(Menu menu) {
    +    MenuInflater inflater = {@link android.app.Activity#getMenuInflater()};
    +    inflater.inflate(R.menu.game_menu, menu);
    +    return true;
    +}
    +
    + +

    Também é possível adicionar itens de menu usando {@link android.view.Menu#add(int,int,int,int) +add()} e recuperar os itens com {@link android.view.Menu#findItem findItem()} para revisar +as propriedades com APIs de {@link android.view.MenuItem}.

    + +

    Caso tenha desenvolvido o aplicativo para Android 2.3.x e anteriores, o sistema chamará {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} para criar o menu de opções +quando o usuário abrir o menu pela primeira vez. Caso tenha desenvolvido para Android 3.0 e posteriores, +o sistema chama {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} +ao iniciar a atividade para mostrar os itens para a barra de ação.

    + + + +

    Tratamento de eventos de clique

    + +

    Quando o usuário seleciona um item para o menu de opções (incluindo os itens de ação na barra de ação), +o sistema chama o método {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} da atividade. Este método passa o {@link android.view.MenuItem} selecionado. É possível +identificar o item chamando {@link android.view.MenuItem#getItemId()}, que retorna o ID único +para o item de menu (definido pelo atributo {@code android:id} no recurso de menu ou em um número inteiro +dado ao método {@link android.view.Menu#add(int,int,int,int) add()}). É possível combinar este ID +com itens de menu conhecidos para realizar a ação adequada. Por exemplo:

    + +
    +@Override
    +public boolean onOptionsItemSelected(MenuItem item) {
    +    // Handle item selection
    +    switch (item.getItemId()) {
    +        case R.id.new_game:
    +            newGame();
    +            return true;
    +        case R.id.help:
    +            showHelp();
    +            return true;
    +        default:
    +            return super.onOptionsItemSelected(item);
    +    }
    +}
    +
    + +

    Ao lidar com um item de menu, retorne {@code true}. Se não lidar com o item de menu, + você deverá chamar a implementação de superclasse de {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} (a implementação +padrão retornará como falsa).

    + +

    Se a atividade incluir fragmentos, o sistema chamará primeiro {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} para a atividade e, em seguida, +para cada fragmento (na ordem em que cada fragmento foi adicionado) até um retornar como +{@code true} ou até todos os fragmentos serem chamados.

    + +

    Dica: o Android 3.0 adiciona a possibilidade de definir o comportamento do clique +para um item de menu em XML, usando o atributo {@code android:onClick}. O valor do atributo +deve ser o nome de um método definido pela atividade usando o menu. O método +deve ser público e aceitar um único parâmetro {@link android.view.MenuItem} — quando o sistema chamar este método, + ele passará o item de menu selecionado. Para obter mais informações e um exemplo, consulte o documento Recurso de menu.

    + +

    Dica: se o aplicativo contiver várias atividades +e algumas delas fornecerem o mesmo menu de opções, +considere criar uma atividade que não implemente nada exceto os métodos {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} e {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()}. Em seguida, estenda esta classe para cada atividade que deve compartilhar +o mesmo menu de opções. Desta maneira, é possível gerenciar um conjunto de códigos para lidar com ações de menu +e cada classe descendente herda os comportamentos do menu. +Se quiser adicionar itens de menu a uma das atividades descendentes, +substitua {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} nesta atividade. Chame {@code super.onCreateOptionsMenu(menu)} para que os itens de menu originais +sejam criados e, em seguida, adicione os novos itens de menu com {@link +android.view.Menu#add(int,int,int,int) menu.add()}. Você também pode substituir o comportamento +da superclasse para itens de menu individuais.

    + + +

    Alteração dos itens de menu em tempo de execução

    + +

    Depois que o sistema chamar {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}, ele reterá uma instância do {@link android.view.Menu} que você populará +e não chamará {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} +novamente, a não ser que o menu seja invalidado por algum motivo. No entanto, você deve usar {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} somente para criar o estado inicial do menu +e não para realizar alterações durante o ciclo de vida da atividade.

    + +

    Caso queira modificar o menu de opções com base +em eventos que ocorrem durante o ciclo de vida da atividade, é possível fazê-lo +no método {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}. Este método +passa a você o objeto {@link android.view.Menu}, já que ele existe para que seja possível modificá-lo, +como com adição, remoção ou desativação de itens. (Os fragmentos também fornecem um retorno de chamada {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()}.)

    + +

    No Android 2.3.x e em anteriores, o sistema chamará {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()} sempre que o usuário abrir o menu de opções (pressionar o botão Menu +).

    + +

    No Android 3.0 e posteriores, avalia-se o menu de opções quanto a sempre estar aberto quando os itens de menu +são apresentados na barra de ação. Quando um evento ocorre e você quer realizar uma atualização de menu, +você deve chamar {@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()} para pedir +que o sistema chame {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}.

    + +

    Observação: +você nunca deve alterar os itens no menu de opções com base no {@link android.view.View} atualmente +em foco. Quando estiver no modo de toque (quando o usuário não está usando cursor de bola ou um teclado), as vistas +não podem ter foco, então você nunca deve usar o foco como base para modificar +os itens no menu de opções. Se quiser fornecer itens de menu que sejam sensíveis a contexto para um {@link +android.view.View}, use um menu de contexto.

    + + + + +

    Criação de menus contextuais

    + +
    + +

    Figura 3. Capturas de tela de um menu de contexto flutuante (esquerda) +e a barra de ação contextual (direita).

    +
    + +

    Um menu contextual oferece ações que afetam um item ou estrutura de contexto específica na IU. +É possível fornecer um menu de contexto para qualquer vista, mas ele é geralmente usado para itens em um {@link +android.widget.ListView}, {@link android.widget.GridView}, ou em outras coleções de vistas +em que o usuário pode realizar ações diretas em cada item.

    + +

    Há duas formas de fornecer ações contextuais:

    +
      +
    • Em um menu de contexto flutuante. Um menu aparece como uma lista flutuante +de itens de menu (semelhante a uma caixa de diálogo) quando o usuário realiza um clique longo (pressiona e segura) +em uma vista que declara suporte para um menu de contexto. Os usuários podem realizar uma ação contextual +em um item por vez.
    • + +
    • No modo de ação contextual. Este modo é uma implementação de sistema de +{@link android.view.ActionMode} que exibe uma barra de ação contextual no topo da tela +com itens de ação que afetam os itens selecionados. Quando este modo está ativo, +os usuários podem realizar uma ação em vários itens por vez (se o aplicativo permitir).
    • +
    + +

    Observação: o modo de ação contextual está disponível no Android 3.0 (API +de nível 11) e em posteriores e é a técnica preferencial para exibir ações contextuais +quando disponível. Se o aplicativo for compatível com versões mais antigas que a 3.0, então você deve retornar a um menu +de contexto flutuante nestes dispositivos.

    + + +

    Criação de um menu de contexto flutuante

    + +

    Para fornecer um menu de contexto flutuante:

    +
      +
    1. Registre o {@link android.view.View} ao qual o menu de contexto deve estar associado +chamando {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} e passe-o +para {@link android.view.View}. +

      Se a atividade usar {@link android.widget.ListView} ou {@link android.widget.GridView} +e você quiser que cada item forneça o mesmo menu de contexto, registre todos os itens para um menu de contexto +passando {@link android.widget.ListView} ou {@link android.widget.GridView} para {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()}.

      +
    2. + +
    3. Implemente o método {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +em {@link android.app.Activity} ou {@link android.app.Fragment}. +

      Quando a vista registrada receber um evento de clique longo, o sistema chamará o método {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +. É aqui que você define os itens de menu, geralmente inflando um recurso de menu. Por +exemplo:

      +
      +@Override
      +public void onCreateContextMenu(ContextMenu menu, View v,
      +                                ContextMenuInfo menuInfo) {
      +    super.onCreateContextMenu(menu, v, menuInfo);
      +    MenuInflater inflater = getMenuInflater();
      +    inflater.inflate(R.menu.context_menu, menu);
      +}
      +
      + +

      {@link android.view.MenuInflater} permite que você infle o menu de contexto de um recurso de menu. Os parâmetros do método +de retorno de chamada incluem o {@link android.view.View} +que o usuário selecionou e um objeto {@link android.view.ContextMenu.ContextMenuInfo} que fornece +informações adicionais sobre o item selecionado. Se sua atividade tiver várias vistas, em que cada uma forneça +um menu de contexto diferente, você deve usar esses parâmetros para determinar qual menu de contexto +deve ser inflado.

      +
    4. + +
    5. Implemente {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}. +

      Quando o usuário selecionar um item de menu, o sistema chamará este método para que você possa realizar +a ação adequada. Por exemplo:

      + +
      +@Override
      +public boolean onContextItemSelected(MenuItem item) {
      +    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
      +    switch (item.getItemId()) {
      +        case R.id.edit:
      +            editNote(info.id);
      +            return true;
      +        case R.id.delete:
      +            deleteNote(info.id);
      +            return true;
      +        default:
      +            return super.onContextItemSelected(item);
      +    }
      +}
      +
      + +

      O método {@link android.view.MenuItem#getItemId()} consulta o ID +para o item de menu selecionado, o qual pode ser atribuído a cada item de menu no XML usando o atributo {@code +android:id}, como exibido na seção Definição de um menu em +XML.

      + +

      Ao lidar com um item de menu, retorne {@code true}. Se não lidar com o item de menu, +você deverá passar o item de menu para a implementação de superclasse. Se a atividade incluir fragmentos, +ela receberá este retorno de chamada primeiro. Ao chamar a superclasse ao não lidar, o sistema +passará o evento para o respectivo método de retorno de chamada em cada fragmento, um por vez (na ordem +em que cada fragmento foi adicionado) até que {@code true} ou {@code false} seja retornado. (A implementação +padrão para {@link android.app.Activity} e {@code android.app.Fragment} retorna {@code +false}, então você deve sempre chamar a superclasse ao não tratar de um item de menu.)

      +
    6. +
    + + +

    Uso do modo de ação contextual

    + +

    O modo de ação contextual é uma implementação de sistema de {@link android.view.ActionMode} +que direciona a interação do usuário a efetuar ações contextuais. Quando um usuário +ativa este modo selecionando um item, uma barra de ação contextual aparece na parte superior da tela +para apresentar as ações que o usuário pode realizar nos itens selecionados. Enquanto este modo estiver ativo, +o usuário pode selecionar vários itens (se você permitir), desmarcar itens e continuar +a navegar dentro da atividade (o tanto que você permitir). O modo de ação +é desativado e a barra de ação contextual desaparece quando o usuário desmarca todos os itens, pressiona o botão VOLTAR, +ou seleciona a ação Pronto na lateral esquerda da barra.

    + +

    Observação: a barra de ação contextual não é necessariamente +associada à barra de ação. Elas operam de forma independente, +apesar de a barra de ação contextual ocupar visualmente a posição +da barra de ação.

    + +

    Caso esteja desenvolvendo para Android 3.0 (API de nível 11) e posteriores, +você deve usar o modo de ação contextual para apresentar ações contextuais, em vez de usar o menu de contexto flutuante.

    + +

    Para oferecer vistas que fornecem ações contextuais, você deve invocar o modo de ação contextual +sobre um dos eventos (ou ambos):

    +
      +
    • O usuário realiza um clique longo na vista.
    • +
    • O usuário seleciona uma caixa de seleção ou um componente de IU semelhante dentro da vista.
    • +
    + +

    A maneira do aplicativo de invocar o modo de ação contextual e definir o comportamento +para cada ação depende do seu projeto. Há basicamente dois projetos:

    +
      +
    • Para ações contextuais em vistas arbitrárias individuais.
    • +
    • Para ações contextuais de agrupadas em itens em um {@link +android.widget.ListView} ou {@link android.widget.GridView} (permitindo que o usuário selecione vários itens +e realize uma ação em todos eles).
    • +
    + +

    As seguintes seções descrevem a configuração necessária para cada cenário.

    + + +

    Ativação do modo de ação contextual para vistas individuais

    + +

    Caso queira invocar o modo de ação contextual somente quando o usuário selecionar +vistas específicas, você deve:

    +
      +
    1. Implementar a interface {@link android.view.ActionMode.Callback}. Em seus métodos de retorno de chamada, +é possível especificar as ações da barra de ação contextual, responder aos eventos de clique em itens de ação, +e tratar de outros eventos de ciclo de vida do modo de ação.
    2. +
    3. Chame {@link android.app.Activity#startActionMode startActionMode()} quando quiser exibir +a barra (como quando o usuário realiza cliques longos na visualização).
    4. +
    + +

    Por exemplo:

    + +
      +
    1. Implementar a interface {@link android.view.ActionMode.Callback ActionMode.Callback}: +
      +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
      +
      +    // Called when the action mode is created; startActionMode() was called
      +    @Override
      +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
      +        // Inflate a menu resource providing context menu items
      +        MenuInflater inflater = mode.getMenuInflater();
      +        inflater.inflate(R.menu.context_menu, menu);
      +        return true;
      +    }
      +
      +    // Called each time the action mode is shown. Always called after onCreateActionMode, but
      +    // may be called multiple times if the mode is invalidated.
      +    @Override
      +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      +        return false; // Return false if nothing is done
      +    }
      +
      +    // Called when the user selects a contextual menu item
      +    @Override
      +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
      +        switch (item.getItemId()) {
      +            case R.id.menu_share:
      +                shareCurrentItem();
      +                mode.finish(); // Action picked, so close the CAB
      +                return true;
      +            default:
      +                return false;
      +        }
      +    }
      +
      +    // Called when the user exits the action mode
      +    @Override
      +    public void onDestroyActionMode(ActionMode mode) {
      +        mActionMode = null;
      +    }
      +};
      +
      + +

      Observe que esses retornos de chamada de eventos são quase exatamente iguais aos retornos de chamada do menu de opções, exceto que cada um deles também passa o objeto {@link +android.view.ActionMode} associado ao evento. É possível usar APIs de {@link +android.view.ActionMode} para realizar várias alterações ao CAB, como revisar um título e um subtítulo +com {@link android.view.ActionMode#setTitle setTitle()} e {@link +android.view.ActionMode#setSubtitle setSubtitle()} (útil para indicar quantos itens +são selecionados).

      + +

      Observe também que os exemplos acima definem a variável {@code mActionMode} como nula quando +o modo de ação é destruído. Na etapa a seguir, você verá como ela é inicializada +e quão útil salvar a variável do membro na atividade ou no fragmento pode ser.

      +
    2. + +
    3. Chame {@link android.app.Activity#startActionMode startActionMode()} para ativar o modo de ação contextual +quando apropriado, como em resposta a um clique longo em um {@link +android.view.View}:

      + +
      +someView.setOnLongClickListener(new View.OnLongClickListener() {
      +    // Called when the user long-clicks on someView
      +    public boolean onLongClick(View view) {
      +        if (mActionMode != null) {
      +            return false;
      +        }
      +
      +        // Start the CAB using the ActionMode.Callback defined above
      +        mActionMode = getActivity().startActionMode(mActionModeCallback);
      +        view.setSelected(true);
      +        return true;
      +    }
      +});
      +
      + +

      Ao chamar {@link android.app.Activity#startActionMode startActionMode()}, o sistema +retorna o {@link android.view.ActionMode} criado. Ao salvar isto em uma variável do membro, +é possível realizar alterações na barra de ação contextual em resposta a outros eventos. No exemplo acima, +{@link android.view.ActionMode} é usado para garantir que a instância {@link android.view.ActionMode} +não seja recriada se já estiver ativa, verificando se o membro é nulo antes de iniciar +o modo de ação.

      +
    4. +
    + + + +

    Ativação de ações contextuais agrupadas em ListView ou GridView

    + +

    Se tiver uma coleção de itens em um {@link android.widget.ListView} ou {@link +android.widget.GridView} (ou outra extensão de {@link android.widget.AbsListView}) e quiser +permitir que os usuários realizem ações de agrupamento, você deve:

    + +
      +
    • Implementar a interface {@link android.widget.AbsListView.MultiChoiceModeListener} e defini-la +para o grupo de visualização com {@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()}. Nos métodos de retorno de chamada da escuta, é possível especificar as ações +para a barra de ação contextual, responder a eventos de clique em itens de ação e lidar com outros retornos de chamada +herdados da interface {@link android.view.ActionMode.Callback}.
    • + +
    • Chame {@link android.widget.AbsListView#setChoiceMode setChoiceMode()} com o argumento {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
    • +
    + +

    Por exemplo:

    + +
    +ListView listView = getListView();
    +listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    +listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
    +
    +    @Override
    +    public void onItemCheckedStateChanged(ActionMode mode, int position,
    +                                          long id, boolean checked) {
    +        // Here you can do something when items are selected/de-selected,
    +        // such as update the title in the CAB
    +    }
    +
    +    @Override
    +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    +        // Respond to clicks on the actions in the CAB
    +        switch (item.getItemId()) {
    +            case R.id.menu_delete:
    +                deleteSelectedItems();
    +                mode.finish(); // Action picked, so close the CAB
    +                return true;
    +            default:
    +                return false;
    +        }
    +    }
    +
    +    @Override
    +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    +        // Inflate the menu for the CAB
    +        MenuInflater inflater = mode.getMenuInflater();
    +        inflater.inflate(R.menu.context, menu);
    +        return true;
    +    }
    +
    +    @Override
    +    public void onDestroyActionMode(ActionMode mode) {
    +        // Here you can make any necessary updates to the activity when
    +        // the CAB is removed. By default, selected items are deselected/unchecked.
    +    }
    +
    +    @Override
    +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    +        // Here you can perform updates to the CAB due to
    +        // an {@link android.view.ActionMode#invalidate} request
    +        return false;
    +    }
    +});
    +
    + +

    É isso. Agora, quando o usuário selecionar um item com um clique longo, o sistema chamará o método {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +e exibirá a barra de ação contextual com as ações especificadas. Enquanto a barra de ação contextual +estiver visível, os usuários poderão selecionar itens adicionais.

    + +

    Em alguns casos em que as ações contextuais fornecem itens de ação comuns, você pode +querer adicionar uma caixa de seleção ou um elemento de IU semelhante que permite que os usuários selecionem itens, +pois eles podem não descobrir o comportamento do clique longo. Quando um usuário seleciona a caixa de seleção, +é possível invocar o modo de ação contextual definindo o respectivo item de lista +para o estado marcado com {@link android.widget.AbsListView#setItemChecked setItemChecked()}.

    + + + + +

    Criação de um menu pop-up

    + +
    + +

    Figura 4. Um menu pop-up no aplicativo do Gmail, ancorado ao botão +de estouro no cando direito superior.

    +
    + +

    Um {@link android.widget.PopupMenu} é um menu modal ancorado a uma {@link android.view.View}. +Ele aparece sob a vista de âncora se tiver espaço, ou sobre a vista. Ele é útil para:

    +
      +
    • Fornecer um menu de estilo de estouro para ações que se relacionam com o conteúdo específico (como +cabeçalhos de e-mail do Gmail, exibidos na figura 4). +

      Observação: Isto não é igual ao menu de contexto, +que geralmente é usado para ações que afetam o conteúdo selecionado. Para ações que afetam o conteúdo +selecionado, use o modo de ação contextual ou o menu de contexto flutuante.

    • +
    • Fornecer uma segunda parte de uma sentença de comando (como um botão marcado como "Adicionar" +que produz um menu pop-up com opções diferentes de "Adicionar").
    • +
    • Fornecer um menu suspenso semelhante a {@link android.widget.Spinner} que não retenha +uma seleção persistente.
    • +
    + + +

    Observação: {@link android.widget.PopupMenu} está disponível com a API +de nível 11 ou posteriores.

    + +

    Se definir o menu em XML, abaixo é exposto o modo de exibir o menu pop-up:

    +
      +
    1. Represente um {@link android.widget.PopupMenu} com seu construtor, +que usa o aplicativo {@link android.content.Context} e {@link android.view.View} atual ao qual o menu +deve ser ancorado.
    2. +
    3. Use {@link android.view.MenuInflater} para inflar o recurso de menu no objeto {@link +android.view.Menu} retornado por {@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()}. Em APIs de nível 14 ou posteriores, é possível usar +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()}.
    4. +
    5. Chame {@link android.widget.PopupMenu#show() PopupMenu.show()}.
    6. +
    + +

    Por exemplo, a seguir há um botão com o atributo {@link android.R.attr#onClick android:onClick} +que exibe um menu pop-up:

    + +
    +<ImageButton
    +    android:layout_width="wrap_content" 
    +    android:layout_height="wrap_content" 
    +    android:src="@drawable/ic_overflow_holo_dark"
    +    android:contentDescription="@string/descr_overflow_button"
    +    android:onClick="showPopup" />
    +
    + +

    A atividade pode então exibir o menu pop-up desta forma:

    + +
    +public void showPopup(View v) {
    +    PopupMenu popup = new PopupMenu(this, v);
    +    MenuInflater inflater = popup.getMenuInflater();
    +    inflater.inflate(R.menu.actions, popup.getMenu());
    +    popup.show();
    +}
    +
    + +

    Em APIs de nível 14 ou posteriores, é possível combinar as duas linhas que inflam o menu com {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()}.

    + +

    O menu é dispensado quando o usuário seleciona um item ou toca fora +da área do menu. É possível ouvir o evento de dispensa usando {@link +android.widget.PopupMenu.OnDismissListener}.

    + +

    Tratamento de eventos de clique

    + +

    Para realizar uma ação +quando o usuário seleciona um item de menu, você deve implementar a interface {@link +android.widget.PopupMenu.OnMenuItemClickListener} e registrá-la com {@link +android.widget.PopupMenu} chamando {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()}. Quando o usuário seleciona um item, o sistema chama o retorno de chamada {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} +na interface.

    + +

    Por exemplo:

    + +
    +public void showMenu(View v) {
    +    PopupMenu popup = new PopupMenu(this, v);
    +
    +    // This activity implements OnMenuItemClickListener
    +    popup.setOnMenuItemClickListener(this);
    +    popup.inflate(R.menu.actions);
    +    popup.show();
    +}
    +
    +@Override
    +public boolean onMenuItemClick(MenuItem item) {
    +    switch (item.getItemId()) {
    +        case R.id.archive:
    +            archive(item);
    +            return true;
    +        case R.id.delete:
    +            delete(item);
    +            return true;
    +        default:
    +            return false;
    +    }
    +}
    +
    + + +

    Criação de grupos de menu

    + +

    Um grupo de menu é uma coleção de itens de menu que compartilham certas peculiaridades. Com um grupo, +é possível:

    +
      +
    • Exibir ou ocultar todos os itens com {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()}
    • +
    • Ativar ou desativar todos os itens com {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()}
    • +
    • Especificar se todos os itens são marcáveis com {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}
    • +
    + +

    É possível criar um grupo aninhando elementos {@code <item>} dentro de um elemento {@code <group>} +no recurso de menu ou especificando um ID de grupo com o método {@link +android.view.Menu#add(int,int,int,int) add()}.

    + +

    Abaixo há um exemplo de recurso de menu que inclui um grupo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/menu_save"
    +          android:icon="@drawable/menu_save"
    +          android:title="@string/menu_save" />
    +    <!-- menu group -->
    +    <group android:id="@+id/group_delete">
    +        <item android:id="@+id/menu_archive"
    +              android:title="@string/menu_archive" />
    +        <item android:id="@+id/menu_delete"
    +              android:title="@string/menu_delete" />
    +    </group>
    +</menu>
    +
    + +

    Os itens que estão no grupo aparecem no mesmo nível que o primeiro item — todos os três itens +no menu são irmãos. No entanto, é possível modificar as peculiaridades dos dois itens +no grupo mencionando o ID do grupo e usando os métodos listados acima. O sistema +também nunca separará os itens agrupados. Por exemplo, se você declarar {@code +android:showAsAction="ifRoom"} para cada item, eles aparecerão na barra de ação +ou no estouro de ação.

    + + +

    Uso de itens de menu marcáveis

    + +
    + +

    Figura 5. Captura de tela de um submenu +com itens marcáveis.

    +
    + +

    Um menu como uma interface pode ser útil para ativar e desativar as opções, usar uma caixa de seleção +para opções independentes ou botões de rádio para grupos +de opções mutuamente exclusivas. A figura 5 mostra um submenu com itens marcáveis +com botões de rádio.

    + +

    Observação: os itens de menu no menu de ícones (do menu de opções) +não podem exibir uma caixa de seleção ou um botão de rádio. Caso escolha tornar marcáveis os itens no menu de ícones, +você deverá indicar manualmente o estado marcado arrastando o ícone e/ou digitando +sempre que o estado for alterado.

    + +

    É possível definir o comportamento marcável para itens individuais de menu usando o atributo {@code +android:checkable} no elemento {@code <item>}, ou para um grupo inteiro +com o atributo {@code android:checkableBehavior} no elemento {@code <group>}. Por exemplo, +todos os itens neste grupo de menu são marcáveis com um botão de rádio:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <group android:checkableBehavior="single">
    +        <item android:id="@+id/red"
    +              android:title="@string/red" />
    +        <item android:id="@+id/blue"
    +              android:title="@string/blue" />
    +    </group>
    +</menu>
    +
    + +

    O atributo {@code android:checkableBehavior} aceita: +

    +
    {@code single}
    +
    Somente um item do grupo pode ser marcado (botões de rádio)
    +
    {@code all}
    +
    Todos os itens podem ser marcados (caixas de seleção)
    +
    {@code none}
    +
    Nenhum item é marcável
    +
    + +

    É possível aplicar um estado marcado padrão a um item usando o atributo {@code android:checked} +no elemento {@code <item>} e alterar o seu código com o método {@link +android.view.MenuItem#setChecked(boolean) setChecked()}.

    + +

    Quando um item marcável é selecionado, o sistema chama o respectivo método retorno de chamada do item selecionado +(como {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). É aqui +que você deve definir o estado da caixa de seleção, pois a caixa de seleção ou o botão de rádio +não altera o seu estado automaticamente. É possível consultar o estado do item (como ele era antes +do usuário selecioná-lo) com {@link android.view.MenuItem#isChecked()} e, em seguida, definir o estado marcado com +{@link android.view.MenuItem#setChecked(boolean) setChecked()}. Por exemplo:

    + +
    +@Override
    +public boolean onOptionsItemSelected(MenuItem item) {
    +    switch (item.getItemId()) {
    +        case R.id.vibrate:
    +        case R.id.dont_vibrate:
    +            if (item.isChecked()) item.setChecked(false);
    +            else item.setChecked(true);
    +            return true;
    +        default:
    +            return super.onOptionsItemSelected(item);
    +    }
    +}
    +
    + +

    Caso você não defina o estado marcado desta maneira, o estado visível do item (a caixa de seleção +ou o botão de rádio) +não se alterará quando o usuário selecioná-lo. Quando o estado é definido, a atividade preserva o estado marcado +do item para que, quando o usuário abrir o menu posteriormente, o estado marcado +definido esteja visível.

    + +

    Observação: +os itens de menu marcáveis servem para serem usados somente em uma base por sessão e não são salvos quando +o aplicativo é destruído. Caso tenha configurações de aplicativo que gostaria de salvar para o usuário, +você deve armazenar os dados usando as preferências compartilhadas.

    + + + +

    Adição de itens de menu com base em uma intenção

    + +

    Às vezes, você desejará que um item de menu inicie uma atividade usando uma {@link android.content.Intent} +(se é uma atividade no seu ou em outro aplicativo). Quando você sabe qual intenção +quer usar e tem um item de menu específico que deve iniciar a intenção, é possível executá-la +com {@link android.app.Activity#startActivity(Intent) startActivity()} durante +o método de retorno de chamada selecionado no item (como o retorno de chamada {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}).

    + +

    No entanto, caso não tenha certeza de que o dispositivo +do usuário contém um aplicativo que lida com a intenção, adicionar um item que o invoca +resulta em um item de menu que não funciona, pois a intenção pode não se resolver +em uma atividade. Para resolver isto, o Android permite que você adicione itens de menu dinamicamente ao seu menu +quando encontra atividades no dispositivo que lidam com a intenção.

    + +

    Para adicionar itens de menu com base nas atividades disponíveis que aceitam uma intenção:

    +
      +
    1. Defina a intenção +com a categoria {@link android.content.Intent#CATEGORY_ALTERNATIVE} +e/ou {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, além de quaisquer outros requisitos.
    2. +
    3. Chame {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}. O Android procura um aplicativo que possa realizar a intenção +e adiciona-o ao seu menu.
    4. +
    + +

    Se não houver nenhum aplicativo instalado +que satisfaça a intenção, nenhum item de menu será adicionado.

    + +

    Observação: +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} é usado para lidar com o elemento atualmente selecionado +na tela. Portanto, ele deve ser usado apenas ao criar um menu em {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()}.

    + +

    Por exemplo:

    + +
    +@Override
    +public boolean onCreateOptionsMenu(Menu menu){
    +    super.onCreateOptionsMenu(menu);
    +
    +    // Create an Intent that describes the requirements to fulfill, to be included
    +    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    +    Intent intent = new Intent(null, dataUri);
    +    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
    +
    +    // Search and populate the menu with acceptable offering applications.
    +    menu.addIntentOptions(
    +         R.id.intent_group,  // Menu group to which new items will be added
    +         0,      // Unique item ID (none)
    +         0,      // Order for the items (none)
    +         this.getComponentName(),   // The current activity name
    +         null,   // Specific items to place first (none)
    +         intent, // Intent created above that describes our requirements
    +         0,      // Additional flags to control items (none)
    +         null);  // Array of MenuItems that correlate to specific items (none)
    +
    +    return true;
    +}
    + +

    Para cada atividade encontrada que fornece um filtro de intenção correspondente à intenção definida, +um item de menu é adicionado, usando o valor no android:label do filtro de intenção +como o título do item e o ícone do aplicativo como o ícone do item de menu. O método +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} retorna o número de itens de menu adicionados.

    + +

    Observação: Ao chamar {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()}, ele substitui todos os itens de menu no grupo do menu especificado +no primeiro argumento.

    + + +

    Permissão para a atividade ser adicionada a outros menus

    + +

    Você pode também oferecer os serviços da sua atividade para outros aplicativos, +para que o aplicativo possa ser incluído no menu de outros (revertendo as funções descritas acima).

    + +

    Para ser incluído nos menus de outros aplicativos, você precisa definir +um filtro de intenção como normalmente faz, mas certificando-se de incluir os valores {@link android.content.Intent#CATEGORY_ALTERNATIVE} +e/ou {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} para a categoria +do filtro de intenção. Por exemplo:

    +
    +<intent-filter label="@string/resize_image">
    +    ...
    +    <category android:name="android.intent.category.ALTERNATIVE" />
    +    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    +    ...
    +</intent-filter>
    +
    + +

    Leia mais sobre a criação de filtros de intenção no documento +Intenções e filtros de intenções.

    + +

    Para obter um exemplo de aplicativo que usa esta técnica, consulte o código de exemplo do +Bloco +de notas.

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 0000000000000000000000000000000000000000..42563ace224828b749aaf52f649ca7121708925a --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=Notificações +@jd:body + + +

    + Uma notificação é uma mensagem que pode ser exibida ao usuário fora da IU normal do aplicativo. +Quando você diz ao sistema para emitir uma notificação, ela primeiro aparece como um ícone +na área de notificação. Para ver os detalhes da notificação, o usuário abre +a gaveta de notificação. A área de notificação e a gaveta de notificação +são áreas controladas pelo sistema que o usuário pode visualizar a qualquer momento. +

    + +

    + Figura 1. Notificações na área de notificação. +

    + +

    + Figura 2. Notificações na gaveta de notificação. +

    + +

    Observação: exceto quando notado, este guia menciona a classe +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +na versão 4 da Biblioteca de suporte. +A classe {@link android.app.Notification.Builder Notification.Builder} foi adicionada no Android +3.0 (API de nível 11).

    + +

    Considerações de projeto

    + +

    As notificações, como parte importante da interface do usuário do Android, possuem as próprias diretrizes de projeto. +As alterações do Material Design introduzidas no Android 5.0 (API de nível 21) são de importância +específica e, por isso, recomenda-se revisar o treinamento do Material Design + para obter mais informações. Para saber como projetar notificações e suas interações, leia o guia de projeto +Notificações.

    + +

    Criação de uma notificação

    + +

    Você especifica as ações e informações da IU para uma notificação +no objeto {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. +Para criar a própria notificação, chama-se +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()}, +que retorna um objeto {@link android.app.Notification} contendo suas especificações. Para emitir a notificação, +passa-se o objeto {@link android.app.Notification} ao sistema +chamando {@link android.app.NotificationManager#notify NotificationManager.notify()}.

    + +

    Conteúdo necessário da notificação

    +

    + Um objeto {@link android.app.Notification} deve conter o seguinte: +

    +
      +
    • + Um ícone pequeno, definido por + {@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} +
    • +
    • + Um título, definido por + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} +
    • +
    • + Texto de detalhes, definido por + {@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} +
    • +
    +

    Configurações e conteúdo opcionais da notificação

    +

    + Todas as outras configurações e o conteúdo da notificação são opcionais. Para saber mais sobre isso, + consulte as documentações de referência de {@link android.support.v4.app.NotificationCompat.Builder}. +

    + +

    Ações da notificação

    +

    + Apesar de serem opcionais, deve-se adicionar pelo menos uma ação à notificação. + Uma ação permite que os usuários direcionem-se diretamente da notificação + para uma {@link android.app.Activity} no aplicativo, onde podem visualizar um ou mais eventos + ou realizar outros trabalhos. +

    +

    + Uma notificação pode fornecer várias ações. Deve-se sempre definir a ação que será ativada + quando o usuário clicar na notificação. Geralmente, esta ação abre uma + {@link android.app.Activity} no aplicativo. É possível também adicionar botões à notificação + que realizem ações adicionais, como ativar a soneca de um alarme imediatamente + para uma mensagem de texto. Este recurso está disponível a partir do Android 4.1. Se você usar botões de ação adicionais, + também deverá disponibilizar a funcionalidade em uma {@link android.app.Activity} no aplicativo; consulte + a seção Tratamento da compatibilidade para obter mais informações. +

    +

    + Dentro de uma {@link android.app.Notification}, a própria ação é definida + por uma {@link android.app.PendingIntent} contendo uma + {@link android.content.Intent} que inicia + uma {@link android.app.Activity} no aplicativo. Para associar + a {@link android.app.PendingIntent} a um gesto, chame o método adequado + de {@link android.support.v4.app.NotificationCompat.Builder}. Por exemplo, se quiser iniciar + {@link android.app.Activity} quando o usuário clicar no texto da notificação + na gaveta de notificação, deve-se adicionar {@link android.app.PendingIntent} chamando + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}. +

    +

    + Iniciar uma {@link android.app.Activity} quando o usuário clica na notificação + é o cenário de ação mais comum. É possível também iniciar uma {@link android.app.Activity} quando o usuário + dispensa uma notificação. A partir do Android 4.1, é possível iniciar + uma {@link android.app.Activity} a partir de um botão de ação. Para obter mais informações, leia o guia de referência + de {@link android.support.v4.app.NotificationCompat.Builder}. +

    + +

    Prioridade da notificação

    +

    + Se quiser, é possível definir a prioridade de uma notificação. A prioridade + age como uma sugestão à IU do dispositivo sobre como a notificação deve ser exibida. + Para definir a prioridade de uma notificação, chame {@link + android.support.v4.app.NotificationCompat.Builder#setPriority(int) + NotificationCompat.Builder.setPriority()} e passe em uma das constantes de prioridade {@link + android.support.v4.app.NotificationCompat}. Há cinco níveis de prioridade, + de {@link + android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) a {@link + android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); se não for definida, a prioridade + segue o padrão de {@link + android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0). +

    +

    Para obter mais informações sobre como definir um nível de prioridade adequado, Consulte "Definição e gerenciamento corretos da prioridade das notificações" + no guia de projeto + Notificações. +

    + +

    Criação de uma notificação simples

    +

    + O seguinte fragmento ilustra uma notificação simples que especifica uma atividade para abrir quando +o usuário clica na notificação. Observe que o código cria um objeto + {@link android.support.v4.app.TaskStackBuilder} e usa-o para criar + a {@link android.app.PendingIntent} para a ação. Este padrão é explicado com mais detalhes + na seção + Preservação da navegação ao iniciar uma atividade: +

    +
    +NotificationCompat.Builder mBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.notification_icon)
    +        .setContentTitle("My notification")
    +        .setContentText("Hello World!");
    +// Creates an explicit intent for an Activity in your app
    +Intent resultIntent = new Intent(this, ResultActivity.class);
    +
    +// The stack builder object will contain an artificial back stack for the
    +// started Activity.
    +// This ensures that navigating backward from the Activity leads out of
    +// your application to the Home screen.
    +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    +// Adds the back stack for the Intent (but not the Intent itself)
    +stackBuilder.addParentStack(ResultActivity.class);
    +// Adds the Intent that starts the Activity to the top of the stack
    +stackBuilder.addNextIntent(resultIntent);
    +PendingIntent resultPendingIntent =
    +        stackBuilder.getPendingIntent(
    +            0,
    +            PendingIntent.FLAG_UPDATE_CURRENT
    +        );
    +mBuilder.setContentIntent(resultPendingIntent);
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// mId allows you to update the notification later on.
    +mNotificationManager.notify(mId, mBuilder.build());
    +
    +

    É isso. O usuário foi notificado.

    + +

    Aplicação de um layout expandido a uma notificação

    +

    + Para que uma notificação apareça em uma vista expandida, cria-se primeiro + um objeto {@link android.support.v4.app.NotificationCompat.Builder} com as opções + de visualização normal desejadas. Em seguida, chama-se {@link android.support.v4.app.NotificationCompat.Builder#setStyle + Builder.setStyle()} com um objeto de layout expandido como o argumento. +

    +

    + Lembre-se de que as notificações expandidas não estão disponíveis em plataformas anteriores ao Android 4.1. Para saber mais + sobre como tratar de notificações para Android 4.1 e plataformas anteriores, leia + a seção Tratamento da compatibilidade. +

    +

    + Por exemplo, o seguinte fragmento de código demonstra como alterar a notificação criada + no fragmento anterior para usar o layout expandido: +

    +
    +NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
    +    .setSmallIcon(R.drawable.notification_icon)
    +    .setContentTitle("Event tracker")
    +    .setContentText("Events received")
    +NotificationCompat.InboxStyle inboxStyle =
    +        new NotificationCompat.InboxStyle();
    +String[] events = new String[6];
    +// Sets a title for the Inbox in expanded layout
    +inboxStyle.setBigContentTitle("Event tracker details:");
    +...
    +// Moves events into the expanded layout
    +for (int i=0; i < events.length; i++) {
    +
    +    inboxStyle.addLine(events[i]);
    +}
    +// Moves the expanded layout object into the notification object.
    +mBuilder.setStyle(inBoxStyle);
    +...
    +// Issue the notification here.
    +
    + +

    Tratamento da compatibilidade

    + +

    + Nem todos os recursos de notificação estão disponíveis para uma versão específica, + mesmo se os métodos que os definem estiverem na classe + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} da biblioteca de suporte. + Por exemplo, botões de ação, que dependem das notificações expandidas, aparecem somente a partir do Android + 4.1 ou de posteriores porque as próprias notificações expandidas estão disponíveis somente a partir + destas versões. +

    +

    + Para garantir a melhor compatibilidade, crie notificações + com {@link android.support.v4.app.NotificationCompat NotificationCompat} e suas subclasses, + particularmente {@link android.support.v4.app.NotificationCompat.Builder + NotificationCompat.Builder}. Além disso, siga o processo a seguir ao implementar uma notificação: +

    +
      +
    1. + Forneça toda a funcionalidade da notificação aos usuários, independentemente + da versão que estejam usando. Para fazer isto, verifique se toda a disponibilidade está disponível a partir de uma + {@link android.app.Activity} no aplicativo. Pode-se adicionar uma nova + {@link android.app.Activity} para fazer isto. +

      + Por exemplo, caso queira usar + {@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} + para fornecer um controle que interrompa e inicie a reprodução de mídia, primeiro implemente + este controle em uma {@link android.app.Activity} no aplicativo. +

      +
    2. +
    3. + Certifique-se de que todos os usuários possam acessar a funcionalidade na {@link android.app.Activity}, + iniciando-a quando o usuário clicar na notificação. Para fazer isto, + crie uma {@link android.app.PendingIntent} + para a{@link android.app.Activity}. Chame + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} para adicionar a {@link android.app.PendingIntent} à notificação. +
    4. +
    5. + Agora, adicione os recursos de notificação expandidos que deseja usar à notificação. Lembre-se + de que qualquer funcionalidade adicionada também deve estar disponível na {@link android.app.Activity} + que é iniciada quando os usuários clicam na notificação. +
    6. +
    + + + + +

    Gerenciamento de notificações

    +

    + Quando for necessário emitir uma notificação várias vezes para o mesmo tipo de evento, + deve-se evitar criar uma notificação completamente nova. Em vez disso, deve-se considerar atualizar + uma notificação anterior, alterando e/ou adicionado alguns valores. +

    +

    + Por exemplo, o Gmail notifica o usuário de que novos e-mails foram recebidos aumentando a contagem +de mensagens não lidas e adicionando um resumo de cada e-mail à notificação. Isto é chamado + de "acumular" a notificação. Isto é descrito com mais detalhes no guia de projeto + Notificações. +

    +

    + Observação: este recurso do Gmail requer o layout expandido da "caixa de entrada", + que faz parte do recurso de notificação expandida disponível a partir do Android 4.1. +

    +

    + A seguinte seção descreve como atualizar as notificações e como removê-las. +

    +

    Atualização de notificações

    +

    + Para definir uma notificação para que possa ser atualizada, deve-se emiti-la com um ID de notificação + chamando {@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()}. + Para atualizar esta notificação após emiti-la, + atualize ou crie um objeto {@link android.support.v4.app.NotificationCompat.Builder}, + compile um objeto {@link android.app.Notification} a partir dele e emita + a {@link android.app.Notification} com o mesmo ID usado anteriormente. Se a notificação anterior + ainda estiver visível, o sistema a atualizará com o conteúdo + do objeto {@link android.app.Notification}. Se a notificação anterior for dispensada, + uma nova notificação será criada. +

    +

    + O seguinte fragmento demonstra uma notificação que é atualizada para refletir + o número de eventos que ocorreram. Ele acumula a notificação, exibindo um resumo: +

    +
    +mNotificationManager =
    +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// Sets an ID for the notification, so it can be updated
    +int notifyID = 1;
    +mNotifyBuilder = new NotificationCompat.Builder(this)
    +    .setContentTitle("New Message")
    +    .setContentText("You've received new messages.")
    +    .setSmallIcon(R.drawable.ic_notify_status)
    +numMessages = 0;
    +// Start of a loop that processes data and then notifies the user
    +...
    +    mNotifyBuilder.setContentText(currentText)
    +        .setNumber(++numMessages);
    +    // Because the ID remains unchanged, the existing notification is
    +    // updated.
    +    mNotificationManager.notify(
    +            notifyID,
    +            mNotifyBuilder.build());
    +...
    +
    + + +

    Remoção de notificações

    +

    + As notificações permanecem visíveis até que um dos seguintes casos aconteça: +

    +
      +
    • + O usuário dispense a notificação individualmente usando "Apagar tudo" + (se a notificação puder ser apagada). +
    • +
    • + O usuário clique na notificação + e chame-se {@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()} + quando a notificação é criada. +
    • +
    • + Chame-se {@link android.app.NotificationManager#cancel(int) cancel()} + para um ID de notificação específico. Este método também exclui notificações contínuas. +
    • +
    • + Chame-se {@link android.app.NotificationManager#cancelAll() cancelAll()}, que remove +todas as notificações emitidas anteriormente. +
    • +
    + + +

    Preservação da navegação ao iniciar uma atividade

    +

    + Ao iniciar uma {@link android.app.Activity} a partir de uma notificação, deve-se preservar + a experiência de navegação esperada pelo usuário. Clicar em Voltar deve levar o usuário + de volta pelo fluxo de trabalho normal do aplicativo à tela Inicial, e clicar em Recentes deve exibir + a {@link android.app.Activity} como uma tarefa separada. Para preservar a experiência de navegação, + deve-se iniciar a {@link android.app.Activity} em uma tarefa nova. O modo de definição + da {@link android.app.PendingIntent} para fornecer uma tarefa nova depende da natureza + da {@link android.app.Activity} que está sendo iniciado. Há duas situações gerais: +

    +
    +
    + Atividade comum +
    +
    + Inicia-se uma {@link android.app.Activity} que faz parte do fluxo de trabalho normal + do aplicativo. Nesta situação, defina {@link android.app.PendingIntent} + para iniciar uma tarefa nova e forneça a {@link android.app.PendingIntent} com uma pilha de retorno + que reproduza o comportamento Voltar normal do aplicativo. +

    + As notificações do aplicativo do Gmail demonstram isso. Ao clicar em uma notificação + para uma única mensagem de e-mail, a própria mensagem será exibida. Tocar em Voltar + faz com que o usuário volte ao Gmail até a tela inicial, como se ele tivesse acessado o Gmail + a partir da tela inicial em vez de a partir da notificação. +

    +

    + Isto acontece independentemente do aplicativo que estava em primeiro plano + quando a notificação foi tocada. Por exemplo, se você estiver no Gmail escrevendo uma mensagem e clicar + em uma notificação de um e-mail, você acessará este e-mail imediatamente. Tocar em Voltar + leva você de volta à caixa de entrada e, em seguida, à tela inicial, em vez de levar + à mensagem que estava escrevendo. +

    +
    +
    + Atividade especial +
    +
    + O usuário vê apenas esta {@link android.app.Activity} se for iniciada a partir de uma notificação. + De certo modo, a {@link android.app.Activity} estende a notificação fornecendo + informações que seriam difíceis de exibir na própria notificação. Para estas situações, + defina a {@link android.app.PendingIntent} para iniciar em uma tarefa nova. Não há necessidade + de criar uma pilha de retorno, pois a {@link android.app.Activity} iniciada + não faz parte do fluxo de atividades do aplicativo. Clicar em Voltar ainda levará o usuário + à tela inicial. +
    +
    + +

    Definição de uma atividade PendingIntent comum

    +

    + Para definir uma {@link android.app.PendingIntent} que inicia uma {@link android.app.Activity} de entrada + direta, siga estas etapas: +

    +
      +
    1. + Defina a hierarquia de {@link android.app.Activity} do aplicativo no manifesto. +
        +
      1. + Adicione compatibilidade com Android 4.0.3 e mais antigos. Para fazer isto, especifique o pai + da {@link android.app.Activity} que está iniciando adicionando um elemento +<meta-data> + como o filho de +<activity>. +

        + Para este elemento, defina +android:name="android.support.PARENT_ACTIVITY". + Defina +android:value="<parent_activity_name>", + onde <parent_activity_name> é o valor de +android:name + para o elemento +<activity> + pai. Veja o XML a seguir para ver um exemplo. +

        +
      2. +
      3. + Adicione também compatibilidade com Android 4.1 e mais recentes. Para fazer isto, adicione o atributo +android:parentActivityName + ao elemento +<activity> + da {@link android.app.Activity} que estiver iniciando. +
      4. +
      +

      + O XML final deve parecer-se com isto: +

      +
      +<activity
      +    android:name=".MainActivity"
      +    android:label="@string/app_name" >
      +    <intent-filter>
      +        <action android:name="android.intent.action.MAIN" />
      +        <category android:name="android.intent.category.LAUNCHER" />
      +    </intent-filter>
      +</activity>
      +<activity
      +    android:name=".ResultActivity"
      +    android:parentActivityName=".MainActivity">
      +    <meta-data
      +        android:name="android.support.PARENT_ACTIVITY"
      +        android:value=".MainActivity"/>
      +</activity>
      +
      +
    2. +
    3. + Crie uma pilha de retorno com base na {@link android.content.Intent} que inicia + a {@link android.app.Activity}: +
        +
      1. + Crie a {@link android.content.Intent} para iniciar a{@link android.app.Activity}. +
      2. +
      3. + Crie um compilador de pilhas chamando {@link android.app.TaskStackBuilder#create + TaskStackBuilder.create()}. +
      4. +
      5. + Adicione a pilha de retorno ao compilador de pilha chamando + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}. + Para cada {@link android.app.Activity} na hierarquia definida no manifesto, + a pilha de retorno conterá um objeto {@link android.content.Intent} + que inicia a {@link android.app.Activity}. Este método adiciona sinalizadores + que iniciam a pilha em uma tarefa nova. +

        + Observação: Apesar de o argumento + de {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} + ser uma referência para a {@link android.app.Activity} iniciada, a chamada do método + não adiciona a {@link android.content.Intent} que inicia + a {@link android.app.Activity}. Em vez disso, lidamos com isto na próxima etapa. +

        +
      6. +
      7. + Adicione a {@link android.content.Intent} que inicia a {@link android.app.Activity} + a partir da notificação chamando + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. + Passe a {@link android.content.Intent} criada na primeira etapa + como o argumento + para {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. +
      8. +
      9. + Se for necessário, adicione argumentos para os objetos {@link android.content.Intent} + na pilha chamando {@link android.support.v4.app.TaskStackBuilder#editIntentAt + TaskStackBuilder.editIntentAt()}. Às vezes, isto é necessário para garantir + que a {@link android.app.Activity} alvo exiba dados significantes quando o usuário + navegar a ela usando Voltar. +
      10. +
      11. + Adquira uma {@link android.app.PendingIntent} para esta pilha de retorno chamando + {@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}. + É possível usar esta {@link android.app.PendingIntent} como o argumento + para {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
      12. +
      +
    4. +
    +

    + O seguinte fragmento de código demonstra o processo: +

    +
    +...
    +Intent resultIntent = new Intent(this, ResultActivity.class);
    +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    +// Adds the back stack
    +stackBuilder.addParentStack(ResultActivity.class);
    +// Adds the Intent to the top of the stack
    +stackBuilder.addNextIntent(resultIntent);
    +// Gets a PendingIntent containing the entire back stack
    +PendingIntent resultPendingIntent =
    +        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    +...
    +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    +builder.setContentIntent(resultPendingIntent);
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +mNotificationManager.notify(id, builder.build());
    +
    + +

    Definição de uma atividade PendingIntent especial

    +

    + A seção a seguir descreve como definir uma atividade + {@link android.app.PendingIntent} especial. +

    +

    + Uma {@link android.app.Activity} especial não precisa de uma pilha de retorno, então não é necessário + definir sua hierarquia de {@link android.app.Activity} no manifesto + e chamar + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} para compilar + uma pilha de retorno. Em vez disso, use o manifesto para definir as opções de tarefa da {@link android.app.Activity} + e crie a {@link android.app.PendingIntent} + chamando {@link android.app.PendingIntent#getActivity getActivity()}: +

    +
      +
    1. + No manifesto, adicione os seguintes atributos ao elemento +<activity> + para a {@link android.app.Activity} +
      +
      +android:name="activityclass" +
      +
      + O nome da classe completamente qualificado da atividade. +
      +
      +android:taskAffinity="" +
      +
      + Combinado com o sinalizador + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + que foi definido no código, isto garante que esta {@link android.app.Activity} + não acesse a tarefa padrão do aplicativo. Tarefas existentes + que tiverem a afinidade padrão do aplicativo não serão afetadas. +
      +
      +android:excludeFromRecents="true" +
      +
      + Exclui a nova tarefa de Recentespara que o usuário não + navegue acidentalmente de volta. +
      +
      +

      + Este fragmento mostra o elemento: +

      +
      +<activity
      +    android:name=".ResultActivity"
      +...
      +    android:launchMode="singleTask"
      +    android:taskAffinity=""
      +    android:excludeFromRecents="true">
      +</activity>
      +...
      +
      +
    2. +
    3. + Compilar e emitir a notificação: +
        +
      1. + Crie uma {@link android.content.Intent} que inicie + a {@link android.app.Activity}. +
      2. +
      3. + Defina a {@link android.app.Activity} para iniciar em uma tarefa nova e vazia + chamando {@link android.content.Intent#setFlags setFlags()} com os sinalizadores + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + e + {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK}. +
      4. +
      5. + Defina quaisquer outras opções necessárias para a {@link android.content.Intent}. +
      6. +
      7. + Crie uma {@link android.app.PendingIntent} a partir da {@link android.content.Intent} + chamando {@link android.app.PendingIntent#getActivity getActivity()}. + É possível usar esta {@link android.app.PendingIntent} como o argumento + para {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
      8. +
      +

      + O seguinte fragmento de código demonstra o processo: +

      +
      +// Instantiate a Builder object.
      +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
      +// Creates an Intent for the Activity
      +Intent notifyIntent =
      +        new Intent(this, ResultActivity.class);
      +// Sets the Activity to start in a new, empty task
      +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
      +                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
      +// Creates the PendingIntent
      +PendingIntent notifyPendingIntent =
      +        PendingIntent.getActivity(
      +        this,
      +        0,
      +        notifyIntent,
      +        PendingIntent.FLAG_UPDATE_CURRENT
      +);
      +
      +// Puts the PendingIntent into the notification builder
      +builder.setContentIntent(notifyPendingIntent);
      +// Notifications are issued by sending them to the
      +// NotificationManager system service.
      +NotificationManager mNotificationManager =
      +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +// Builds an anonymous Notification object from the builder, and
      +// passes it to the NotificationManager
      +mNotificationManager.notify(id, builder.build());
      +
      +
    4. +
    + + +

    Exibição do progresso em uma notificação

    +

    + As notificações podem incluir um indicador de progresso animado que exibe aos usuários + o status de uma operação em andamento. Se for possível estimar a duração da operação + e o quanto dela já foi concluído em um determinado momento, use a forma "determinada" do indicador + (uma barra de progresso). Se não for possível estimar a duração da operação, + use a forma "indeterminada" do indicador (um indicador de atividade). +

    +

    + Os indicadores de progresso são exibidos com a implementação da plataforma + da classe {@link android.widget.ProgressBar}. +

    +

    + Para usar o indicador de progresso em plataformas a partir do Android 4.0, + chame {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. Para versões mais antigas, + deve-se criar o próprio layout personalizado de notificação + que inclua uma vista de {@link android.widget.ProgressBar}. +

    +

    + As seguintes seções descrevem como exibir o progresso em uma notificação + usando {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. +

    + +

    Exibição de um indicador de progresso de duração fixa

    +

    + Para exibir uma determinada barra de progresso, adicione a barra à notificação + chamando {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(max, progress, false)} e, em seguida, emitindo a notificação. À medida que a operação prosseguir, + incremente progress e atualize a notificação. No término da operação, + progress deve ser igual a max. Uma maneira comum de chamar + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} + é definir max como 100 e, em seguida, incrementar progress + como um valor "percentual completo" para a operação. +

    +

    + É possível deixar a barra de progresso em exibição ou removê-la quando a operação for concluída. Em ambos os casos, + lembre-se de atualizar o texto da notificação para exibir que a operação foi concluída. + Para remover a barra de progresso, + chame {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(0, 0, false)}. Por exemplo: +

    +
    +...
    +mNotifyManager =
    +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +mBuilder = new NotificationCompat.Builder(this);
    +mBuilder.setContentTitle("Picture Download")
    +    .setContentText("Download in progress")
    +    .setSmallIcon(R.drawable.ic_notification);
    +// Start a lengthy operation in a background thread
    +new Thread(
    +    new Runnable() {
    +        @Override
    +        public void run() {
    +            int incr;
    +            // Do the "lengthy" operation 20 times
    +            for (incr = 0; incr <= 100; incr+=5) {
    +                    // Sets the progress indicator to a max value, the
    +                    // current completion percentage, and "determinate"
    +                    // state
    +                    mBuilder.setProgress(100, incr, false);
    +                    // Displays the progress bar for the first time.
    +                    mNotifyManager.notify(0, mBuilder.build());
    +                        // Sleeps the thread, simulating an operation
    +                        // that takes time
    +                        try {
    +                            // Sleep for 5 seconds
    +                            Thread.sleep(5*1000);
    +                        } catch (InterruptedException e) {
    +                            Log.d(TAG, "sleep failure");
    +                        }
    +            }
    +            // When the loop is finished, updates the notification
    +            mBuilder.setContentText("Download complete")
    +            // Removes the progress bar
    +                    .setProgress(0,0,false);
    +            mNotifyManager.notify(ID, mBuilder.build());
    +        }
    +    }
    +// Starts the thread by calling the run() method in its Runnable
    +).start();
    +
    + + +

    Exibição de um indicador de atividade contínua

    +

    + Para exibir um indicador de atividade indeterminado, adicione-o à notificação + com {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)} + (os dois primeiros argumentos são ignorados) e emita a notificação. O resultado é um indicador + que tem o mesmo estilo que uma barra de progresso, exceto que sua animação é contínua. +

    +

    + Emita a notificação no início da operação. A animação será executada + até que a notificação seja modificada. Quando a operação for concluída, + chame {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)} + e, em seguida, atualize a notificação para remover o indicador de atividade. + Sempre faça isso. Caso contrário, a animação será executada mesmo quando a operação for concluída. Lembre-se também de alterar + o texto da notificação para indicar que a operação foi concluída. +

    +

    + Para ver como os indicadores de atividade funcionam, consulte o fragmento anterior. Localize as seguintes linhas: +

    +
    +// Sets the progress indicator to a max value, the current completion
    +// percentage, and "determinate" state
    +mBuilder.setProgress(100, incr, false);
    +// Issues the notification
    +mNotifyManager.notify(0, mBuilder.build());
    +
    +

    + Substitua as linhas encontradas pelas seguintes linhas: +

    +
    + // Sets an activity indicator for an operation of indeterminate length
    +mBuilder.setProgress(0, 0, true);
    +// Issues the notification
    +mNotifyManager.notify(0, mBuilder.build());
    +
    + +

    Metadados de notificação

    + +

    As notificações podem ser classificadas de acordo com os metadados atribuídos +com os seguintes métodos {@link android.support.v4.app.NotificationCompat.Builder}:

    + +
      +
    • {@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} + diz ao sistema como lidar com as notificações do aplicativo quando o dispositivo estiver no modo de Prioridade + (por exemplo: se a notificação representar uma chamada recebida, mensagem instantânea ou alarme).
    • +
    • {@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} faz com que as notificações + com o campo de prioridade definido para {@code PRIORITY_MAX} ou {@code PRIORITY_HIGH} + apareçam em uma pequena janela flutuante se a notificação também tiver som ou vibração.
    • +
    • {@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} + permite a adição de uma lista de pessoas a uma notificação. O aplicativo pode usar isto para sinalizar + ao sistema que ele deve agrupar as notificações de pessoas específicas, ou classificar notificações + destas pessoas como sendo mais importantes.
    • +
    + +
    + +

    + Figura 3. Atividade de tela cheia exibindo uma notificação de informações prévias +

    +
    + +

    Notificação de informações prévias

    + +

    Com o Android 5.0 (API de nível 21), as notificações podem aparecer em uma pequena janela flutuante +(também chamada de notificação de informações prévias) quando o dispositivo estiver ativo +(ou seja, com o dispositivo desbloqueado e a tela ativada). Essas notificações +aparecem de maneira semelhante à forma compacta da notificação, exceto que a notificação de informações prévias +também exibe botões de ação. Os usuários podem agir +ou dispensar a notificação de informações prévias sem deixar o aplicativo atual.

    + +

    Exemplos de condições que podem ativar uma notificação de informações prévias incluem:

    + +
      +
    • A atividade do usuário estar no modo de tela cheia (o aplicativo usar +{@link android.app.Notification#fullScreenIntent}) ou
    • +
    • A notificação tem alta prioridade e usa toques +ou vibrações
    • +
    + +

    Notificações da tela de bloqueio

    + +

    Com o lançamento do Android 5.0 (API de nível 21), as notificações podem aparecer +na tela de bloqueio. O aplicativo pode usar esta funcionalidade para fornecer controles de reprodução de mídia e outras +ações comuns. Os usuários podem escolher, acessando Configurações, se querem exibir notificações na tela de bloqueio +e é possível designar se uma notificação do aplicativo será visível na tela de bloqueio.

    + +

    Configuração de visibilidade

    + +

    O aplicativo pode controlar o nível de detalhe visível nas notificações exibidas +em uma tela de bloqueio segura. Chama-se {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()} +e especifica-se um dos seguintes valores:

    + +
      +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC} exibe o conteúdo completo +da notificação.
    • +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET} não exibe parte alguma +desta notificação na tela de bloqueio.
    • +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} exibe informações básicas, +como o ícone e o título do conteúdo da notificação, mas oculta o conteúdo completo.
    • +
    + +

    Quando {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} é definido, é possível +fornecer também uma versão alternativa do conteúdo da notificação que oculta determinados detalhes. Por exemplo, +um aplicativo de SMS pode exibir uma notificação que exiba Você tem 3 novas mensagens de texto, mas oculte +o conteúdo e o remetente das mensagens. Para fornecer esta notificação alternativa, cria-se primeiro +uma notificação de substituição usando {@link android.support.v4.app.NotificationCompat.Builder}. Ao criar +o objeto de notificação privada, anexe a notificação de substituição a ele +usando o método {@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} +.

    + +

    Controle de reprodução de mídia na tela de bloqueio

    + +

    No Android 5.0 (API de nível 21), a tela de bloqueio deixa de exibir controles de mídia +com base em {@link android.media.RemoteControlClient}, que foi reprovado. Em vez disso, usa-se o modelo +{@link android.app.Notification.MediaStyle} com o método +{@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()}, +que converte as ações em ícones clicáveis.

    + +

    Observação: o modelo e o método {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +não estão incluídos na biblioteca de suporte. Portanto, esses recursos funcionam somente no Android 5.0 +e em versões mais recentes.

    + +

    Para exibir os controles de reprodução de mídia na tela de bloqueio no Android 5.0, defina a visibilidade +como {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC}, como descrito acima. Em seguida, +adicione as ações e defina o modelo {@link android.app.Notification.MediaStyle}, como descrito +no seguinte exemplo de código:

    + +
    +Notification notification = new Notification.Builder(context)
    +    // Show controls on lock screen even when user hides sensitive content.
    +    .setVisibility(Notification.VISIBILITY_PUBLIC)
    +    .setSmallIcon(R.drawable.ic_stat_player)
    +    // Add media control buttons that invoke intents in your media service
    +    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
    +    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
    +    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
    +    // Apply the media style template
    +    .setStyle(new Notification.MediaStyle()
    +    .setShowActionsInCompactView(1 /* #1: pause button */)
    +    .setMediaSession(mMediaSession.getSessionToken())
    +    .setContentTitle("Wonderful music")
    +    .setContentText("My Awesome Band")
    +    .setLargeIcon(albumArtBitmap)
    +    .build();
    +
    + +

    Observação: a reprovação de {@link android.media.RemoteControlClient} +tem mais implicações para o controle de mídia. Consulte +Controle de reprodução de mídia +para obter mais informações sobre as novas APIs para gerenciar a sessão de mídia e o controle de reprodução.

    + + + +

    Layouts de notificação personalizados

    +

    + A estrutura das notificações permite que um layout de notificação personalizado seja definido, + o que define a aparência da notificação em um objeto {@link android.widget.RemoteViews}. + As notificações de layout personalizado são parecidas com notificações normais, mas baseiam-se + em um {@link android.widget.RemoteViews} definido em um arquivo de layout XML. +

    +

    + A altura disponível para um layout de notificação personalizado depende da vista da notificação. Layouts de vista normal + são limitados a 64 dp, e layouts de vista expandida são limitados a 256 dp. +

    +

    + Para definir um layout de notificação personalizada, comece instanciando + um objeto {@link android.widget.RemoteViews} que infle um arquivo de layout XML. Em seguida, + em vez de chamar métodos como + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()}, + chame {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Para definir + os detalhes do conteúdo na notificação personalizada, use os métodos em + {@link android.widget.RemoteViews} para definir os valores dos filhos da vista: +

    +
      +
    1. + Crie um layout XML para a notificação em um arquivo separado. É possível usar o nome de arquivo que desejar, + mas deve-se usar a extensão .xml +
    2. +
    3. + No aplicativo, use métodos {@link android.widget.RemoteViews} para definir os ícones + e o texto da notificação. Coloque este objeto {@link android.widget.RemoteViews} + em {@link android.support.v4.app.NotificationCompat.Builder} + chamando {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Evite definir + um {@link android.graphics.drawable.Drawable} de segundo plano + no objeto {@link android.widget.RemoteViews}, pois a cor do texto pode torná-lo ilegível. +
    4. +
    +

    + A classe {@link android.widget.RemoteViews} também inclui métodos que podem ser usados + para adicionar facilmente um {@link android.widget.Chronometer} ou uma {@link android.widget.ProgressBar} + ao layout da notificação. Para obter mais informações sobre como criar layouts personalizados + para a notificação, consulte a documentação de referência de {@link android.widget.RemoteViews}. +

    +

    + Atenção: ao usar um layout personalizado de notificação, certifique-se + de garantir que ele funcione com diferentes orientações e resoluções do dispositivo. Enquanto este aviso + aplica-se a todos os layouts de vistas, é muito importante para as notificações, + pois o espaço na gaveta da notificação é muito restrito. Não torne o layout personalizado muito complexo + e certifique-se de testá-lo em várias configurações. +

    + +

    Uso de recursos de estilo para texto de notificação personalizada

    +

    + Sempre use recursos de estilo para o texto de uma notificação personalizada A cor de fundo + da notificação pode variar dentre vários dispositivos e versões e o uso de recursos de estilo + ajuda a lidar com isto. A partir do Android 2.3, o sistema definiu um estilo + para o texto do layout de notificação padrão. Se usar o mesmo estilo nos aplicativos que usam Android + 2.3 ou mais recentes, você garantirá que o texto esteja visível em relação ao fundo da exibição. +

    diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/overview.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..d12bfe5fac8b593e37f764a5cf9cca45457a8a3f --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=Visão geral da IU +@jd:body + + +

    Todos os elementos da interface do usuário em um aplicativo para Android são criados usando objetos {@link android.view.View} e +{@link android.view.ViewGroup}. Uma {@link android.view.View} é um objeto que desenha +algo na tela com o qual o usuário pode interagir. Um {@link android.view.ViewGroup} é um +objeto que contém outros objetos {@link android.view.View} (e {@link android.view.ViewGroup}) para +definir o layout da interface.

    + +

    O Android fornece uma coleção de subclasses {@link android.view.View} e {@link +android.view.ViewGroup} que oferecem controles de entrada comuns (como botões e campos de +texto) e vários modelos de layout (como um layout linear ou relativo).

    + + +

    Layout da interface do usuário

    + +

    A interface do usuário de cada componente do aplicativo é definida usando uma hierarquia de objetos {@link +android.view.View} e {@link android.view.ViewGroup}, como mostra a figura 1. Cada grupo de vistas +é um recipiente invisível que organiza vistas de nível inferior, enquanto que as vistas de nível inferior podem ser +controles de entrada ou outros widgets +que desenham alguma parte da IU. Essa árvore hierárquica pode ser simples ou complexa, conforme necessário +(mas a simplicidade é melhor para desempenho).

    + + +

    Figura 1. Ilustração de uma hierarquia de vistas, que define o layout +de uma IU.

    + +

    Para declarar o layout, é possível instanciar objetos {@link android.view.View} no código e começar a +criar uma árvore. Mas a forma mais fácil e efetiva de definir o layout é com um arquivo XML. +O XML oferece uma estrutura legível por humanos para o layout, similar a HTML.

    + +

    O nome de um elemento XML para uma vista é respectivo à classe do Android que ele representa. Portanto, um elemento +<TextView> cria um widget {@link android.widget.TextView} na IU +e um elemento <LinearLayout> cria um grupo de vistas de {@link android.widget.LinearLayout} +.

    + +

    Por exemplo, um layout vertical simples com uma vista de texto e um botão se parece com:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +              android:layout_width="fill_parent" 
    +              android:layout_height="fill_parent"
    +              android:orientation="vertical" >
    +    <TextView android:id="@+id/text"
    +              android:layout_width="wrap_content"
    +              android:layout_height="wrap_content"
    +              android:text="I am a TextView" />
    +    <Button android:id="@+id/button"
    +            android:layout_width="wrap_content"
    +            android:layout_height="wrap_content"
    +            android:text="I am a Button" />
    +</LinearLayout>
    +
    + +

    Ao carregar um recurso de layout no aplicativo, o Android inicializa cada nó do layout em um +objeto em tempo de execução que pode ser usado para definir comportamentos adicionais, consultar o estado do objeto ou modificar o +layout.

    + +

    Para obter um guia completo para criar um layout de IU, consulte Layouts +XML. + + +

    Componentes da interface do usuário

    + +

    Você não precisa criar toda a IU usando objetos {@link android.view.View} e {@link +android.view.ViewGroup}. O Android fornece vários componentes de aplicativo que oferecem +um layout de IU padrão para os quais basta definir o conteúdo. Esses componentes de IU +têm um conjunto único de APIs que são descritas nos respectivos documentos, como Barra de ação, Caixas de diálogo e Notificações de status.

    + + diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/settings.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/settings.jd new file mode 100644 index 0000000000000000000000000000000000000000..f95966c37578da9aa6583b460b86c937494a6a0c --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=Configurações +page.tags=preferência,preferenceactivity,preferencefragment + +@jd:body + + + + + + + +

    Geralmente os aplicativos contêm configurações que permitem aos usuários modificar características e comportamentos do aplicativo. Por +exemplo: alguns aplicativos permitem aos usuários especificar se as notificações estão ativadas ou especificar a frequência +com que o aplicativo sincroniza dados com a nuvem.

    + +

    Para fornecer configurações ao aplicativo, é preciso usar +as APIs {@link android.preference.Preference} do Android para programar uma interface coerente +com a experiência do usuário em outros aplicativos Android (inclusive as configurações do sistema). Este documento descreve +como programar as configurações do aplicativo por meio de APIs {@link android.preference.Preference}.

    + +
    +

    Projeto de configurações

    +

    Para obter mais informações sobre o projeto de configurações, leia o guia de projeto Configurações.

    +
    + + + +

    Figura 1. Capturas de tela das configurações do aplicativo Mensagens +do Android. A seleção de um item definido por uma {@link android.preference.Preference} +abre uma interface para alterar a configuração.

    + + + + +

    Visão geral

    + +

    Em vez de usar objetos {@link android.view.View} para criar a interface do usuário, as configurações +são criadas por meio de várias subclasses da classe {@link android.preference.Preference} +declaradas em um arquivo XML.

    + +

    Os objetos {@link android.preference.Preference} são as peças fundamentais de uma única +configuração. Cada {@link android.preference.Preference} aparece como um item em uma lista e oferece a IU +adequada para que os usuários modifiquem a configuração. Por exemplo: uma {@link +android.preference.CheckBoxPreference} cria um item de lista que exibe uma caixa de seleção e uma {@link +android.preference.ListPreference} cria um item que abre uma caixa de diálogo com uma lista de opções.

    + +

    Cada {@link android.preference.Preference} adicionada tem um par de valor-chave correspondente +que o sistema usa para salvar a configuração em um arquivo +{@link android.content.SharedPreferences} padrão para as configurações do aplicativo. Quando o usuário altera uma configuração, o sistema atualiza o valor +correspondente no arquivo {@link android.content.SharedPreferences}. O único momento em que +se deve interagir diretamente com o arquivo {@link android.content.SharedPreferences} associado +é no momento de ler o valor para determinar o comportamento do aplicativo com base na configuração do usuário.

    + +

    O valor salvo em {@link android.content.SharedPreferences} para cada configuração pode ser +um dos seguintes tipos de dados:

    + +
      +
    • Boolean
    • +
    • Float
    • +
    • Int
    • +
    • Long
    • +
    • String
    • +
    • String {@link java.util.Set}
    • +
    + +

    Como a IU de configurações do aplicativo é criada com objetos {@link android.preference.Preference} + em vez de objetos +{@link android.view.View}, é preciso usar uma subclasse {@link android.app.Activity} ou +{@link android.app.Fragment} especializada para exibir as configurações de lista:

    + +
      +
    • Se o aplicativo for compatível com versões do Android anteriores à 3.0 (nível da API 10 ou anterior), será +necessário criar a atividade como uma extensão da classe {@link android.preference.PreferenceActivity}.
    • +
    • No Android 3.0 ou versões posteriores, deve-se usar um {@link android.app.Activity} tradicional +que hospeda um {@link android.preference.PreferenceFragment} que exige as configurações do aplicativo. +No entanto, pode-se também usar {@link android.preference.PreferenceActivity} para criar um layout de dois painéis +para telas maiores quando há vários grupos de configurações.
    • +
    + +

    Veja como configurar a {@link android.preference.PreferenceActivity} e instâncias de {@link +android.preference.PreferenceFragment} nas seções sobre a Criação de uma atividade de preferência e Uso +de fragmentos de preferência.

    + + +

    Preferências

    + +

    Toda configuração do aplicativo é representada por uma subclasse específica da classe {@link +android.preference.Preference}. Cada subclasse contém um conjunto de propriedades essenciais que permitem +especificar itens como o título da configuração e o valor padrão. Cada subclasse também oferece +suas propriedades e interface do usuário especializadas. Por exemplo: a figura 1 ilustra uma captura de tela +das configurações do aplicativo Mensagens. Cada item de lista na tela de configurações tem, como fundo, um objeto {@link +android.preference.Preference} diferente.

    + +

    A seguir há algumas das preferências mais comuns:

    + +
    +
    {@link android.preference.CheckBoxPreference}
    +
    Exibe um item com uma caixa de seleção para uma configuração que esteja ativada ou desativada. O valor +salvo é um booleano (true se estiver selecionada).
    + +
    {@link android.preference.ListPreference}
    +
    Abre uma caixa de diálogo com uma lista de botões de opção. O valor salvo +pode ser qualquer um dos tipos de valor compatíveis (listados acima).
    + +
    {@link android.preference.EditTextPreference}
    +
    Abre uma caixa de diálogo com um widget {@link android.widget.EditText}. O valor salvo é um {@link +java.lang.String}.
    +
    + +

    Consulte a classe {@link android.preference.Preference} para ver uma lista de todas as outras subclasses e +as propriedades correspondentes.

    + +

    É claro que as classes embutidas não acomodam todas as necessidades e o aplicativo pode exigir +algo mais especializado. Por exemplo: a plataforma atualmente não fornece nenhuma classe {@link +android.preference.Preference} para selecionar um número ou data. Portanto, pode ser necessário definir +a própria subclasse {@link android.preference.Preference}. Veja mais informações na seção sobre Composição de uma preferência personalizada.

    + + + +

    Definição de preferências em XML

    + +

    Embora se possa instanciar novos objetos {@link android.preference.Preference} em tempo de execução, +deve-se definir uma lista de configurações no XML com uma hierarquia +de objetos {@link android.preference.Preference}. Recomenda-se o uso de um arquivo XML para definir a coleção de configurações porque o arquivo +oferece uma estrutura fácil de ler e simples de atualizar. Além disso, as configurações do aplicativo +geralmente são predeterminadas, embora ainda seja possível modificar a coleção em tempo de execução.

    + +

    Cada subclasse {@link android.preference.Preference} pode ser declarada com um elemento XML +correspondente ao nome da classe, como {@code <CheckBoxPreference>}.

    + +

    É preciso salvar o arquivo XML no diretório {@code res/xml/}. Embora seja possível nomear livremente +o arquivo, é mais frequente vê-lo com o nome {@code preferences.xml}. Geralmente só é necessário um arquivo +porque as ramificações na hierarquia (que abrem sua própria lista de configurações) são declaradas +por meio de instâncias aninhadas de {@link android.preference.PreferenceScreen}.

    + +

    Observação: se você deseja criar um layout de vários painéis +para as configurações, serão necessários arquivos XML separados para cada fragmento.

    + +

    O nó raiz do arquivo XML deve ser um elemento {@link android.preference.PreferenceScreen +<PreferenceScreen>}. É dentro desse elemento que se adiciona cada {@link +android.preference.Preference}. Cada filho adicionado dentro do elemento +{@link android.preference.PreferenceScreen <PreferenceScreen>} é exibido com um item +único na lista de configurações.

    + +

    Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <CheckBoxPreference
    +        android:key="pref_sync"
    +        android:title="@string/pref_sync"
    +        android:summary="@string/pref_sync_summ"
    +        android:defaultValue="true" />
    +    <ListPreference
    +        android:dependency="pref_sync"
    +        android:key="pref_syncConnectionType"
    +        android:title="@string/pref_syncConnectionType"
    +        android:dialogTitle="@string/pref_syncConnectionType"
    +        android:entries="@array/pref_syncConnectionTypes_entries"
    +        android:entryValues="@array/pref_syncConnectionTypes_values"
    +        android:defaultValue="@string/pref_syncConnectionTypes_default" />
    +</PreferenceScreen>
    +
    + +

    Nesse exemplo, existe um {@link android.preference.CheckBoxPreference} e um {@link +android.preference.ListPreference}. Os dois itens contêm estes três atributos:

    + +
    +
    {@code android:key}
    +
    Esse atributo é necessário para preferências que persistem a um valor de dados. Ele especifica a chave +exclusiva (uma string) que o sistema usa ao salvar o valor dessa configuração em {@link +android.content.SharedPreferences}. +

    As únicas instâncias em que esse atributo é dispensável ocorrem quando a preferência é um +{@link android.preference.PreferenceCategory} ou {@link android.preference.PreferenceScreen}, +ou quando a preferência especifica um {@link android.content.Intent} para invocar (com um elemento {@code <intent>}) ou um {@link android.app.Fragment} para exibir (com um atributo {@code +android:fragment}).

    +
    +
    {@code android:title}
    +
    Fornece à configuração um nome visível ao usuário.
    +
    {@code android:defaultValue}
    +
    Especifica o valor inicial que o sistema deve definir no arquivo {@link +android.content.SharedPreferences}. Deve-se fornecer um valor padrão para +todas as configurações.
    +
    + +

    Para mais informações sobre todos os outros atributos compatíveis, consulte a documentação {@link +android.preference.Preference} (e subclasse respectiva).

    + + +
    + +

    Figura 2. Definição de categorias +com títulos.
    1. A categoria é especificada pelo elemento {@link +android.preference.PreferenceCategory <PreferenceCategory>}.
    2. O título +é especificado com o atributo {@code android:title}.

    +
    + + +

    Quando a lista de configurações excede cerca de 10 itens, pode ser necessário adicionar títulos +para definir grupos de configurações ou exibir esses grupos +em uma tela separada. Essas opções são descritas nas seções a seguir.

    + + +

    Criação de grupos de configuração

    + +

    Se você apresentar uma lista de 10 ou mais configurações, +os usuários podem ter dificuldade de percorrê-las, compreendê-las e processá-las. Para solucionar isso, +pode-se dividir algumas ou todas as configurações em grupos, transformando uma longa lista +em várias listas mais curtas. Um grupo de configurações relacionadas pode ser apresentado de uma das seguintes formas:

    + + + +

    Pode-se usar uma ou ambas as técnicas de agrupamento para organizar as configurações do aplicativo. Ao decidir +qual delas usar e como dividir as configurações, deve-se seguir as diretrizes do guia +Configurações de Projeto do Android.

    + + +

    Uso de títulos

    + +

    Para usar divisores com cabeçalhos entre grupos de configurações (como ilustrado na figura 2), +coloca-se cada grupo de objetos {@link android.preference.Preference} dentro de {@link +android.preference.PreferenceCategory}.

    + +

    Por exemplo:

    + +
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <PreferenceCategory 
    +        android:title="@string/pref_sms_storage_title"
    +        android:key="pref_key_storage_settings">
    +        <CheckBoxPreference
    +            android:key="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_auto_delete"
    +            android:title="@string/pref_title_auto_delete"
    +            android:defaultValue="false"... />
    +        <Preference 
    +            android:key="pref_key_sms_delete_limit"
    +            android:dependency="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_delete_limit"
    +            android:title="@string/pref_title_sms_delete"... />
    +        <Preference 
    +            android:key="pref_key_mms_delete_limit"
    +            android:dependency="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_delete_limit"
    +            android:title="@string/pref_title_mms_delete" ... />
    +    </PreferenceCategory>
    +    ...
    +</PreferenceScreen>
    +
    + + +

    Uso de subtelas

    + +

    Para usar grupos de configurações em uma subtela (como ilustrado na figura 3), coloque o grupo +de objetos {@link android.preference.Preference} dentro de {@link +android.preference.PreferenceScreen}.

    + + +

    Figura 3. Subtelas de configuração. O elemento {@code +<PreferenceScreen>} cria +um item que, quando selecionado, abre uma lista separada para exibir as configurações aninhadas.

    + +

    Por exemplo:

    + +
    +<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    +    <!-- opens a subscreen of settings -->
    +    <PreferenceScreen
    +        android:key="button_voicemail_category_key"
    +        android:title="@string/voicemail"
    +        android:persistent="false">
    +        <ListPreference
    +            android:key="button_voicemail_provider_key"
    +            android:title="@string/voicemail_provider" ... />
    +        <!-- opens another nested subscreen -->
    +        <PreferenceScreen
    +            android:key="button_voicemail_setting_key"
    +            android:title="@string/voicemail_settings"
    +            android:persistent="false">
    +            ...
    +        </PreferenceScreen>
    +        <RingtonePreference
    +            android:key="button_voicemail_ringtone_key"
    +            android:title="@string/voicemail_ringtone_title"
    +            android:ringtoneType="notification" ... />
    +        ...
    +    </PreferenceScreen>
    +    ...
    +</PreferenceScreen>
    +
    + + +

    Uso de intenções

    + +

    Em alguns casos, pode ser necessário que um item de preferência abra em um atividade diferente +e não na tela de configuração, como um navegador da web para exibir uma página da web. Para invocar um {@link +android.content.Intent} quando o usuário seleciona um item de preferência, adicione um elemento {@code <intent>} +como filho do elemento {@code <Preference>} correspondente.

    + +

    Por exemplo, a seguir apresenta-se como usar um item de preferência para abrir uma página da web:

    + +
    +<Preference android:title="@string/prefs_web_page" >
    +    <intent android:action="android.intent.action.VIEW"
    +            android:data="http://www.example.com" />
    +</Preference>
    +
    + +

    É possível criar intenções implícitas e explícitas usando os seguintes atributos:

    + +
    +
    {@code android:action}
    +
    A ação a atribuir, conforme o método {@link android.content.Intent#setAction setAction()}. +
    +
    {@code android:data}
    +
    Os dados a atribuir, conforme o método {@link android.content.Intent#setData setData()}.
    +
    {@code android:mimeType}
    +
    O tipo MIME atribuir, conforme o método {@link android.content.Intent#setType setType()}. +
    +
    {@code android:targetClass}
    +
    A parte de classe do nome do componente, conforme o método {@link android.content.Intent#setComponent +setComponent()}.
    +
    {@code android:targetPackage}
    +
    A parte de pacote do nome do componente, conforme o método {@link +android.content.Intent#setComponent setComponent()}.
    +
    + + + +

    Criação de uma atividade de preferência

    + +

    Para exibir as configurações em uma atividade, estenda a classe {@link +android.preference.PreferenceActivity}. É uma extensão da classe tradicional {@link +android.app.Activity} que exibe uma lista de configurações com base em uma hierarquia de objetos {@link +android.preference.Preference}. A {@link android.preference.PreferenceActivity} +automaticamente persiste às configurações associadas a cada {@link +android.preference.Preference} quando o usuário faz uma alteração.

    + +

    Observação: ao desenvolver um aplicativo para Android 3.0 +ou superior, deve-se usar o {@link android.preference.PreferenceFragment}. Consulte a próxima +seção sobre o Uso de fragmentos de preferência.

    + +

    O mais importante é não carregar um layout de vistas durante o retorno de chamada {@link +android.preference.PreferenceActivity#onCreate onCreate()}. Em vez disso, chama-se {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} +para adicionar à atividade as preferências declaradas em um arquivo XML. Por exemplo: abaixo há o código mínimo +necessário para um {@link android.preference.PreferenceActivity} funcional:

    + +
    +public class SettingsActivity extends PreferenceActivity {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +}
    +
    + +

    Na verdade, esse código é suficiente para alguns aplicativos porque, assim que o usuário modifica uma preferência, +o sistema salva as alterações em um arquivo padrão {@link android.content.SharedPreferences} +que os componentes do outro aplicativo poderá ler quando for necessário verificar as configurações do usuário. No entanto, +muitos aplicativos exigem um pouco mais de código para escutar as alterações que ocorrem nas preferências. +Para informações sobre a escuda de alterações no arquivo {@link android.content.SharedPreferences}, +consulte a seção sobre Leitura de preferências.

    + + + + +

    Uso de fragmentos de preferência

    + +

    Ao desenvolver para Android 3.0 (nível da API 11) ou versões posteriores, deve-se usar um {@link +android.preference.PreferenceFragment} para exibir a lista de objetos {@link android.preference.Preference}. + Pode-se adicionar um {@link android.preference.PreferenceFragment} a qualquer atividade — não é +necessário usar {@link android.preference.PreferenceActivity}.

    + +

    Os fragmentos permitem uma arquitetura +mais flexível para o aplicativo em comparação com o uso de apenas atividades para qualquer +tipo de atividade criada. Assim, sugerimos usar {@link +android.preference.PreferenceFragment} para controlar a exibição das configurações em vez de {@link +android.preference.PreferenceActivity} sempre que possível.

    + +

    A implementação de {@link android.preference.PreferenceFragment} pode ser tão simples +quanto definir o método {@link android.preference.PreferenceFragment#onCreate onCreate()} para carregar +um arquivo de preferências com {@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()}. Por exemplo:

    + +
    +public static class SettingsFragment extends PreferenceFragment {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        // Load the preferences from an XML resource
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +    ...
    +}
    +
    + +

    É possível adicionar esse fragmento a um {@link android.app.Activity} como se faria com qualquer outro +{@link android.app.Fragment}. Por exemplo:

    + +
    +public class SettingsActivity extends Activity {
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        // Display the fragment as the main content.
    +        getFragmentManager().beginTransaction()
    +                .replace(android.R.id.content, new SettingsFragment())
    +                .commit();
    +    }
    +}
    +
    + +

    Observação: um {@link android.preference.PreferenceFragment} não tem +seu próprio objeto {@link android.content.Context}. Se for necessário um objeto {@link android.content.Context} +, é possível chamar {@link android.app.Fragment#getActivity()}. No entanto, tome cuidado para chamar +{@link android.app.Fragment#getActivity()} somente quando o fragmento estiver anexado a uma atividade. Quando +o fragmento ainda não estiver anexado, ou tiver sido separado durante o fim do seu ciclo de vida, {@link +android.app.Fragment#getActivity()} retornará como nulo.

    + + +

    Configuração de valores padrão

    + +

    As preferências criadas provavelmente definem alguns comportamentos importantes do aplicativo, portanto +é necessário inicializar o arquivo {@link android.content.SharedPreferences} associado +com os valores padrão de cada {@link android.preference.Preference} quando o usuário abre o aplicativo +pela primeira vez.

    + +

    A primeira coisa a fazer é especificar o valor padrão de cada objeto {@link +android.preference.Preference} +no arquivo XML com o atributo {@code android:defaultValue}. O valor pode ser qualquer tipo de dados +apropriado para o objeto {@link android.preference.Preference} correspondente. Por +exemplo:

    + +
    +<!-- default value is a boolean -->
    +<CheckBoxPreference
    +    android:defaultValue="true"
    +    ... />
    +
    +<!-- default value is a string -->
    +<ListPreference
    +    android:defaultValue="@string/pref_syncConnectionTypes_default"
    +    ... />
    +
    + +

    Em seguida, a partir do método {@link android.app.Activity#onCreate onCreate()} na atividade principal +do aplicativo — e em qualquer outra atividade pela qual o usuário possa entrar no aplicativo pela +primeira vez —, chame {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}:

    + +
    +PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
    +
    + +

    Essa chamada durante {@link android.app.Activity#onCreate onCreate()} garante que o aplicativo +seja adequadamente inicializado com as configurações padrão, que o aplicativo pode precisar ler +para determinar alguns comportamentos (se, por exemplo, baixará dados enquanto estiver +em uma rede de celular).

    + +

    Esse método usa três argumentos:

    +
      +
    • O {@link android.content.Context} do aplicativo.
    • +
    • O ID de recurso do arquivo XML de preferências para o qual você deseja definir os valores padrão.
    • +
    • Booleano que indica se os valores padrão devem ser definidos mais de uma vez. +

      Quando false, o sistema define os valores parão somente se esse método nunca tiver +sido chamado (ou se {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} +no arquivo de preferências compartilhadas do valor padrão for falso).

    • +
    + +

    Enquanto o terceiro argumento estiver definido como false, pode-se chamar esse método com segurança +toda vez que a atividade iniciar sem substituir as preferências salvas do usuário redefinindo-as +para os padrões. No entanto, se ele for definido como true, todos os valores anteriores +serão substituídos pelos padrões.

    + + + +

    Uso de cabeçalhos de preferência

    + +

    Em casos raros, pode ser necessário projetar as configurações de forma que a primeira tela +exiba somente uma lista de subtelas (como as do aplicativo Configurações +conforme ilustrado nas figuras 4 e 5). Ao desenvolver um projeto desse tipo para Android 3.0 ou versão posterior, +deve-se usar um novo recurso "cabeçalhos" no Android 3.0 em vez de criar subtelas com elementos +{@link android.preference.PreferenceScreen} aninhados.

    + +

    Para criar as configurações com cabeçalhos, é preciso:

    +
      +
    1. Separar cada grupo de configurações em instâncias separadas de {@link +android.preference.PreferenceFragment}. Ou seja, cada grupo de configurações precisa de um arquivo +XML separado.
    2. +
    3. Criar um arquivo XML de cabeçalhos que lista cada grupo de configurações e declara que fragmento +contém a lista correspondente de configurações.
    4. +
    5. Estender a classe {@link android.preference.PreferenceActivity} para hospedar as configurações.
    6. +
    7. Implementar o retorno de chamada {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} para especificar +o arquivo de cabeçalhos.
    8. +
    + +

    Um grande benefício de usar esse modelo é que {@link android.preference.PreferenceActivity} +automaticamente apresenta o layout de dois painéis ilustrado na figura 4 ao executar em telas grandes.

    + +

    Mesmo se o aplicativo for compatível com versões de Android anteriores à 3.0, é possível programar +o aplicativo para usar {@link android.preference.PreferenceFragment} para uma apresentação em dois painéis +em dispositivos mais novos e ser compatível com a hierarquia tradicional multitelas +em dispositivos mais antigos (veja a seção sobre Compatibilidade de versões +mais antigas com cabeçalhos de preferência).

    + + +

    Figura 4. Layout de dois painéis com cabeçalhos.
    1. Os cabeçalhos +são definidos com um arquivo XML de cabeçalhos.
    2. Cada grupo de configurações é definido por um +{@link android.preference.PreferenceFragment} especificado por um elemento {@code <header>} +no arquivo de cabeçalhos.

    + + +

    Figura 5. Um dispositivo celular com cabeçalhos de configuração. Quando +um item é selecionado o {@link android.preference.PreferenceFragment} associado substitui +os cabeçalhos.

    + + +

    Criação do arquivo de cabeçalhos

    + +

    Cada grupo de configurações na lista de cabeçalhos é especificado por um único elemento {@code <header>} +dentro de um elemento raiz {@code <preference-headers>}. Por exemplo:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    +    <header 
    +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one" />
    +    <header 
    +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" >
    +        <!-- key/value pairs can be included as arguments for the fragment. -->
    +        <extra android:name="someKey" android:value="someHeaderValue" />
    +    </header>
    +</preference-headers>
    +
    + +

    Com o atributo {@code android:fragment}, cada cabeçalho declara uma instância de {@link +android.preference.PreferenceFragment} que deve abrir quando o usuário selecionar o cabeçalho.

    + +

    O elemento {@code <extras>} permite passar os pares de valores-chave para o fragmento em um {@link +android.os.Bundle}. O fragmento pode recuperar os argumentos chamando {@link +android.app.Fragment#getArguments()}. Há vários motivos para passar argumentos ao fragmento, +mas um bom motivo é reutilizar a mesma subclasse de {@link +android.preference.PreferenceFragment} para cada grupo e usar o argumento para especificar +o arquivo XML de preferências que o fragmento deve carregar.

    + +

    Por exemplo, a seguir há um fragmento que pode ser reutilizado em vários grupos de configurações, quando +cada cabeçalho define um argumento {@code <extra>} com a chave {@code "settings"}:

    + +
    +public static class SettingsFragment extends PreferenceFragment {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        String settings = getArguments().getString("settings");
    +        if ("notifications".equals(settings)) {
    +            addPreferencesFromResource(R.xml.settings_wifi);
    +        } else if ("sync".equals(settings)) {
    +            addPreferencesFromResource(R.xml.settings_sync);
    +        }
    +    }
    +}
    +
    + + + +

    Exibição de cabeçalhos

    + +

    Para exibir os cabeçalhos de preferência, é preciso implementar o método de retorno de chamada {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} e chamar +{@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}. Por exemplo:

    + +
    +public class SettingsActivity extends PreferenceActivity {
    +    @Override
    +    public void onBuildHeaders(List<Header> target) {
    +        loadHeadersFromResource(R.xml.preference_headers, target);
    +    }
    +}
    +
    + +

    Quando o usuário seleciona um item de uma lista de cabeçalhos, o sistema abre o {@link +android.preference.PreferenceFragment} associado.

    + +

    Observação: ao usar cabeçalhos de preferência, a subclasse de {@link +android.preference.PreferenceActivity} não precisa implementar o método {@link +android.preference.PreferenceActivity#onCreate onCreate()} porque a única tarefa +necessária para a atividade é carregar os cabeçalhos.

    + + +

    Compatibilidade de versões mais antigas com cabeçalhos de preferência

    + +

    Se o aplicativo for compatível com versões de Android anteriores à 3.0, ainda será possível usar cabeçalhos +para fornecer um layout em dois painéis ao executar no Android 3.0 e versões posteriores. Basta criar um arquivo XML de preferências +adicional que usa elementos básicos {@link android.preference.Preference +<Preference>} que se comportam como os itens de cabeçalho (para uso das versões mais antigas +do Android).

    + +

    No entanto, em vez de abrir um novo {@link android.preference.PreferenceScreen}, cada elemento {@link +android.preference.Preference <Preference>} envia um {@link android.content.Intent} +ao {@link android.preference.PreferenceActivity} que especifica que arquivo XML de preferência +carregar.

    + +

    Por exemplo, abaixo há um arquivo XML de cabeçalhos de preferência usado no Android 3.0 +e posterior ({@code res/xml/preference_headers.xml}):

    + +
    +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    +    <header 
    +        android:fragment="com.example.prefs.SettingsFragmentOne"
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one" />
    +    <header 
    +        android:fragment="com.example.prefs.SettingsFragmentTwo"
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" />
    +</preference-headers>
    +
    + +

    E apresenta-se também um arquivo de preferências que fornece os mesmos cabeçalhos de versões de Android +mais antigas que a 3.0 ({@code res/xml/preference_headers_legacy.xml}):

    + +
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <Preference 
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one"  >
    +        <intent 
    +            android:targetPackage="com.example.prefs"
    +            android:targetClass="com.example.prefs.SettingsActivity"
    +            android:action="com.example.prefs.PREFS_ONE" />
    +    </Preference>
    +    <Preference 
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" >
    +        <intent 
    +            android:targetPackage="com.example.prefs"
    +            android:targetClass="com.example.prefs.SettingsActivity"
    +            android:action="com.example.prefs.PREFS_TWO" />
    +    </Preference>
    +</PreferenceScreen>
    +
    + +

    Como a compatibilidade com {@code <preference-headers>} foi adicionada no Android 3.0, o sistema chama +{@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} em seu {@link +android.preference.PreferenceActivity} somente ao executar em Androd 3.0 ou posterior. Para carregar +o arquivo de cabeçalhos de “legado" ({@code preference_headers_legacy.xml}), é preciso verificar a versãodo Android +e, se a versão for mais antiga que o Android 3.0 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}), chama {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} +para carregar o arquivo de cabeçalho legado. Por exemplo:

    + +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    ...
    +
    +    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    +        // Load the legacy preferences headers
    +        addPreferencesFromResource(R.xml.preference_headers_legacy);
    +    }
    +}
    +
    +// Called only on Honeycomb and later
    +@Override
    +public void onBuildHeaders(List<Header> target) {
    +   loadHeadersFromResource(R.xml.preference_headers, target);
    +}
    +
    + +

    Depois só resta tratar o {@link android.content.Intent} passado para a atividade +para identificar que arquivo de preferências carregar. Portanto, para recuperar a ação da intenção e compará-la +com strings de ações conhecidas usadas nas tags de {@code <intent>} do XML de preferências:

    + +
    +final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
    +...
    +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +
    +    String action = getIntent().getAction();
    +    if (action != null && action.equals(ACTION_PREFS_ONE)) {
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +    ...
    +
    +    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    +        // Load the legacy preferences headers
    +        addPreferencesFromResource(R.xml.preference_headers_legacy);
    +    }
    +}
    +
    + +

    Observe que chamadas consecutivas a {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} +empilharão todas as preferências em uma única lista, portanto certifique-se de que seja chamado somente uma vez, encadeando +as condições com declarações else-if.

    + + + + + +

    Leitura de preferências

    + +

    Por padrão, todas as preferências do aplicativo são salvas em um arquivo acessível de qualquer lugar +dentro do aplicativo chamando o método estático {@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()}. Isso retorna o objeto {@link +android.content.SharedPreferences} que contém todos os pares de valores-chave associados +aos objetos {@link android.preference.Preference} usados em {@link +android.preference.PreferenceActivity}.

    + +

    Por exemplo, abaixo apresenta-se como ler um dos valores de preferência de outra atividade +no aplicativo:

    + +
    +SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
    +String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
    +
    + + + +

    Escuta de alterações de preferência

    + +

    Há alguns motivos pelos quais pode ser necessário ser notificado assim que o usuário altera +uma das preferências. Para receber um retorno de chamada quando acontece uma alteração em alguma das preferências, +implemente a interface {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} e registre a escuta +para o objeto {@link android.content.SharedPreferences} chamando {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}.

    + +

    A interface tem somente um método de retorno de chamada, {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()}, e pode ser mais fácil implementar a interface como parte +da atividade. Por exemplo:

    + +
    +public class SettingsActivity extends PreferenceActivity
    +                              implements OnSharedPreferenceChangeListener {
    +    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
    +    ...
    +
    +    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
    +        String key) {
    +        if (key.equals(KEY_PREF_SYNC_CONN)) {
    +            Preference connectionPref = findPreference(key);
    +            // Set summary to be the user-description for the selected value
    +            connectionPref.setSummary(sharedPreferences.getString(key, ""));
    +        }
    +    }
    +}
    +
    + +

    Nesse exemplo, o método verifica se a configuração alterada se destina a uma chave de preferência conhecida. Ele +chama {@link android.preference.PreferenceActivity#findPreference findPreference()} para obter +o objeto {@link android.preference.Preference} alterado para que possa modificar o sumário +do item como uma descrição da seleção do usuário. Ou seja, quando a configuração for uma {@link +android.preference.ListPreference} ou outra configuração de múltipla escolha, deve-se chamar {@link +android.preference.Preference#setSummary setSummary()} quando a configuração for alterada para exibir +o status atual (como a configuração Suspensão mostrada na figura 5).

    + +

    Observação: conforme descrito no documento do Projeto para Android sobre Configurações, recomendamos atualizar +o sumário de {@link android.preference.ListPreference} a cada vez que o usuário alterar a preferência +para descrever a configuração atual.

    + +

    Para um gerenciamento adequado do ciclo de vida na atividade, recomendamos registrar e remover o registro +de {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener} durante os retornos de chamada de {@link +android.app.Activity#onResume} e {@link android.app.Activity#onPause} respectivamente:

    + +
    +@Override
    +protected void onResume() {
    +    super.onResume();
    +    getPreferenceScreen().getSharedPreferences()
    +            .registerOnSharedPreferenceChangeListener(this);
    +}
    +
    +@Override
    +protected void onPause() {
    +    super.onPause();
    +    getPreferenceScreen().getSharedPreferences()
    +            .unregisterOnSharedPreferenceChangeListener(this);
    +}
    +
    + +

    Atenção: ao chamar {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}, o gerenciador de preferências +não armazena atualmente uma referência à escuta. É preciso armazenar uma referência +forte à escuta, senão ela será suscetível à coleta de lixo. Recomendamos +manter uma referência à escuta nos dados de instância de um objeto +que existirá enquanto a escuta for necessária.

    + +

    Por exemplo: no código a seguir, o autor da chamada não mantém nenhuma +referência à escuta. Como resultado, a escuta estará sujeita à coleta de lixo +e falhará futuramente em algum momento indeterminado:

    + +
    +prefs.registerOnSharedPreferenceChangeListener(
    +  // Bad! The listener is subject to garbage collection!
    +  new SharedPreferences.OnSharedPreferenceChangeListener() {
    +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    +    // listener implementation
    +  }
    +});
    +
    + +

    Em vez disso, armazene uma referência à escuta nos dados de instância de um objeto +que existirá enquanto a escuta for necessária:

    + +
    +SharedPreferences.OnSharedPreferenceChangeListener listener =
    +    new SharedPreferences.OnSharedPreferenceChangeListener() {
    +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    +    // listener implementation
    +  }
    +};
    +prefs.registerOnSharedPreferenceChangeListener(listener);
    +
    + +

    Gerenciamento de uso de rede

    + + +

    A partir do Android 4.0, o aplicativo Configurações do sistema permite aos usuários ver o quanto +de dados de rede que os aplicativos usam em primeiro e segundo plano. Portanto, os usuários +podem desativar os dados em segundo plano de aplicativos individuais. Para evitar que os usuários desativem +o acesso do aplicativo a dados em segundo plano, deve-se usar a conexão de dados de forma eficiente +e permitir aos usuários refinar o uso de dados do aplicativo por meio das configurações do aplicativo.

    + +

    Por exemplo: deve-se permitir ao usuário controlar a frequência de sincronização dos dados do aplicativo para +uploads/downloads somente quando estiver em Wi-Fi, o aplicativo usar dados em deslocamento etc. Com esses +controles disponíveis para eles, é bem menos provável que os usuários desativem o acesso do aplicativo a dados +quando eles se aproximam dos limites que definem nas Configurações do sistema porque, em vez disso, podem controlar +precisamente a quantidade de dados que o aplicativo usa.

    + +

    Depois de adicionadas as preferências necessárias em {@link android.preference.PreferenceActivity} +para controlar os hábitos de dados do aplicativo, deve-se adicionar um filtro de intenções para {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} no arquivo de manifesto. Por exemplo:

    + +
    +<activity android:name="SettingsActivity" ... >
    +    <intent-filter>
    +       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
    +       <category android:name="android.intent.category.DEFAULT" />
    +    </intent-filter>
    +</activity>
    +
    + +

    Esse filtro de intenções indica ao sistema que se trata da atividade que controla +o uso de dados do aplicativo. Assim, quando o usuário inspeciona a quantidade de dados que o aplicativo está usando +no aplicativo Configurações do sistema, um botão Exibir configurações de aplicativo fica disponível e inicia +{@link android.preference.PreferenceActivity} para que o usuário refine a quantidade de dados que +o aplicativo usa.

    + + + + + + + +

    Composição de uma preferência personalizada

    + +

    A estrutura do Android contém uma variedade de subclasses {@link android.preference.Preference} que permitem +criar uma IU com diferentes tipos de configurações. +No entanto, pode ser necessário descobrir uma configuração pra a qual não há nenhuma solução embutida, +como um seletor de números ou seletor de datas. Nesse caso, será preciso criar uma preferência personalizada, estendendo +a classe {@link android.preference.Preference} ou uma das outras subclasses.

    + +

    Ao estender a classe {@link android.preference.Preference}, há algumas coisas importantes +a fazer:

    + +
      +
    • Especificar a interface do usuário exibida quando o usuário seleciona as configurações.
    • +
    • Salvar os valores da configuração conforme apropriado.
    • +
    • Inicializar {@link android.preference.Preference} com o valor atual (ou padrão) +quando ela é exibida.
    • +
    • Fornecer o valor padrão quando solicitado pelo sistema.
    • +
    • Se {@link android.preference.Preference} fornece sua própria IU (como uma caixa de diálogo, por exemplo), salve +e restaure o estado para tratar de alterações de ciclo de vida (como quando o usuário gira a tela).
    • +
    + +

    As seções a seguir descrevem como executar cada uma dessas tarefas.

    + + + +

    Especificação da interface do usuário

    + +

    Se a classe {@link android.preference.Preference} for estendida, será preciso implementar +{@link android.preference.Preference#onClick()} para definir a ação que ocorre quando +o usuário a seleciona. No entanto, a maioria das configurações personalizadas estendem {@link android.preference.DialogPreference} +para exibir uma caixa de diálogo, o que simplifica o procedimento. Quando se estende {@link +android.preference.DialogPreference}, é preciso chamar {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()} na classe +do construtor para especificar o layout da caixa de diálogo.

    + +

    Por exemplo, eis o construtor de um {@link +android.preference.DialogPreference} personalizado que declara o layout e especifica o texto dos botões padrão +da caixa de diálogo positiva e negativa:

    + +
    +public class NumberPickerPreference extends DialogPreference {
    +    public NumberPickerPreference(Context context, AttributeSet attrs) {
    +        super(context, attrs);
    +        
    +        setDialogLayoutResource(R.layout.numberpicker_dialog);
    +        setPositiveButtonText(android.R.string.ok);
    +        setNegativeButtonText(android.R.string.cancel);
    +        
    +        setDialogIcon(null);
    +    }
    +    ...
    +}
    +
    + + + +

    Salvamento do valor da configuração

    + +

    Para salvar um valor da configuração a qualquer momento, chame um dos métodos {@code persist*()} da classe {@link +android.preference.Preference}, como {@link +android.preference.Preference#persistInt persistInt()} se o valor da configuração for um inteiro +ou {@link android.preference.Preference#persistBoolean persistBoolean()} para salvar um booleano.

    + +

    Observação: cada {@link android.preference.Preference} pode salvar somente +um tipo de dados, portanto é preciso usar o método {@code persist*()} adequado para o tipo de dados usado pela +{@link android.preference.Preference} personalizada.

    + +

    Quando se opta por persistir, a configuração pode depender da classe {@link +android.preference.Preference} estendida. Se {@link +android.preference.DialogPreference} for estendida, deve-se persistir o valor somente quando a caixa de diálogo +fecha devido a um resultado positivo (o usuário seleciona o botão "OK").

    + +

    Quando uma {@link android.preference.DialogPreference} fecha, o sistema chama o método {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()}. O método contém um argumento +booleano que especifica se o resultado do usuário é "positivo" — se o valor é +true e, em seguida, o usuário selecionou o botão positivo e você deve salvar o novo valor. Por +exemplo:

    + +
    +@Override
    +protected void onDialogClosed(boolean positiveResult) {
    +    // When the user selects "OK", persist the new value
    +    if (positiveResult) {
    +        persistInt(mNewValue);
    +    }
    +}
    +
    + +

    Nesse exemplo, mNewValue é um membro da classe que retém o valor atual +da configuração. A chamada de {@link android.preference.Preference#persistInt persistInt()} salva o valor +no arquivo {@link android.content.SharedPreferences} (usando automaticamente a chave +especificada no arquivo XML dessa {@link android.preference.Preference}).

    + + +

    Inicialização do valor atual

    + +

    Quando o sistema adiciona o {@link android.preference.Preference} à tela, ele chama +{@link android.preference.Preference#onSetInitialValue onSetInitialValue()} para notificar +se a configuração tem um valor persistido. Se não houver valor persistido, essa chamada fornece +o valor padrão.

    + +

    O método {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} passa +um booleano, restorePersistedValue, para indicar se um valor já foi persistido +para a configuração. Se for true, deve-se recuperar o valor persistindo chamando-se + um dos métodos {@code getPersisted*()} da classe {@link +android.preference.Preference}, como {@link +android.preference.Preference#getPersistedInt getPersistedInt()} para um valor inteiro. Geralmente +se recupera o valor persistido para atualizar adequadamente a IU de forma a refletir +o valor salvo anteriormente.

    + +

    Se restorePersistedValue for false, deve-se +usar o valor padrão passado no segundo argumento.

    + +
    +@Override
    +protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    +    if (restorePersistedValue) {
    +        // Restore existing state
    +        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    +    } else {
    +        // Set default state from the XML attribute
    +        mCurrentValue = (Integer) defaultValue;
    +        persistInt(mCurrentValue);
    +    }
    +}
    +
    + +

    Cada método {@code getPersisted*()} pega um argumento que especifica o valor +padrão a usar caso não haja nenhum valor persistido ou se a chave não existir. No +exemplo acima, uma constante local é usada para especificar o valor padrão se {@link +android.preference.Preference#getPersistedInt getPersistedInt()} não puder retornar um valor persistido.

    + +

    Atenção: não é possível usar +defaultValue como valor padrão no método {@code getPersisted*()} porque +seu valor é sempre nulo quando restorePersistedValue é true.

    + + +

    Fornecimento de um valor padrão

    + +

    Se a instância da classe {@link android.preference.Preference} especificar um valor padrão +(com o atributo {@code android:defaultValue}), +o sistema chama {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()} quando instancia o objeto para recuperar o valor. É preciso +implementar esse método para que o sistema salve o valor padrão em {@link +android.content.SharedPreferences}. Por exemplo:

    + +
    +@Override
    +protected Object onGetDefaultValue(TypedArray a, int index) {
    +    return a.getInteger(index, DEFAULT_VALUE);
    +}
    +
    + +

    Os argumentos do método oferecem todo o necessário: a matriz de atributos e a posição +do índice do {@code android:defaultValue}, que é preciso recuperar. É preciso implementar esse método +para extrair o valor padrão do atributo porque deve-se especificar um valor padrão +local para o atributo caso o valor seja indefinido.

    + + + +

    Salvamento e restauração do estado da preferência

    + +

    Como um {@link android.view.View} em um layout, a subclasse {@link android.preference.Preference} +é responsável por salvar e restaurar seu estado caso a atividade ou fragmento seja +reiniciado (como ocorre quando o usuário gira a tela). Para salvar e restaurar +adequadamente o estado da classe {@link android.preference.Preference}, é preciso implementar +os métodos de retorno de chamada do ciclo de vida {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} e {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}.

    + +

    O estado de {@link android.preference.Preference} é definido por um objeto que implementa +a interface {@link android.os.Parcelable}. A estrutura do Android fornece esse objeto +como um ponto inicial para definir o objeto de estado: a classe {@link +android.preference.Preference.BaseSavedState}.

    + +

    Para definir como a classe {@link android.preference.Preference} salva seu estado, deve-se estender +a classe {@link android.preference.Preference.BaseSavedState}. É preciso substituir +alguns métodos e definir o objeto {@link android.preference.Preference.BaseSavedState#CREATOR}. +

    + +

    Na maioria dos aplicativos, é possível copiar a implementação a seguir e simplesmente alterar as linhas +que tratam o {@code value} se a subclasse {@link android.preference.Preference} salvar um tipo +de dados que não seja um inteiro.

    + +
    +private static class SavedState extends BaseSavedState {
    +    // Member that holds the setting's value
    +    // Change this data type to match the type saved by your Preference
    +    int value;
    +
    +    public SavedState(Parcelable superState) {
    +        super(superState);
    +    }
    +
    +    public SavedState(Parcel source) {
    +        super(source);
    +        // Get the current preference's value
    +        value = source.readInt();  // Change this to read the appropriate data type
    +    }
    +
    +    @Override
    +    public void writeToParcel(Parcel dest, int flags) {
    +        super.writeToParcel(dest, flags);
    +        // Write the preference's value
    +        dest.writeInt(value);  // Change this to write the appropriate data type
    +    }
    +
    +    // Standard creator object using an instance of this class
    +    public static final Parcelable.Creator<SavedState> CREATOR =
    +            new Parcelable.Creator<SavedState>() {
    +
    +        public SavedState createFromParcel(Parcel in) {
    +            return new SavedState(in);
    +        }
    +
    +        public SavedState[] newArray(int size) {
    +            return new SavedState[size];
    +        }
    +    };
    +}
    +
    + +

    Com a implementação acima de {@link android.preference.Preference.BaseSavedState} adicionada +ao aplicativo (geralmente como uma subclasse da subclasse {@link android.preference.Preference}), +é preciso implementar os métodos {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} e {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} +da subclasse {@link android.preference.Preference}.

    + +

    Por exemplo:

    + +
    +@Override
    +protected Parcelable onSaveInstanceState() {
    +    final Parcelable superState = super.onSaveInstanceState();
    +    // Check whether this Preference is persistent (continually saved)
    +    if (isPersistent()) {
    +        // No need to save instance state since it's persistent,
    +        // use superclass state
    +        return superState;
    +    }
    +
    +    // Create instance of custom BaseSavedState
    +    final SavedState myState = new SavedState(superState);
    +    // Set the state's value with the class member that holds current
    +    // setting value
    +    myState.value = mNewValue;
    +    return myState;
    +}
    +
    +@Override
    +protected void onRestoreInstanceState(Parcelable state) {
    +    // Check whether we saved the state in onSaveInstanceState
    +    if (state == null || !state.getClass().equals(SavedState.class)) {
    +        // Didn't save the state, so call superclass
    +        super.onRestoreInstanceState(state);
    +        return;
    +    }
    +
    +    // Cast state to custom BaseSavedState and pass to superclass
    +    SavedState myState = (SavedState) state;
    +    super.onRestoreInstanceState(myState.getSuperState());
    +    
    +    // Set this Preference's widget to reflect the restored state
    +    mNumberPicker.setValue(myState.value);
    +}
    +
    + diff --git a/docs/html-intl/intl/pt-br/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/pt-br/guide/topics/ui/ui-events.jd new file mode 100644 index 0000000000000000000000000000000000000000..e0ace1d4b3cac5a90467e8b3d2c9a936291c4431 --- /dev/null +++ b/docs/html-intl/intl/pt-br/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=Eventos de entrada +parent.title=Interface do usuário +parent.link=index.html +@jd:body + + + +

    No Android, há mais de uma maneira de interceptar os eventos da interação de um usuário com o aplicativo. +Ao considerar os eventos dentro da interface do usuário, a abordagem é capturar os eventos +de um objeto de View específico com o qual o usuário interage. A classe View fornece os meios para fazer isto.

    + +

    Dentro das várias classes View que você usará para compor o layout, é possível notar vários métodos +públicos de retorno de chamada que parecem úteis para eventos de IU. Esses métodos são chamados pela estrutura do Android quando +a ação respectiva ocorre neste objeto. Por exemplo, quando uma View (como um botão) é tocada, +o método onTouchEvent() é chamado neste objeto. No entanto, para interceptar isto, você deve estender +a classe e substituir o método. No entanto, estender todos os objetos de View +para lidar com tal evento não seria algo prático. É por isso que a classe View também contém +uma coleção de interfaces aninhadas com retornos de chamada que podem ser definidas com muito mais facilidade. Essas interfaces, +chamadas de escutas de evento, são a sua passagem para capturar a interação do usuário com a IU.

    + +

    Geralmente, as escutas de evento são usadas para escutar a interação do usuário. +No entanto, há casos em que você pode querer estender uma classe View para criar um componente personalizado. +Talvez você queira estender a classe {@link android.widget.Button} +para deixar algo mais extravagante. Neste caso, você poderá definir os comportamentos de evento padrão +para a classe usando manipuladores de evento.

    + + +

    Escutas de evento

    + +

    Uma escuta de evento é uma interface na classe {@link android.view.View} que contém +um único método de retorno de chamada. Esses métodos serão chamados pela estrutura do Android, quando a View para a qual a escuta +estiver registrada for ativada pela interação do usuário com o item na IU.

    + +

    Inclusos nas interfaces da escuta de evento estão os seguintes métodos de retorno de chamada:

    + +
    +
    onClick()
    +
    De {@link android.view.View.OnClickListener}. + Isto é chamado quando o usuário toca no item + (no modo de toque), ou atribui foco ao item com as teclas de navegação ou cursor de bola + e pressiona a tecla "enter" adequada ou pressiona o cursor de bola.
    +
    onLongClick()
    +
    De {@link android.view.View.OnLongClickListener}. + Isto é chamado quando o usuário toca e mantém o item pressionado (no modo de toque), + ou atribui foco ao item com as teclas de navegação ou cursor de bola + e mantém pressionada a tecla "enter" adequada ou o cursor de bola (por um segundo).
    +
    onFocusChange()
    +
    De {@link android.view.View.OnFocusChangeListener}. + Isto é chamado quando o usuário navega no ou do item, usando as teclas de navegação ou cursor de bola.
    +
    onKey()
    +
    De {@link android.view.View.OnKeyListener}. + Isto é chamado quando o usuário está com foco no item ou solta uma tecla de hardware no dispositivo.
    +
    onTouch()
    +
    De {@link android.view.View.OnTouchListener}. + Isto é chamado quando o usuário realiza uma ação qualificada como um toque de evento, incluindo o pressionamento, a liberação, + ou qualquer outro gesto de movimento na tela (dentro dos limites do item).
    +
    onCreateContextMenu()
    +
    De {@link android.view.View.OnCreateContextMenuListener}. + Isto é chamado quando um menu de contexto está sendo construído (como resultado de um "clique longo"). Consulte a discussão + sobre menus de contexto no guia do desenvolvedor Menus +.
    +
    + +

    Esses métodos são os únicos habitantes de suas respectivas interfaces. Para definir um desses métodos +e lidar com seus eventos, implemente a interface aninhada na atividade ou defina-a como uma classe anônima. +Em seguida, passe uma instância da implementação +para o respectivo método View.set...Listener(). (Ex.:, chame +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()} +e passe-o à implementação de {@link android.view.View.OnClickListener OnClickListener}.)

    + +

    O exemplo abaixo mostra como registrar uma escuta de clique para um botão.

    + +
    +// Create an anonymous implementation of OnClickListener
    +private OnClickListener mCorkyListener = new OnClickListener() {
    +    public void onClick(View v) {
    +      // do something when the button is clicked
    +    }
    +};
    +
    +protected void onCreate(Bundle savedValues) {
    +    ...
    +    // Capture our button from layout
    +    Button button = (Button)findViewById(R.id.corky);
    +    // Register the onClick listener with the implementation above
    +    button.setOnClickListener(mCorkyListener);
    +    ...
    +}
    +
    + +

    Você também pode achar mais conveniente implementar OnClickListener como parte da atividade. +Isto evitará carga adicional na classe e a alocação do objeto. Por exemplo:

    +
    +public class ExampleActivity extends Activity implements OnClickListener {
    +    protected void onCreate(Bundle savedValues) {
    +        ...
    +        Button button = (Button)findViewById(R.id.corky);
    +        button.setOnClickListener(this);
    +    }
    +
    +    // Implement the OnClickListener callback
    +    public void onClick(View v) {
    +      // do something when the button is clicked
    +    }
    +    ...
    +}
    +
    + +

    Observe que o retorno de chamada onClick() no exemplo acima +não tem valor de retorno, mas outros métodos de escuta de evento podem retornar um booleano. O motivo +depende do evento. Para os poucos que retornam, apresenta-se a razão:

    +
      +
    • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()} - + Isto retorna um booleano para indicar se você consumiu o evento e se ele deve ser levado adiante. + Ou seja, ele retorna verdadeiro para indicar que você lidou com o evento e não deve seguir adiante; + ou retorna falso caso você não tenha lidado com ele e/ou o evento deva continuar para qualquer + outra escuta de clique.
    • +
    • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()} - + Isto retorna um booleano para indicar se você consumiu o evento e se ele deve ser levado adiante. + Ou seja, ele retorna verdadeiro para indicar que você lidou com o evento e não deve seguir adiante; + ou retorna falso caso você não tenha lidado com ele e/ou o evento deva continuar para qualquer + outra escuta de tecla.
    • +
    • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()} - + Isto retorna um booleano para indicar se a escuta consome este evento. O importante é que este evento + pode possuir várias ações que se seguem mutuamente. Portanto, se retornar falso quando + o evento de ação inferior for recebido, você indicará que não consumiu o evento e que não está + interessado em ações subsequentes deste evento. Logo, você não será chamado para outras ações + dentro do evento, como um gesto de dedo ou um evento de ação para cima eventual.
    • +
    + +

    Lembre-se de que os eventos de tecla de hardware sempre são entregues à vista atualmente em foco. Eles são enviados a partir da parte superior +da hierarquia de vistas e segue à parte inferior até atingir o destino adequado. Se a vista (ou um filho da vista) +estiver em foco, é possível ver o percurso do evento pelo método {@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()}. Como uma alternativa para capturar eventos de tecla por meio da vista, também é possível +receber todos os eventos dentro da Atividade com {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} +e {@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}.

    + +

    Além disso, ao pensar sobre a entrada de texto para o aplicativo, lembre-se de que vários dispositivos possuem apenas +métodos de entrada de software. Tais métodos não precisam ser baseados em teclas; alguns podem usar entrada de texto por voz, por escrita e outros. Mesmo se um método de entrada +apresentar uma interface parecida com teclado, geralmente ele não ativa +a família de eventos {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}. Nunca deve-se compilar +uma IU que exija pressionamentos de teclas específicas para ser controlada, a não ser que você queira limitar o aplicativo a dispositivos +com um teclado físico. Em particular, não confie nestes métodos para validar a entrada quando o usuário pressiona a tecla +de retorno; em vez disso, use ações como {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} para sinalizar +ao método de entrada como o aplicativo espera reagir para que ele possa alterar a IU de forma significativa. Evite suposições +sobre como um método de entrada de software deve funcionar e confie apenas no fornecimento do texto já formatado para o aplicativo.

    + +

    Observação: o Android chamará manipuladores de evento e, em seguida, manipuladores adequados padrão +a partir da segunda definição de classe. Logo, retornar verdadeiro destas escutas de evento +interromperá a propagação do evento para outras escutas de evento e também bloqueará o retorno de chamada +para o manipulador de evento padrão na vista. Portanto, certifique-se de que quer encerrar o evento ao retornar verdadeiro.

    + + +

    Manipuladores de evento

    + +

    Se estiver compilando um componente personalizado a partir de View, então você poderá definir vários métodos de retorno de chamada +usados como manipuladores de evento padrão. +No documento sobre Componentes +personalizados, você aprenderá a ver alguns dos retornos de chamada usados para lidar com eventos, +incluindo:

    +
      +
    • {@link android.view.View#onKeyDown} - Chamado quando um novo evento de tecla ocorre.
    • +
    • {@link android.view.View#onKeyUp} - Chamado quando um evento de tecla para cima ocorre.
    • +
    • {@link android.view.View#onTrackballEvent} - Chamado quando um evento de movimento do cursor de bola ocorre.
    • +
    • {@link android.view.View#onTouchEvent} - Chamado quando um evento de movimento de toque ocorre.
    • +
    • {@link android.view.View#onFocusChanged} - Chamado quando a vista ganha ou perde foco.
    • +
    +

    Há alguns outros métodos que você deve ter ciência que não fazem parte da classe View, +mas podem ter impacto direto na maneira de lidar com os eventos. Portanto, ao gerenciar eventos mais complexos +dentro de um layout, considere esses outros métodos:

    +
      +
    • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)} - Isto permite que {@link + android.app.Activity} intercepte todos os evento de toque antes de serem enviados à janela.
    • +
    • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)} - Isto permite que {@link + android.view.ViewGroup} assista aos eventos à medida que são enviados para as vistas filho.
    • +
    • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)} - Chame isto + sobre uma Vista pai para indicar que ela não deve interceptar eventos de toque com {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)}.
    • +
    + +

    Modo de toque

    +

    +Quando um usuário está navegando em uma interface do usuário com teclas direcionais ou cursor de bola, +é necessário fornecer foco para itens de ação (como botões) para que o usuário possa +ver o que aceitará entrada. Se o dispositivo tiver capacidades de toque, no entanto, e o usuário +começar a interagir com a interface por meio de toque, então não é mais necessário +destacar itens ou fornecer foco para uma vista específica. Contudo, há um modo +de interação chamado "modo de toque". +

    +

    +Para dispositivos com capacidades de toque, quando o usuário toca na tela, o dispositivo +entra no modo de toque. A partir deste ponto, somente vistas que tiverem +{@link android.view.View#isFocusableInTouchMode} como verdadeiro poderão ter foco, como widgets de edição de texto. +Outras vistas tocáveis, como botões, não receberão foco ao serem tocadas. Em vez disso, +elas simplesmente dispararão escutas de clique quando forem pressionadas. +

    +

    +Sempre que um usuário pressionar teclas direcionais ou rolar com o cursor de bola, o dispositivo +sairá do modo de toque e encontrará uma vista para atribuir foco. Agora, o usuário pode retomar a interação +com a interface do usuário sem tocar na tela. +

    +

    +O estado de modo de toque é mantido em todo o sistema (todas as janelas e atividades). +Para consultar o estado atual, é possível chamar +{@link android.view.View#isInTouchMode} para ver se o dispositivo está no modo de toque no momento. +

    + + +

    Tratamento de foco

    + +

    A estrutura lidará com a rotina de movimento de foco em resposta à entrada do usuário. +Isto inclui a mudança de foco à medida que as vistas são removidas ou ocultadas, ou à medida que novas +vistas se tornem disponíveis. As vistas indicam a prontidão para receber foco +por meio do método {@link android.view.View#isFocusable()}. Para determinar se uma vista pode receber +foco, chame {@link android.view.View#setFocusable(boolean) setFocusable()}. Quando no modo de toque, +é possível consultar se uma vista permite foco com {@link android.view.View#isFocusableInTouchMode()}. +É possível alterar isto com {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()}. +

    + +

    O movimento de foco é baseado em um algoritmo que encontra um semelhante mais próximo +em uma dada direção. Em casos raros, o algoritmo padrão pode não corresponder +ao comportamento pretendido do desenvolvedor. Nessas situações, é possível +fornecer substituições explícitas com os seguintes atributos XML no arquivo do layout: +nextFocusDown, nextFocusLeft, nextFocusRighte +nextFocusUp. Adicione um desses atributos à vista a partir +do foco que ela está abandonando. Defina o valor do atributo para ser o ID da vista +para o foco que deve ser fornecido. Por exemplo:

    +
    +<LinearLayout
    +    android:orientation="vertical"
    +    ... >
    +  <Button android:id="@+id/top"
    +          android:nextFocusUp="@+id/bottom"
    +          ... />
    +  <Button android:id="@+id/bottom"
    +          android:nextFocusDown="@+id/top"
    +          ... />
    +</LinearLayout>
    +
    + +

    Geralmente, neste layout vertical, navegar para cima a partir do primeiro botão +não resultaria em nada, nem navegar para baixo a partir do segundo botão. Agora que o botão superior +definiu o botão do fundo como nextFocusUp (e vice-versa), o foco da navegação +alternará de "cima para baixo" e "baixo para cima".

    + +

    Caso queira declarar uma vista como alvo de foco na IU (quando tradicionalmente não é), +adicione o atributo XML android:focusable à vista, na declaração do layout. +Defina o valor como verdadeiro. Também é possível declarar uma vista +como alvo de foco no Modo de Toque com android:focusableInTouchMode.

    +

    Para solicitar foco a uma determinada Vista, chame {@link android.view.View#requestFocus()}.

    +

    Para ouvir eventos de foco (receber notificações quando uma vista receber ou perder foco), use +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}, +como discutido na seção Escutas de evento acima.

    + + + + diff --git a/docs/html-intl/intl/pt-br/index.jd b/docs/html-intl/intl/pt-br/index.jd index 4d0a39e6ed155386848dc115241bc3bf0a142934..f5e1569dc8466ec1a80cbd71c8ea886e57db32c5 100644 --- a/docs/html-intl/intl/pt-br/index.jd +++ b/docs/html-intl/intl/pt-br/index.jd @@ -5,41 +5,81 @@ page.customHeadTag= -
--> - + -
+ + + -
+

Build Beautiful Apps

Resources to get you started with designing and developing for Android. diff --git a/docs/html-intl/intl/pt-br/preview/api-overview.jd b/docs/html-intl/intl/pt-br/preview/api-overview.jd deleted file mode 100644 index 139fb33d4a24157e63e61b859960806b998697e5..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/api-overview.jd +++ /dev/null @@ -1,521 +0,0 @@ -page.title=Visão geral da API -page.keywords=preview,sdk,compatibility -page.tags=previewresources, androidm -sdk.platform.apiLevel=22-mnc -page.image=images/cards/card-api-overview_16-9_2x.png -@jd:body - - - - -

O M Developer Preview fornece uma visualização avançada no próximo lançamento - para a plataforma Android, oferecendo novos recursos para desenvolvedores e usuários -de aplicativos. Este documento fornece uma introdução às APIs mais notáveis.

- -

O M Developer Preview foi feito para novos desenvolvedores -adotantes e testadores. Caso tenha interesse - em influenciar a direção da estrutura do Android, -experimente o M Developer - Preview e envie-nos feedback!

- -

Cuidado: não publique aplicativos -que usam o M Developer Preview na Google Play Store.

- -

Observação: este documento frequentemente -menciona classes e métodos que ainda não possuem material de referência disponível em developer.android.com. Esses elementos de API -são formatados em {@code code style} neste documento (sem hyperlinks). Para a -documentação de API preliminar destes elementos, faça o download da referência da prévia.

- -

Alterações importantes de comportamento

- -

Caso tenha publicado anteriormente um aplicativo para Android, saiba que ele pode ser afetado -pelas alterações na plataforma.

- -

Consulte alterações de comportamento para obter mais informações.

- -

Vínculo de aplicativo

-

Esta prévia aprimora o sistema de intenções do Android fornecendo vínculo de aplicativo mais poderoso. -Este recurso permite que você associe um aplicativo com um domínio de web próprio. Com base nesta -associação, a plataforma pode determinar o aplicativo padrão a ser usado para lidar com um link da web -em particular e ignorar a solicitação aos usuários para selecionar um aplicativo. Para aprender como implementar este aplicativo, consulte -vínculo de aplicativo. - -

Backup automático para aplicativos

-

O sistema agora realiza backup automático completo de dados e restauração para aplicativos. Este comportamento -é ativado por padrão para aplicativos com M Preview; não é necessário mais código adicional. Se -os usuários excluírem as contas da Google, os dados de backup também serão excluídos. Para aprender como este recurso -funciona e como configurar o backup no sistema do arquivo, consulte -backup automático para aplicativos.

- -

Autenticação

-

Esta prévia oferece novas APIs para permitir que você autentique os usuários usando digitalizadores de impressão digital em dispositivos -suportados e verifique o quão recentemente os usuários autenticaram pela última vez usando o -mecanismo de desbloqueio por dispositivo (como senha de tela de bloqueio). Use essas APIs em conjunto com -o sistema Android Keystore.

- -

Autenticação com impressão digital

- -

Para autenticar os usuários por meio de digitalização de impressão digital, adquira uma instância da nova classe -{@code android.hardware.fingerprint.FingerprintManager} e chame o método -{@code FingerprintManager.authenticate()}. O aplicativo deve ser executado em um dispositivo -compatível com sensor de impressão digital. Deve-se implementar a interface do usuário para o fluxo de autenticação -de impressão digital no aplicativo e usar o ícone de impressão digital padrão do Android na IU. -O ícone de impressão digital do Android ({@code c_fp_40px.png}) é incluído no -aplicativo de exemplo. Caso esteja desenvolvendo vários aplicativos que usam -autenticação de impressão digital, observe que cada aplicativo deve autenticar a impressão digital do usuário independentemente. -

- -

Para usar este recurso no aplicativo, adicione primeiro a permissão {@code USE_FINGERPRINT} no -manifesto.

- -
-<uses-permission
-        android:name="android.permission.USE_FINGERPRINT" />
-
- - - -

Para ver a implementação do aplicativo da autenticação com impressão digital, consulte o - - exemplo de caixa de diálogo de impressão digital.

- -

Caso esteja testando este recurso, siga estas etapas:

-
    -
  1. Instale o Android SDK Tools Revision 24.3, caso ainda não tenha instalado.
  2. -
  3. Registre uma nova impressão digital no emulador acessando -Configurações > Segurança > Impressão digital e, em seguida, siga as instruções de registro.
  4. -
  5. Use um emulador para emular eventos de toque de dedo com o -comando a seguir. Use o mesmo comando para emular os eventos de toque de impressão digital na tela de -bloqueio ou no aplicativo. -
    -adb -e emu finger touch <finger_id>
    -
    -

    No Windows, talvez seja necessário executar {@code telnet 127.0.0.1 } seguido de -{@code finger touch }. -

    -
  6. -
- -

Confirmação de credencial

-

O aplicativo pode autenticar os usuários com base no quão recentemente o dispositivo foi desbloqueado pela última vez. Este -recurso libera o usuário de ter que lembrar de senhas específicas de aplicativo extras e evita -a necessidade de implementar a própria interface do usuário de autenticação. O aplicativo deve usar este recurso -em conjunto com uma implementação de chave secreta ou pública para a implementação de usuário.

- -

Para definir uma duração de tempo limite em que a mesma chave pode ser usada novamente -após o usuário autenticar, chame o novo método -{@code android.security.keystore.KeyGenParameterSpec.setUserAuthenticationValidityDurationSeconds()} - ao definir um {@link javax.crypto.KeyGenerator} ou -{@link java.security.KeyPairGenerator}. Este recurso funciona para operações criptográficas -simétricas.

- -

Evite exibir o diálogo de nova autenticação excessivamente — os aplicativos devem tentar -usar o objeto criptográfico primeiro e, se o tempo limite expirar, usar o método -{@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence) createConfirmDeviceCredentialIntent()} - para autenticar novamente o usuário dentro do aplicativo. -

- -

Para ver uma implementação de aplicativo deste recurso, consulte o - -exemplo de confirmação de credencial.

- -

Compartilhamento direto

- - - -

Esta prévia fornece as APIs para tornar o compartilhamento intuitivo e rápido para os usuários. É possível -definir os alvos de compartilhamento direto que iniciam uma atividade específica no aplicativo. -Esses alvos de compartilhamento direto são expostos aos usuários por meio do menu Compartilhar. Este recurso permite que os usuários -compartilhem conteúdos aos alvos, como contatos, dentro de outros aplicativos. Por exemplo: o alvo de compartilhamento direto -pode iniciar uma atividade em outro aplicativo de rede social, o que permite que o usuário compartilhe o conteúdo diretamente -para um amigo ou comunidade específica neste aplicativo.

- -

Para ativar os alvos de compartilhamento direto, deve-se definir uma classe que estende a classe -{@code android.service.}
-{@code chooser.ChooserTargetService}. Declare o -{@code ChooserTargetService} no manifesto. Dentro desta declaração, especifique a permissão -{@code BIND_CHOOSER_TARGET_SERVICE} e um filtro de intenções na ação -{@code SERVICE_INTERFACE}.

-

O seguinte exemplo mostra como se deve declarar o {@code ChooserTargetService} no -manifesto.

-
-<service android:name=".ChooserTargetService"
-        android:label="@string/service_name"
-        android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
-    <intent-filter>
-        <action android:name="android.service.chooser.ChooserTargetService" />
-    </intent-filter>
-</service>
-
- -

Para cada atividade que quiser expor ao {@code ChooserTargetService}, adicione um elemento -{@code } com o nome -{@code "android.service.chooser.chooser_target_service"} no manifesto do aplicativo. -

- -
-<activity android:name=".MyShareActivity”
-        android:label="@string/share_activity_label">
-    <intent-filter>
-        <action android:name="android.intent.action.SEND" />
-    </intent-filter>
-<meta-data
-        android:name="android.service.chooser.chooser_target_service"
-        android:value=".ChooserTargetService" />
-</activity>
-
- -

Interações por voz

-

-Esta prévia fornece uma API de interação por voz que, junto com -ações de voz -, permite a criação de experiências por voz nos aplicativos. Chame o método -{@code android.app.Activity.isVoiceInteraction()} para determinar se a atividade -foi iniciada em resposta à ação de voz. Caso tenha sido iniciada, o aplicativo pode usar a classe -{@code android.app.VoiceInteractor} para solicitar uma confirmação de voz do usuário, -selecionar a partir de uma lista de opções e muito mais. Para aprender mais sobre a implementação de ações de voz, consulte -o site de desenvolvedor de ações de voz. -

- -

Auxiliar API

-

-Esta prévia oferece uma nova maneira de usuários se envolverem com os aplicativos com um assistente. Para usar este -recurso, o usuário deve possibilitar que o assistente use o contexto atual. Quando ativado, -o usuário pode invocar um assistente dentro de qualquer aplicativo mantendo o botão Iniciar pressionado.

-

O aplicativo pode optar por não compartilhar o contexto atual com o assistente configurando o sinalizador -{@link android.view.WindowManager.LayoutParams#FLAG_SECURE}. Além do conjunto -padrão de informações que a plataforma passa ao assistente, o aplicativo pode compartilhar -informações adicionais usando a nova classe {@code android.app.Activity.AssistContent}.

- -

Para fornecer ao assistente contexto adicional do aplicativo, siga estas etapas:

- -
    -
  1. Implemente a interface {@link android.app.Application.OnProvideAssistDataListener}.
  2. -
  3. Registre esta escuta usando -{@link android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()}.
  4. -
  5. Para fornecer informações contextuais específicas da atividade, substitua o retorno de chamada -{@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} - e, opcionalmente, o novo retorno de chamada {@code Activity.onProvideAssistContent()}. -
- -

Notificações

-

Esta prévia adiciona as seguintes alterações de API às notificações:

-
    -
  • Novo nível de filtro {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} que corresponde -ao modo "não perturbe" de despertadores apenas.
  • -
  • Novo valor da categoria {@code Notification.CATEGORY_REMINDER} que é usado para distinguir -lembretes de agendamentos do usuário de outros eventos ( -{@link android.app.Notification#CATEGORY_EVENT}) e despertadores ( -{@link android.app.Notification#CATEGORY_ALARM}).
  • -
  • Nova classe {@code android.graphics.drawable.Icon} que pode ser anexada às notificações -por meio dos métodos {@code Notification.Builder.setSmallIcon(Icon)} e -{@code Notification.Builder.setLargeIcon(Icon)}.
  • -
  • Novo método {@code NotificationManager.getActiveNotifications()} que permite que os aplicativos -descubram quais das notificações ainda estão ativas. Para ver uma implementação de aplicativo que usa este recurso, -consulte o exemplo de notificações ativas.
  • -
- -

Suporte para Bluetooth Stylus

-

Esta prévia fornece um suporte aprimorado para a entrada de usuário usando um Bluetooth Stylus. Os usuários podem -parear e conectar um Bluetooth Stylus compatível com o telefone ou tablet. Quando conectado, as informações -de posição da tela tátil são fundidas com as informações de botão e pressão do Stylus -para fornecer um alcance maior de expressão em comparação à tela tátil sozinha. O aplicativo pode escutar o pressionar -de botões do Stylus e realizar ações secundárias registrando os novos retornos de chamada -{@code View.onStylusButtonPressListener} e {@code GestureDetector.OnStylusButtonPressListener} - na atividade.

- -

Use as constantes e os métodos {@link android.view.MotionEvent} para detectar as interações -de botão do Stylus:

-
    -
  • Se o usuário toca no Stylus com um botão na tela do aplicativo, o método -{@link android.view.MotionEvent#getToolType(int) getTooltype()} retorna -{@link android.view.MotionEvent#TOOL_TYPE_STYLUS}.
  • -
  • Para aplicativos com M Preview, o método -{@link android.view.MotionEvent#getButtonState() getButtonState()} - retorna {@code MotionEvent.STYLUS_BUTTON_PRIMARY} quando o usuário -pressiona o botão principal do Stylus. Se o Stylus tiver um segundo botão, o mesmo método retorna -{@code MotionEvent.STYLUS_BUTTON_SECONDARY} quando o usuário o pressiona. Se o usuário pressiona -os dois botões simultaneamente, o método retorna os valores com OR juntos ( -{@code STYLUS_BUTTON_PRIMARY|STYLUS_BUTTON_SECONDARY}).
  • -
  • -Para aplicativos com uma versão de plataforma inferior , o método -{@link android.view.MotionEvent#getButtonState() getButtonState()} retorna -{@link android.view.MotionEvent#BUTTON_SECONDARY} (para o pressionar do botão principal do Stylus), -{@link android.view.MotionEvent#BUTTON_TERTIARY} (para o pressionar do botão secundário do Stylus) ou ambos. -
  • -
- -

Digitalização de baixa energia por Bluetooth aprimorada

-

-Se o aplicativo realizar digitalizações de baixa energia por Bluetooth, é possível usar o novo método -{@code android.bluetooth.le.ScanSettings.Builder.setCallbackType()} para especificar -que você quer que os retornos de chamada sejam notificados apenas quando um pacote de propaganda correspondente ao conjunto -{@link android.bluetooth.le.ScanFilter} for encontrado primeiro e quando -ele não for visto por um período. Esta abordagem de digitalização é mais eficiente -do que a fornecida na versão anterior da plataforma. -

- -

Suporte a Hotspot 2.0 Release 1

-

-Esta prévia adiciona suporte ao Hotspot 2.0 Release 1 nos dispositivos Nexus 6 e Nexus 9. Para fornecer -as credenciais de Hotspot 2.0 no aplicativo, use os novos métodos da classe -{@link android.net.wifi.WifiEnterpriseConfig}, como {@code setPlmn()} e -{@code setRealm()}. No objeto {@link android.net.wifi.WifiConfiguration}, é possível definir os campos -{@link android.net.wifi.WifiConfiguration#FQDN} e {@code providerFriendlyName}. -A nova propriedade {@code ScanResult.PasspointNetwork} indica se uma rede detectada representa -um ponto de acesso Hotspot 2.0. -

- -

Modo de exibição 4K

-

A plataforma agora permite que aplicativos solicitem que a resolução seja aprimorada para renderização 4K -em hardware compatível. Para consultar a resolução física atual, use as novas APIs -{@code android.view.Display.Mode}. Se a IU for desenhada em uma resolução lógica -menor e for redimensionada para uma resolução física maior, saiba que a resolução física que o método -{@code Display.Mode.getPhysicalWidth()} retorna pode ser diferente da resolução -física informada por {@link android.view.Display#getSize(android.graphics.Point) getSize()}.

- -

É possível solicitar que o sistema altere a resolução física no aplicativo à medida que ele é executado configurando -a propriedade {@code WindowManager.LayoutParams.preferredDisplayModeId} da janela do aplicativo. Este -recurso é útil se quiser alternar para a resolução de exibição 4K. Enquanto estiver no modo de exibição 4K, -a IU continua a ser renderizada na resolução original (como 1080 p) e é escalonada para 4K, mas os objetos -{@link android.view.SurfaceView} podem exibir o conteúdo na resolução nativa.

- -

ColorStateLists com tema

-

Os atributos de tema agora são suportados no -{@link android.content.res.ColorStateList} para dispositivos que executam o M Preview. Os métodos -{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} e -{@link android.content.res.Resources#getColor(int) getColor()} ficaram obsoletos. Caso esteja chamando -essas APIs, chame os novos métodos {@code Context.getColorStateList()} ou -{@code Context.getColor()}. Esses métodos também estão disponíveis na biblioteca -v4 appcompat via {@link android.support.v4.content.ContextCompat}.

- -

Recursos de áudio

- -

Esta prévia adiciona aprimoramentos ao processamento de áudio no Android, incluindo:

-
    -
  • Suporte para protocolo MIDI -, com as novas APIs {@code android.media.midi}. Use essas APIs para enviar e receber eventos -de MIDI.
  • -
  • Use as novas classes {@code android.media.AudioRecord.Builder} e {@code android.media.AudioTrack.Builder} - para criar captura de áudio digital e objetos de reprodução, respectivamente, -e configure a fonte de áudio e as propriedades do coletor para substituir os padrões do sistema.
  • -
  • Ganchos de API para associação de dispositivos de entrada e áudio. Isto é particularmente útil se o aplicativo -permite que os usuários iniciem uma pesquisa de voz a partir de um controle de jogo ou controle remoto para Android -TV. O sistema invoca o novo retorno de chamada {@code android.app.Activity.onSearchRequested()} quando -o usuário inicia uma pesquisa. Para determinar se o dispositivo de entrada do usuário tem um microfone integrado, recupere -o objeto {@link android.view.InputDevice} deste retorno de chamada e, em seguida, chame o novo método -{@code InputDevice.hasMic()}.
  • -
  • Novas classes {@code android.media.AudioDevicesManager} que permitem que você -recupere uma lista completa de todas as fontes e dispositivos de áudio do coletor anexados. Também é possível especificar um objeto -{@code android.media.OnAudioDeviceConnectionListener} caso queira que o aplicativo seja notificado -quando um dispositivo de áudio for conectado ou desconectado.
  • -
- -

Recursos de vídeo

-

Esta prévia adiciona novas capacidades às APIs de processamento de vídeo, incluindo:

-
    -
  • A nova classe {@code android.media.MediaSync} que ajuda os aplicativos a renderizar transmissões -de vídeo e áudio sincronizadamente. Os buffers de áudio são enviados sem bloqueio e retornados -por um retorno de chamada. Eles também suportam taxa de reprodução dinâmica. -
  • -
  • O novo evento {@code MediaDrm.EVENT_SESSION_RECLAIMED}, que indica que uma sessão aberta pelo aplicativo -foi recuperada pelo gerenciador de recursos. Se o aplicativo usa sessões de DRM, deve-se -lidar com este evento e garantir que uma sessão recuperada não seja usada. -
  • -
  • O novo código de erro {@code MediaCodec.CodecException.ERROR_RECLAIMED}, que indica que o gerenciador -de recurso recuperou o recurso de mídia usado pelo codec. Com esta exceção, o codec -deve ser liberado, como se fosse movido para o estado terminal. -
  • -
  • A nova interface {@code MediaCodecInfo.CodecCapabilities.getMaxSupportedInstances()} para obter -uma dica do número máximo suportado de instâncias de codec concorrentes. -
  • -
  • O novo método {@code MediaPlayer.setPlaybackParams()} para definir a taxa de reprodução de mídia -para reprodução de movimento lento ou rápido. Ele também estica ou acelera a reprodução de áudio automaticamente -em conjunto com o vídeo.
  • -
- -

Recursos de câmera

-

Esta prévia inclui as seguintes novas APIs para acessar a lanterna da câmera -e para o reprocessamento da câmera de imagens:

- -

API da lanterna

-

Se um dispositivo de câmera tem uma unidade de flash, é possível chamar o método {@code CameraManager.setTorchMode()} -para ligar ou desligar o modo de tocha da unidade de flash sem abrir o dispositivo da câmera. O aplicativo -não tem propriedade exclusiva da unidade de flash ou do dispositivo de câmera. O modo de tocha é desativado -e torna-se indisponível sempre que o dispositivo de câmera estiver indisponível ou quando outros -recursos de câmera que mantêm a tocha ativada ficam indisponíveis. Outros aplicativos também podem chamar {@code setTorchMode()} -para desativar o modo de tocha. Quando o aplicativo que ativou o modo -de tocha for fechado, o modo é desativado.

- -

É possível registrar um retorno de chamada para ser notificado sobre o estado da tocha chamando o método -{@code CameraManager.registerTorchCallback()}. Na primeira vez que o retorno de chamada é registrado, -ele é imediatamente chamado com o estado do modo de tocha de todos os dispositivos de câmera conhecidos com -uma unidade de flash. Se o modo de tocha é ativado ou desativado, o método -{@code CameraManager.TorchCallback.onTorchModeChanged()} é invocado.

- -

API de reprocessamento

-

A API {@link android.hardware.camera2 Camera2} é estendida para suportar YUV e reprocessamento -de imagem de formato opaco privado. O aplicativo determina se as capacidades de reprocessamento estão disponíveis -via {@code CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES}. Se um dispositivo suporta o reprocessamento, -é possível criar uma sessão de captura de câmera de reprocessamento chamando -{@code CameraDevice.createReprocessableCaptureSession()} e criando solicitações para o reprocessamento -do buffer de entrada.

- -

Use a classe {@code ImageWriter} para conectar o fluxo de buffer de entrada à entrada de reprocessamento -da câmera. Para obter um buffer vazio, siga este modelo de programação:

- -
    -
  1. Chame o método {@code ImageWriter.dequeueInputImage()}.
  2. -
  3. Preencha os dados no buffer de entrada.
  4. -
  5. Envie o buffer à câmera chamando o método {@code ImageWriter.queueInputImage()}.
  6. -
- -

Caso esteja usando um objeto {@code ImageWriter} junto com uma imagem -{@code android.graphics.ImageFormat.PRIVATE}, o aplicativo não poderá acessar os dados -da imagem diretamente. Em vez disso, passe a imagem {@code ImageFormat.PRIVATE} diretamente ao -{@code ImageWriter} chamando o método {@code ImageWriter.queueInputImage()} sem nenhuma -cópia de buffer.

- -

A classe {@code ImageReader} agora suporta as transmissões de imagem -de formato {@code android.graphics.ImageFormat.PRIVATE}. Este suporte permite que o aplicativo mantenha uma fila de imagem circular de imagens de saída -{@code ImageReader}, selecione uma ou mais imagens e envie-as para -{@code ImageWriter} para o reprocessamento de câmera.

- -

Recursos do Android for Work

-

Esta prévia inclui as seguintes novas APIs para Android for Work:

-
    -
  • Controles aprimorados para dispositivos de uso único e pertencentes a empresas: O dono do dispositivo -pode controlar as seguintes configurações para aprimorar -os dispositivos de uso único pertencentes a empresas (COSU): -
      -
    • Desativar ou reativar a proteção de bloqueio com o método -{@code DevicePolicyManager.setKeyguardEnabledState()}.
    • -
    • Desativar ou reativar a barra de status (incluindo configurações rápidas, -notificações e o gesto de arrastar para cima que inicia o Google Now) com o método -{@code DevicePolicyManager.setStatusBarEnabledState()}.
    • -
    • Desativar ou reativar inicialização segura com a constante {@link android.os.UserManager} -{@code DISALLOW_SAFE_BOOT}.
    • -
    • Evitar que a tela desligue quando conectada com a constante -{@link android.provider.Settings.Global} {@code STAY_ON_WHILE_PLUGGED_IN}.
    • -
    -
  • -
  • Instalação e desinstalação silenciosa de aplicativos pelo dono do dispositivo: O dono do dispositivo pode agora -instalar e desinstalar aplicativos silenciosamente usando as APIs {@link android.content.pm.PackageInstaller}, -independente do Google Play for Work. É possível tomar providências para os dispositivos -por meio de um dono do dispositivo que recupera e instala aplicativos se a interação de usuário. Este recurso é útil para ativar a provisão de um toque -de telefones públicos ou de outros dispositivos sem a ativação da conta Google.
  • -
  • Acesso silencioso de certificado empresarial: Quando um aplicativo chama -{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()} -, antes do usuário receber a solicitação para selecionar um certificado, o perfil ou dono do dispositivo -pode chamar o método {@code DeviceAdminReceiver.onChoosePrivateKeyAlias()} para fornecer -o alias silenciosamente ao aplicativo da solicitação. Este recurso permite que você forneça acesso de aplicativos gerenciados para certificados -sem a interação de usuário.
  • -
  • Aceitação automática de atualizações do sistema. Ao configurar a política de atualização do sistema com -{@code DevicePolicyManager.setSystemUpdatePolicy()}, o dono do dispositivo pode aceitar automaticamente a atualização -do sistema, no caso de um dispositivo público, ou adiar a atualização e evitar -que ela seja feita pelo usuário por até 30 dias. Além disso, um administrador pode definir uma janela de tempo diária -em que uma atualização deve ser realizada. Por exemplo: durante os momentos em que um dispositivo público não está em uso. Quando -uma atualização do sistema está disponível, o sistema verifica se o aplicativo de controlador de política de trabalho definiu uma política de atualização -do sistema e, portanto, comporta-se de acordo com a definição. -
  • -
  • -Instalação de certificado delegado: Um perfil ou dono do dispositivo pode agora fornecer ao aplicativo -de terceiros a habilidade de chamar essas APIs de gerenciamento -de certificado de {@link android.app.admin.DevicePolicyManager}: -
      -
    • {@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName) -getInstalledCaCerts()}
    • -
    • {@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[]) -hasCaCertInstalled()}
    • -
    • {@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[]) -installCaCert()}
    • -
    • {@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[]) -uninstallCaCert()}
    • -
    • {@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName) -uninstallAllUserCaCerts()}
    • -
    • {@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String) -installKeyPair()}
    • -
    -
  • -
  • Proteção de redefinição de fábrica da empresa: Ao preparar um dono do dispositivo, agora é possível -configurar parâmetros para desbloquear a proteção de redefinição de fábrica (FRP) configurando o pacote -{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS}. Um aplicativo de programador -NFC pode fornecer estes parâmetros depois que um dispositivo for redefinido para desbloquear o FRP e preparar o dispositivo, -sem a necessidade da conta Google anteriormente configurada. Caso não modifique esses parâmetros, -o FRP permanece no local e evita que o dispositivo seja ativado sem as credenciais -Google anteriormente ativadas. -

    Além disso, ao configurar as restrições do aplicativo nos serviços Google, o dono do dispositivo pode especificar contas -Google alternativas para desbloquear o FRP para substituir as contas ativadas no dispositivo.

    -
  • - -
  • Rastreamento de uso de dados. Um perfil ou dono do dispositivo pode agora -consultar as estatísticas de uso de dados visíveis em Configurações > Uso de dados usando os novos métodos -{@code android.app.usage.NetworkStatsManager}. Os donos de perfis recebem automaticamente -a permissão para consultar os dados no perfil que gerenciam, enquanto que os donos do dispositivo têm acesso aos dados -de uso do usuário principal gerenciado.
  • -
  • Gerenciamento de permissão de tempo de execução: -

    Um perfil ou dono do dispositivo pode definir uma política -de permissão para todas as solicitações do tempo de execução de todos os aplicativos que usam -{@code DevicePolicyManager.setPermissionPolicy()}, seja para solicitar ao usuário para conceder -a permissão como normal ou automaticamente conceder ou negar a permissão silenciosamente. Se a política posterior -for definida, o usuário não poderá modificar a seleção feita pelo perfil ou dono do dispositivo dentro da tela de permissões -do aplicativo em configurações.

  • -
  • VPN em configurações: Os aplicativos de VPN agora estão visíveis em -Configurações > Mais > VPN. -Além disso, as notificações que acompanham o uso de VPN são específicas sobre como essa VPN -é configurada. Para o dono de um perfil, as notificações são específicas para determinar -se a VPN é configurada para um perfil gerenciado, um perfil pessoal ou ambos. Para o dono do dispositivo, as notificações -são específicas para determinar se a VPN é configurada para todo o dispositivo.
  • -
  • Notificação de estado de trabalho: Um ícone de pasta da barra de status agora aparece -sempre que um aplicativo do perfil gerenciado tiver uma atividade no primeiro plano. Além disso, se o usuário é desbloqueado -diretamente para a atividade de um aplicativo no perfil gerenciado, um aviso é exibido notificando -ao usuário que ele está dentro do perfil de trabalho. -
  • -
- -

- Para obter uma vista detalhada de todas as alterações de API no M Developer Preview, consulte o relatório de diferenças de API. -

diff --git a/docs/html-intl/intl/pt-br/preview/backup/index.jd b/docs/html-intl/intl/pt-br/preview/backup/index.jd deleted file mode 100644 index 1ba039bc0f617c19d25eed72804b5f4b95764a94..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/backup/index.jd +++ /dev/null @@ -1,327 +0,0 @@ -page.title=Backup automático para aplicativos -page.tags=backup, previewresources, androidm -page.keywords=backup, autobackup, preview -page.image=images/cards/card-auto-backup_2x.png -@jd:body - - - -

- Frenquentemente, os usuários investem muito tempo e esforço para criar dados e configurar preferências - nos aplicativos. Preservar esses dados para os usuários caso substituam um dispositivo quebrado ou atualizem-se para um novo - é importante para garantir uma ótima experiência de usuário. Dispositivos que executam o sistema Android M Preview - ajudam a garantir uma boa experiência para os usuários nessas circunstâncias realizando o backup dos dados do aplicativo - automaticamente no Google Drive. Os dados do aplicativo são restaurados automaticamente se um usuário alterar ou atualizar um - dispositivo. -

- -

- Os backups automáticos estão ativos para todos os aplicativos instalados nos dispositivos que executam o Android M Preview. Nenhum - código adicional de aplicativo é necessário. O sistema fornece aos usuários a habilidade de decisão - sobre os backups automáticos de dados. Também é possível limitar quais dados do aplicativo devem ter o backup. -

- -

- Este documento descreve o novo comportamento do sistema e como especificar quais dados terão backup - para o aplicativo. -

- -

Visão geral

- -

- O recurso de backup automático preserva os dados que o aplicativo cria em um dispositivo de usuário enviando-os - à conta do Google Drive do usuário e criptografando-os. Não há cobranças para você ou para o usuário - em relação ao armazenamento de dados e os dados salvos não contam para a cota do Drive pessoal do usuário. Durante - o período do M Preview, os usuários podem armazenar até 25 MB por aplicativo do Android. -

- -

- Os backups automáticos ocorrem a cada 24 horas, quando o dispositivo está ocioso, carregando e conectado - a uma rede Wi-Fi. Quando esses requisitos são atendidos, o serviço Backup Manager envia todos os dados de backup - disponíveis à nuvem. Quando um usuário transita para um novo dispositivo, ou desinstala e reinstala - o aplicativo com backup realizado, uma operação de restauração copia os dados de backup para o diretório de dados - do aplicativo recém-instalado. -

- -

- Observação: se o usuário usar o - serviço de Backup do Android de legado, este novo comportamento - não se aplicará aos trabalhos de comportamento de backup existentes com o normal. -

- - -

Arquivos de dados excluídos automaticamente

- -

- Nem todos os dados do aplicativo devem ter backup, como arquivos temporários e de armazenamento em cachê. Portanto, o serviço de backup automático - exclui determinados arquivos de dados por padrão: -

- -
    -
  • Arquivos em diretórios identificados pelos métodos {@link android.content.Context#getCacheDir - getCacheDir()} e {@link android.content.ContextWrapper#getCodeCacheDir getCodeCacheDir()} -. -
  • - -
  • Arquivos localizados no armazenamento externo, a não ser que estejam no diretório identificado pelo método - {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} -. -
  • - -
  • Arquivos localizados no diretório identificado pelo método - {@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()}. -
  • -
- -

Configurar backup de dados

- -

- Os dados criados por qualquer aplicativo instalado em um dispositivo M Preview têm backup, exceto - os arquivos excluídos automaticamente listados na seção anterior. É possível limitar e configurar - quais dados terão backup no seu aplicativo usando as configurações no manifesto do aplicativo. -

- -

Incluir ou excluir dados

- -

- Dependendo de quais dados o aplicativo precisar e do modo que forem salvos, você precisará definir - regras específicas para incluir ou excluir determinados arquivos ou diretórios. O serviço de backup automático suporta - a configuração dessas regras de backup por meio do uso de um arquivo de configuração XML e do - manifesto do aplicativo. No manifesto do aplicativo, é possível especificar o arquivo de configuração de esquema de backup como exibido - no seguinte exemplo: -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample">
-    <uses-sdk android:minSdkVersion="MNC"/>
-    <uses-sdk android:targetSdkVersion="MNC"/>
-    <app ...
-        android:fullBackupContent="@xml/mybackupscheme">
-    </app>
-    ...
-</manifest>
-
- -

- Neste código de exemplo, o atributo android:fullBackupContent especifica um arquivo XML, - localizado no diretório res/xml/ do projeto de desenvolvimento do aplicativo, chamado - mybackupscheme.xml. Este arquivo de configuração inclui as regras de quais arquivos terão - backup. O seguinte código de exemplo mostra um arquivo de configuração que exclui um arquivo específico - dos backups: -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<full-backup-content>
-    <exclude domain="database" path="device_info.db"/>
-</full-backup-content>
-
- -

- Esta configuração de backup de exemplo exclui do backup somente um arquivo específico do banco de dados. - Todos os outros arquivos terão backup. -

- -

Sintaxe da configuração de backup

- -

- A configuração do serviço de backup permite que você especifique quais arquivos incluir ou excluir do -backup. A sintaxe para o arquivo XML de configuração de backup de dados é a seguinte: -

- -
-<full-backup-content>
-    <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
-    <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
-</full-backup-content>
-
- -

- Os seguintes elementos e atributos permitem que você especifique os arquivos que serão incluídos ou excluídos - do backup: -

- -
    -
  • - <include>. Use este elemento se quiser especificar um conjunto de recursos -para o backup, em vez de fazer o sistema realizar o backup de todos os dados no aplicativo por padrão. Ao especificar - uma tag <include>, o sistema realiza apenas o backup dos recursos especificados - com este elemento. -
  • - -
  • - <exclude>. Use este elemento para especificar um conjunto de recursos que será excluído - do backup. O sistema realiza o backup de todos os dados no aplicativo, exceto os recursos especificados - com este elemento. -
  • - -
  • - domain. O tipo de recurso que deseja excluir ou incluir no backup. Os valores válidos - que podem ser especificados para este atributo incluem: -
  • - -
  • -
      -
    • - root. Especifica que o recurso está no diretório raiz do aplicativo. -
    • - -
    • - file. Corresponde ao recurso no diretório retornado - pelo método {@link android.content.Context#getFilesDir getFilesDir()}. -
    • - -
    • - database. Corresponde ao banco de dados retornado - pelo método {@link android.content.Context#getDatabasePath getDatabasePath()} ou usando - a classe {@link android.database.sqlite.SQLiteOpenHelper}. -
    • - -
    • - sharedpref. Corresponde a um objeto {@link android.content.SharedPreferences} - retornado pelo método {@link android.content.Context#getSharedPreferences getSharedPreferences()} -. -
    • - -
    • - external. Especifica que o recurso está no armazenamento externo e corresponde - a um arquivo no diretório retornado pelo método - {@link android.content.Context#getExternalFilesDir getExternalFilesDir()}. -
    • - -
    • - path. O caminho de arquivo para um recurso que deseja excluir ou incluir - no backup. -
    • -
    -
  • -
- - -

Proibir backup de dados

- -

- É possível optar por evitar backups automáticos de quaisquer dados do aplicativo configurando - o atributo android:allowBackup para false no elemento do aplicativo - do manifesto. Esta configuração é ilustrada no seguinte código de exemplo: -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample">
-    <uses-sdk android:minSdkVersion="MNC"/>
-    <uses-sdk android:targetSdkVersion="MNC"/>
-    <app ...
-        android:allowBackup="false">
-    </app>
-    ...
-</manifest>
-
- - -

Testar configuração de backup

- -

- Ao criar uma configuração de backup, deve-se testá-la para garantir que o aplicativo - salva os dados e que eles podem ser restaurados corretamente. -

- - -

Ativar registro de backup

- -

- Para ajudar a determinar como o recurso de backup analisa o arquivo XML, ative o registro - antes de realizar um backup de teste: -

- -
-$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE
-
- -

Teste de backup

- -

Para executar um backup manualmente, primeiro deve-se inicializar o Backup Manager chamando o seguinte - comando: -

- -
-$ adb shell bmgr run
-
- -

- Em seguida, realiza-se o backup manualmente do aplicativo usando o seguinte comando, especificando o nome - do pacote para o aplicativo como o parâmetro <PACKAGE>: -

- -
-$ adb shell bmgr fullbackup <PACKAGE>
- - -

Teste de restauração

- -

- Para iniciar manualmente uma restauração após o backup dos dados do aplicativo, chame o seguinte comando, - especificando o nome do pacote para o aplicativo como o parâmetro <PACKAGE>: -

- -
-$ adb shell bmgr restore <PACKAGE>
-
- -

- Aviso: esta ação impede o aplicativo de apagar os dados antes de realizar - a operação de restauração. -

- -

- Para iniciar o processo de restauração do aplicativo, deve-se desinstalar e reinstalá-lo. Os dados - do aplicativo são restaurados automaticamente a partir da nuvem quando a instalação do aplicativo for concluída. -

- - -

Resolução de problemas de backups

- -

- Caso ocorra problemas, é possível apagar os dados de backup e os metadados associados desativando o backup - e reativando-o em Settings (Configurações) > Backup, redefinindo o dispositivo para as especificações de fábrica, ou - chamando este comando: -

- -
$ adb shell bmgr wipe <TRANSPORT> <PACKAGE>
- -

- O valor <TRANSPORT> deve ser prefixado por com.google.android.gms. - Para obter uma lista de transportes, chame o seguinte comando: -

- -
$ adb shell bmgr list transports
- -

Problemas conhecidos

- -

A seguir estão os problemas conhecidos com o serviço de backup automático:

- -
    -
  • Google Cloud Messaging - - Para aplicativos que usam o Google Cloud Messaging para notificações push, há um problema conhecido - onde o backup do ID de registro retornado pelo registro do Google Cloud Messaging pode - desencadear notificações push para o aplicativo restaurado. É importante consultar a API para obter um novo ID de registro - após a instalação em um novo dispositivo, o que não deve ser o caso se o ID de registro antigo - tiver backup. Para evitar isto, exclua o ID de registro do conjunto de arquivos - de backup. -
  • -
diff --git a/docs/html-intl/intl/pt-br/preview/behavior-changes.jd b/docs/html-intl/intl/pt-br/preview/behavior-changes.jd deleted file mode 100644 index cd99bbd449e33c8a439cb7c257a1293aef92e710..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/behavior-changes.jd +++ /dev/null @@ -1,402 +0,0 @@ -page.title=Mudanças de comportamento -page.keywords=preview,sdk,compatibility -sdk.platform.apiLevel=MNC -@jd:body - - - -

Junto com novas capacidades e recursos, o M Developer Preview inclui uma variedade -de mudanças do sistema e alterações no comportamento da API. Este documento destaca algumas -das alterações principais que você deve entender e levar em consideração nos aplicativos.

- -

Caso tenha publicado anteriormente um aplicativo para Android, saiba que ele pode ser afetado - pelas alterações na plataforma.

- -

Permissões do tempo de execução

-

Esta prévia introduz um novo modelo de permissões em que os usuários podem gerenciar diretamente - as permissões do aplicativo no tempo de execução. Este modelo fornece aos usuários uma visibilidade aprimorada e controle sobre permissões, - ao mesmo tempo em que agiliza os processos de atualização automática e instalação para os desenvolvedores de aplicativos. -Os usuários podem conceder ou revogar as permissões individualmente para os aplicativos instalados.

- -

Nos aplicativos direcionados para o M Preview, certifique-se de verificar e solicitar as permissões - no tempo de execução. Para determinar se o aplicativo recebeu uma permissão, chame - o novo método {@code Context.checkSelfPermission()}. Para solicitar uma permissão, chame o novo método - {@code Activity.requestPermission()}. Mesmo se o aplicativo não é direcionado para o M, - deve-se testá-lo sob o novo modelo de permissões.

- -

Para obter mais detalhes sobre o suporte do novo modelo de permissões no aplicativo, consulte a página de prévia de desenvolvedor - -Permissões. Para obter dicas sobre como avaliar o impacto no aplicativo, - consulte o guia de teste.

- -

Otimizações de economia de energia

-

Esta prévia introduz novas otimizações de economia de energia para dispositivos e aplicativos ociosos.

- -

Soneca

-

Se o dispositivo estiver desconectado e parado com a tela desligada por um período, - o modo Soneca será ativado, onde ele tentará manter o sistema em um estado ocioso. Neste modo, - os dispositivos retomam as operações normais periodicamente por breves períodos para que a sincronização de aplicativos - possa ocorrer e para que o sistema possa realizar quaisquer operações pendentes.

- -

As seguintes restrições se aplicam aos aplicativos durante a Soneca:

-
    -
  • O acesso à rede é desativado, a não ser que o aplicativo receba um convite de alta prioridade - do Google Cloud Messaging.
  • -
  • Bloqueios de soneca são ignorados.
  • -
  • Os despertadores agendados com a classe {@link android.app.AlarmManager} são ignorados, - exceto os alarmes definidos com o método {@link android.app.AlarmManager#setAlarmClock setAlarmClock()} - e {@code AlarmManager.setAndAllowWhileIdle()}.
  • -
  • As verificações de Wi-Fi não são realizadas.
  • -
  • Sincronizações e trabalhos para os adaptadores de sincronização e {@link android.app.job.JobScheduler} - não têm permissão para serem executados.
  • -
-

-

Quando o dispositivo sair do modo soneca, quaisquer sincronizações e trabalhos pendentes são executados.

-

É possível testar este recurso conectando o dispositivo executando o M Preview - à máquina de desenvolvimento e chamando os seguintes comandos: -

-
-$ adb shell dumpsys battery unplug
-$ adb shell dumpsys deviceidle step
-$ adb shell dumpsys deviceidle -h
-
-

Observação: o próximo lançamento do - -Google Cloud Messaging permite que você designe - mensagens de alta prioridade. Se o aplicativo recebe mensagens de alta prioridade do GCM, - um acesso breve à rede é concedido mesmo quando o dispositivo está no modo soneca. -

- -

Consulte o -guia de teste para obter dicas sobre -como testar a soneca nos aplicativos.

- -

Aplicativo em espera

-

Com esta prévia, o sistema pode determinar quais aplicativos estão em espera quando -não estão em uso ativo. O aplicativo é considerado em espera após um período, a não ser que o sistema detecte -algum destes sinais:

- -
    -
  • O aplicativo foi explicitamente iniciado pelo usuário.
  • -
  • O aplicativo tem um processo atualmente em primeiro plano (seja uma atividade ou serviço de primeiro plano - ou esteja em uso por outra atividade ou serviço de primeiro plano).
  • -
  • O aplicativo gera uma notificação que o usuário vê na tela de bloqueio - ou na bandeja de notificações.
  • -
  • O usuário explicitamente pede para que o aplicativo seja liberado das otimizações, - ajustado em Settings (Configurações).
  • -
- -

Se o dispositivo estiver desconectado, o aplicativo considerado ocioso terá o acesso -à rede desativado e as sincronizações e os trabalhos suspensos. Quando o dispositivo está conectado em uma fonte de alimentação, - esses aplicativos têm acesso à rede permitido e podem executar quaisquer sincronizações e trabalhos pendentes. Se o dispositivo permanece ocioso por longos períodos, - os aplicativos ociosos têm acesso à rede permitido aproximadamente uma vez por dia.

- -

É possível testar este recurso conectando o dispositivo executando o M Preview - à máquina de desenvolvimento e chamando os seguintes comandos: -

-
-$ adb shell dumpsys battery unplug
-$ adb shell am set-idle <packageName> true
-$ adb shell am set-idle <packageName> false
-$ adb shell am get-idle <packageName>
-
- -

Observação: o próximo lançamento do - -Google Cloud Messaging (GCM) permite que você -designe mensagens de alta prioridade. Se o aplicativo recebe mensagens de alta prioridade do GCM, -um acesso breve à rede é concedido mesmo quando o aplicativo está ocioso. -

- -

Consulte o -guia de teste para obter dicas sobre como -testar a espera dos aplicativos.

- -

Dispositivos de armazenamento adotáveis

-

-Com esta prévia, os usuários podem adotar dispositivos de armazenamento externo como cartões SD. Adotar um dispositivo -de armazenamento externo criptografa e formata o dispositivo para agir como um armazenamento interno. Este recurso -permite que os usuários movam aplicativos e dados privados desses aplicativos entre dispositivos de armazenamento. Ao mover aplicativos, -o sistema respeita a preferência -{@code android:installLocation} -no manifesto.

- -

Se o aplicativo acessar as seguintes APIs ou campos, saiba que os caminhos de arquivos retornados -serão alterados dinamicamente quando o aplicativo for movido entre os dispositivos de armazenamento externo e interno. -Ao compilar caminhos de arquivos, é recomendado que essas APIs sempre sejam chamadas dinamicamente. -Não use caminhos de arquivo criptografados nem persista em caminhos de arquivo completamente qualificados que foram compilados anteriormente.

- -
    -
  • Métodos {@link android.content.Context}: -
      -
    • {@link android.content.Context#getFilesDir() getFilesDir()}
    • -
    • {@link android.content.Context#getCacheDir() getCacheDir()}
    • -
    • {@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}
    • -
    • {@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}
    • -
    • {@link android.content.Context#getDir(java.lang.String,int) getDir()}
    • -
    • {@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}
    • -
    • {@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}
    • -
    • {@link android.content.Context#getPackageCodePath() getPackageCodePath()}
    • -
    • {@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}
    • -
    -
  • -
  • Campos {@link android.content.pm.ApplicationInfo}: -
      -
    • {@link android.content.pm.ApplicationInfo#dataDir dataDir}
    • -
    • {@link android.content.pm.ApplicationInfo#sourceDir sourceDir}
    • -
    • {@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}
    • -
    • {@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}
    • -
    • {@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}
    • -
    • {@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}
    • -
    -
  • -
- -

Para depurar este recurso na prévia de desenvolvedor, é possível ativar a adoção -de uma unidade USB que está conectada a um dispositivo Android por meio de um cabo USB On-The-Go (OTG) executando este comando:

- -
-$ adb shell sm set-force-adoptable true
-
- -

Remoção do cliente Apache HTTP

-

Esta prévia remove o suporte para o cliente Apache HTTP. Se o aplicativo estiver usando este cliente e for direcionado -para Android 2.3 (nível da API 9) ou mais recente, use -a classe {@link java.net.HttpURLConnection}. Esta API é mais eficiente, pois reduz o uso de rede por meio de compressão transparente e armazenamento -em cachê de respostas, além de minimizar o consumo de energia. Para continuar usando as APIs do Apache HTTP, -deve-se primeiro declarar a dependência de tempo de compilação no arquivo {@code build.gradle}: -

-
-android {
-    useLibrary 'org.apache.http.legacy'
-}
-
-

O Android está mudando da biblioteca OpenSSL para -BoringSSL -. Caso esteja usando o Android NDK no aplicativo, não vincule contra bibliotecas criptográficas -que não fazem parte da API de NDK, como {@code libcrypto.so} e {@code libssl.so}. Estas bibliotecas não são APIs públicas -e podem mudar ou apresentar erros sem notificar entre liberações e dispositivos. -Além disso, você pode se expor a vulnerabilidades de segurança. Em vez disso, -modifique o código nativo para chamar as APIs de criptografia Java via JNI ou para vincular estaticamente -com relação a uma biblioteca criptográfica de sua escolha.

- -

Mudanças no AudioManager

-

Ajustar o volume diretamente ou desativar o áudio de transmissões específicas por meio da classe {@link android.media.AudioManager} -não são mais recursos suportados. O método {@link android.media.AudioManager#setStreamSolo(int,boolean) -setStreamSolo()} é obsoleto e deve-se chamar o método -{@code AudioManager.requestAudioFocus()}. De forma semelhante, o método -{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()} é -obsoleto; em vez disso, chame o método {@code AudioManager.adjustStreamVolume()} -e passe o valor da direção de {@code ADJUST_MUTE} ou {@code ADJUST_UNMUTE}.

- -

Seleção de texto

- - - -

Quando os usuários selecionam o texto no aplicativo, agora é possível exibir ações de seleção de texto como -Recortar, Copiar e Colar na -barra de ferramentas flutuante. A implementação da interação do usuário é semelhante ao processo -da barra de ação contextual, como descrito em - -Ativação do modo de ação contextual para vistas individuais.

- -

Para implementar uma barra de ferramentas flutuante para seleção de texto, faça as seguintes alterações nos aplicativos -existentes:

-
    -
  1. No objeto {@link android.view.View} ou {@link android.app.Activity}, altere as chamadas -{@link android.view.ActionMode} de -{@code startActionMode(Callback)} para {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}.
  2. -
  3. Pegue a implementação existente de {@code ActionMode.Callback} e torne-a uma extensão de -{@code ActionMode.Callback2}.
  4. -
  5. Substitua o método {@code Callback2.onGetContentRect()} para fornecer as coordenadas do conteúdo -do objeto {@link android.graphics.Rect} (como um retângulo de seleção de texto) na vista.
  6. -
  7. Se o posicionamento do retângulo deixar de ser válido e for o único elemento a ser invalidado, -chame o método {@code ActionMode.invalidateContentRect()}.
  8. -
- -

Caso esteja usando a -biblioteca de suporte Android revisão 22.2, saiba que as barras de ferramentas flutuantes não -têm compatibilidade com versões anteriores e que o appcompat tem controle sobre os objetos {@link android.view.ActionMode} por -padrão. Isto evita que barras de ferramentas flutuantes sejam exibidas. Para ativar o suporte de -{@link android.view.ActionMode} em um -{@link android.support.v7.app.AppCompatActivity}, chame -{@code android.support.v7.app.AppCompatActivity.getDelegate()} e, em seguida, chame -{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()} no objeto -{@link android.support.v7.app.AppCompatDelegate} retornado e defina -o parâmetro de entrada para {@code false}. Esta chamada retorna o controle dos objetos {@link android.view.ActionMode} -à estrutura. Em dispositivos que são executados no M Preview, isto permite que a estrutura suporte os modos de -{@link android.support.v7.app.ActionBar} ou de barra de ferramenta flutuante, enquanto que, para dispositivos anteriores ao M Preview, -somente os modos {@link android.support.v7.app.ActionBar} são suportados.

- -

Mudanças no Android Keystore

-

Com esta prévia, -o provedor Android Keystore não suporta mais -DSA. ECDSA ainda é suportado.

- -

As chaves que não exigem criptografia em rest não precisam ser excluídas quando a tela de bloqueio segura -é desativada ou redefinida (por exemplo, pelo usuário ou por um administrador do dispositivo). As chaves que exigem -criptografia serão excluídas durante esses eventos.

- -

Mudanças de rede e Wi-Fi

- -

Esta prévia introduz as seguintes alterações de comportamento nas APIs de rede e Wi-Fi.

-
    -
  • Os aplicativos podem alterar o estado dos objetos {@link android.net.wifi.WifiConfiguration} -somente se você os tiver criado. Você não tem permissão para modificar nem excluir objetos -{@link android.net.wifi.WifiConfiguration} criados pelo usuário ou outros aplicativos. -
  • -
  • -Anteriormente, se um aplicativo forçasse o dispositivo a se conectar a uma rede Wi-Fi específica usando -{@link android.net.wifi.WifiManager#enableNetwork(int,boolean) enableNetwork()} com a configuração -{@code disableAllOthers=true}, o dispositivo desconectava de outras redes, -como dados de celular. Nesta prévia, o dispositivo não rompe a conexão com outras redes. Se -o {@code targetSdkVersion} do aplicativo for {@code “20”} ou menor, ele é fixado -à rede Wi-Fi selecionada. Se o {@code targetSdkVersion} do aplicativo for {@code “21”} ou maior, use -as APIS de multi-rede (como -{@link android.net.Network#openConnection(java.net.URL) openConnection()}, -{@link android.net.Network#bindSocket(java.net.Socket) bindSocket()} e o novo método -{@code ConnectivityManager.bindProcessToNetwork()}) para garantir que o tráfego de rede -seja enviado na rede selecionada.
  • -
- -

Mudanças no serviço de câmera

-

Nesta prévia, o modelo para acessar recursos compartilhados no serviço de câmera foi alterado -do antigo modelo de acesso “primeiro a chegar, primeiro a ser atendido” para um modelo de acesso onde -os processos de alta prioridade são favorecidos. As mudanças no comportamento do serviço incluem:

-
    -
  • Acesso aos recursos do subsistema da câmera, incluindo abertura e configuração de um dispositivo de câmera, -concedido com base na prioridade do processo do aplicativo do cliente. Processos de aplicativos com atividades -visíveis ao usuário ou de primeiro plano são geralmente de alta prioridade, tornando a aquisição -e o uso de recursos da câmera mais dependentes.
  • -
  • Clientes de câmera ativa para aplicativos de menor prioridade podem ser "despejados" quando -um aplicativo de alta prioridade tenta usar a câmera. Na API {@link android.hardware.Camera} obsoleta, -isto resulta em -{@link android.hardware.Camera.ErrorCallback#onError(int,android.hardware.Camera) onError()} sendo -chamado para o cliente despejado. Na API {@link android.hardware.camera2 Camera2}, isto resulta em -{@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected(android.hardware.camera2.CameraDevice) onDisconnected()} -sendo chamado para o cliente despejado.
  • -
  • Em dispositivos com hardware de câmera adequado, processos de aplicativo separados podem -abrir e usar independentemente os dispositivos de câmera simultaneamente. No entanto, casos de uso de vários processos -em que o acesso simultâneo causa uma degradação significante de desempenho ou capacidades -de qualquer um dos dispositivos de câmera abertos agora são detectados e proibidos pelo serviço da câmera. Esta alteração -pode resultar em "despejos" para clientes de menor prioridade quando nenhum aplicativo está -tentando acessar o mesmo dispositivo de câmera diretamente. -
  • -
  • -Alterar o usuário atual faz com que os clientes da câmera ativa em aplicativos pertencentes à conta do usuário anterior -sejam despejados. O acesso à câmera é limitado a perfis de usuário pertencentes ao usuário do dispositivo atual. -Na prática, isso significa que uma conta de "convidado", por exemplo, não poderá deixar -processos em execução que usam o subsistema da câmera quando o usuário alternar para uma conta diferente. -
  • -
- -

Tempo de execução de ART

-

O tempo de execução de ART agora implementa adequadamente as regras de acesso -para o método {@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()}. Esta -alteração corrige um problema onde o Dalvik estava verificando as regras de acesso incorretamente em versões anteriores. -Se o aplicativo usa o método -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} e você quer -substituir as verificações de acesso, chame o método -{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()} com o parâmetro -de entrada definido como {@code true}. Se o aplicativo usar a -biblioteca v7 appcompat ou a -biblioteca v7 recyclerview, -deve-se atualizá-lo para usar as versões mais recentes dessas bibliotecas. Caso contrário, certifique-se -de que as classes personalizadas mencionadas a partir do XML sejam atualizadas para que os construtores de classe estejam acessíveis.

- -

Esta prévia atualiza o comportamento do vinculador dinâmico. O vinculador dinâmico agora entende -a diferença entre um {@code soname} da biblioteca e o seu caminho -( -erro público 6670), e a pesquisa por {@code soname} -agora está implementada. Os aplicativos que anteriormente trabalhavam e têm entradas {@code DT_NEEDED} inválidas -(geralmente, caminhos absolutos no sistema de arquivo na máquina de programação) podem falhar ao serem carregados.

- -

O sinalizador {@code dlopen(3) RTLD_LOCAL} agora está corretamente implementado. Observe que -{@code RTLD_LOCAL} é o padrão. Portanto, chamadas para {@code dlopen(3)} que não usam explicitamente -{@code RTLD_LOCAL} serão afetadas (a não ser que o aplicativo tenha usado explicitamente {@code RTLD_GLOBAL}). Com -{@code RTLD_LOCAL}, os símbolos não estarão disponíveis para as bibliotecas carregadas por chamadas posteriores a -{@code dlopen(3)} (o oposto ocorre quando mencionado por entradas {@code DT_NEEDED}).

-

- -

Validação de APK

-

A plataforma agora realiza validações mais estritas de APKs. Um APK é considerado corrompido se um arquivo -for declarado no manifesto, mas não estiver presente no próprio APK. Um APK deve ser atribuído novamente se qualquer -conteúdo for removido.

- -

Mudanças do Android for Work

-

Esta prévia inclui as seguintes mudanças de comportamento para Android for Work:

-
    -
  • Contatos de trabalho em contextos pessoais. O registro de chamadas do telefone do Google -agora exibe os contatos de trabalho quando o usuário visualiza chamadas anteriores. -A configuração {@code DevicePolicyManager.setCrossProfileCallerIdDisabled()} para {@code true} oculta -os contatos do perfil de trabalho no registro de chamadas do telefone. Os contatos de trabalho podem ser exibidos junto com os contatos pessoais -aos dispositivos por meio de Bluetooth somente -se {@code DevicePolicyManager.setBluetoothContactSharingDisabled()} estiver definido como {@code false}. Por -padrão, ele está definido como {@code true}. -
  • -
  • Remoção de configuração Wi-Fi: as configurações de Wi-Fi adicionadas por um dono de perfil -(por exemplo, por meio de chamadas para o método -{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration) -addNetwork()}) agora são removidas se esse perfil de trabalho é excluído.
  • -
  • Bloqueio de configuração Wi-Fi: qualquer configuração Wi-Fi criada por um dono do dispositivo -ativo não pode mais ser modificada nem excluída pelo usuário. O usuário ainda pode criar -e modificar as próprias configurações de Wi-Fi, contanto que a constante {@link android.os.UserManager} -{@link android.os.UserManager#DISALLOW_CONFIG_WIFI} não tenha sido definida por ele.
  • -
  • Faça o download do Work Policy Controller por meio de uma adição de conta Google: quando uma conta Google -que requer um gerenciamento por meio do aplicativo Work Policy Controller (WPC) é adicionada ao dispositivo -fora de um contexto gerenciado, o fluxo de conta adicionada agora solicita ao usuário para instalar -o WPC adequado. Este comportamento também se aplica a contas adicionadas por meio de -Settings (Configurações) > Accounts (Contas) no assistente de configuração inicial do dispositivo.
  • -
  • Alterações aos comportamentos específicos da API DevicePolicyManager: -chamar o método {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName,boolean) setCameraDisabled()} -afeta a câmera somente para o usuário que realizou a chamada. Chamá-lo a partir do perfil gerenciado -não afeta os aplicativos de câmera em execução no usuário principal. Além disso, -o método {@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,int) setKeyguardDisabledFeatures()} -agora está disponível para donos de perfil, além dos donos do dispositivo. Um dono de perfil pode definir -estas restrições de proteção de bloqueio: -
      -
    • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} e - {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_FINGERPRINT}, que afetam - as configurações de proteção de bloqueio para o usuário pai do perfil.
    • -
    • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, que - afeta somente as notificações geradas por aplicativos no perfil gerenciado.
    • -
    -
  • -
diff --git a/docs/html-intl/intl/pt-br/preview/download.jd b/docs/html-intl/intl/pt-br/preview/download.jd deleted file mode 100644 index 80685f9d008b0f9431e51e04a0d00ddcad7da799..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/download.jd +++ /dev/null @@ -1,361 +0,0 @@ -page.title=Downloads -page.image=images/cards/card-download_16-9_2x.png - -@jd:body - -
- - - - -
- - - - -

- O Android M Preview SDK inclui ferramentas de desenvolvimento, arquivos de sistema do Android e arquivos da biblioteca - para ajudar você a testar o aplicativo e novas APIs da próxima versão da plataforma. Este documento - descreve como adquirir os componentes disponíveis para download da prévia para o teste do aplicativo. -

- - -

Android 6.0 SDK

- -

- O Preview SDK está disponível para download no Android SDK Manager. Para obter mais informações - sobre o download e a configuração do Preview SDK, consulte Configuração do Preview SDK. -

- - -

Documentação do desenvolvedor

- -

- O pacote de download da documentação do desenvolvedor fornece informações de referência de API detalhadas e um relatório de diferença de API para a prévia. -

- - - - - - - - - - -
DescriptionDownload / Checksums
Android M Preview 3
Developer Docs
m-preview-3-developer-docs.zip
- MD5: d99b14b0c06d31c8dfecb25072654ca3
- SHA-1: 9cefeeda07676130da606a1796e1c00fffc667c1 -
- - -

Imagens do sistema de hardware

- -

- Essas imagens do sistema permitem que você instale uma versão de prévia da plataforma em um dispositivo físico -para fins de teste. Ao configurar um dispositivo com uma dessas imagens, é possível instalar e testar o aplicativo - para verificar o seu desempenho na próxima versão da plataforma. O processo de instalação de uma imagem do sistema - em um dispositivo remove todos os dados do dispositivo. Portanto, deve-se realizar um backup dos dados - antes de instalar uma imagem do sistema. -

- -

- Aviso: as seguintes imagens do sistema Android são prévias e estão sujeitas a alterações. O uso - dessas imagens do sistema são governadas pelo Contrato de licença do Android SDK Preview. As imagens do sistema do Android Preview - não são versões estáveis e podem conter erros e defeitos que podem resultar - em danos aos sistemas do computador, aos dispositivos e aos dados. As imagens do sistema Android Preview - não estão sujeitas ao mesmo teste do sistema operacional de fábrica e podem fazer com que o telefone e aplicativos e os serviços - instalados parem de funcionar. -

- - - - - - - - - - - - - - - - - - - - - - - - -
DeviceDownload / Checksums
Nexus 5 (GSM/LTE)
"hammerhead"
hammerhead-MPA44I-preview-2ebbc049.tgz
- MD5: 91a924fb0c9f8e716e3b4c9954fd0dbb
- SHA-1: 2ebbc049b68c4da8baeee3e42bb94d7a965ba4a3 -
Nexus 6
"shamu"
shamu-MPA44I-preview-62b9c486.tgz
- MD5: ac6e58da86125073d9c395257fd42664
- SHA-1: 62b9c486fd7a5020e228d53ca5acd5c1857e48ff -
Nexus 9
"volantis"
volantis-MPA44I-preview-5c30a6e2.tgz
- MD5: 7f83768757913d3fea945a661020d185
- SHA-1: 5c30a6e2acd11a81f4105b12d23ff654f534f699 -
Nexus Player
"fugu"
fugu-MPA44I-preview-2860040a.tgz
- MD5: 438da8d37da9e341a69cfb16a4001ac5
- SHA-1: 2860040a326582f1ff5f702bf9a1ef002717fc98 -
- -

Instalar uma imagem no dispositivo

- -

- Para usar uma imagem de dispositivo para testes, deve-se instalá-lo em um dispositivo compatível. Siga - as instruções abaixo para instalar uma imagem de sistema. -

- -
    -
  1. Faça o download e descompacte um dos pacotes de imagem do sistema listados aqui.
  2. -
  3. Faça um backup dos dados do dispositivo que deseja preservar.
  4. -
  5. Siga as instruções em - developers.google.com/android - para programar em flash a imagem no dispositivo.
  6. -
- -

- Observação: ao programar em flash a imagem do sistema de prévia no dispositivo de desenvolvimento, - ele é atualizado automaticamente com o próximo lançamento da prévia por meio de atualizações over-the-air (OTA). -

- -

Reverter um dispositivo para as especificações de fábrica

- -

- Caso queira desinstalar a prévia e reverter o dispositivo para as especificações de fábrica, acesse - developers.google.com/android e - faça o download da imagem que deseja programar em flash no dispositivo. Siga as instruções nesta página - para programar em flash a imagem no dispositivo. -

- -
- -
- - - - diff --git a/docs/html-intl/intl/pt-br/preview/features/app-linking.jd b/docs/html-intl/intl/pt-br/preview/features/app-linking.jd deleted file mode 100644 index 17a9896136196de34d508ce2960b2851482e7358..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/features/app-linking.jd +++ /dev/null @@ -1,123 +0,0 @@ -page.title=Links de aplicativos -page.image=images/cards/card-app-linking_2x.png -page.keywords=applinking, deeplinks, intents -@jd:body - - - -

- O sistema de intenções do Android é um mecanismo flexível para possibilitar que aplicativos lidem com conteúdos e solicitações. - Vários aplicativos podem declarar padrões de URI correspondentes em seus filtros de intenções. Quando um usuário clica em um - link da web que não tem um manipulador de inicialização padrão, a plataforma pode exibir um diálogo para - o usuário selecionar entre uma lista de aplicativos que declararam filtros de intenções correspondentes. -

- -

- O Android M Developer Preview introduz suporte para links de aplicativos, que aprimora - a manipulação de links existentes, permitindo que desenvolvedores de aplicativos associem um aplicativo a um domínio da web pertencente a eles. Quando - os desenvolvedores criam esta associação, a plataforma pode automaticamente determinar o aplicativo padrão usado - para lidar com um link da web particular e ignorar a solicitação aos usuários. -

- - -

Declarar uma associação de site

- -

- Os donos de sites da web devem declarar as associações aos aplicativos para estabelecer um link de aplicativo. O dono do site - declara a relação com um aplicativo hospedando um arquivo JSON chamado {@code statements.json} no - local bem conhecido no domínio: -

- -
http://<domain>:<optional port>/.well-known/statements.json
- -

- Observação: - durante o período do M Developer Preview, o arquivo JSON é verificado por meio de protocolo http. Para - o lançamento oficial da plataforma, o arquivo é verificado com o protocolo http criptografado. -

- -

- Este arquivo JSON indica o aplicativo do Android que deve ser usado como o manipulador padrão para URLs - neste domínio. Ele identifica o aplicativo com base nestes campos: -

- -
    -
  • {@code package_name}: o nome do pacote declarado no manifesto do arquivo.
  • - -
  • {@code sha256_cert_fingerprints}: a impressão digital SHA256 do certificado assinado do aplicativo. - É possível usar o Java Keytool para gerar a impressão digital usando o seguinte comando: -
    keytool -list -v -keystore my-release-key.keystore
    -
  • -
- -

- A seguinte lista de arquivos exibe um exemplo de conteúdo e formato - de um arquivo {@code statements.json}: -

- -
-[{
-  "relation": ["delegate_permission/common.handle_all_urls"],
-  "target": {
-    "namespace": "android_app",
-    "package_name": "<package name>",
-    "sha256_cert_fingerprints": ["6C:EC:C5:0E:34:AE....EB:0C:9B"]
-  }
-}]
-
- - - - -

- Um aplicativo pode solicitar que a plataforma verifique automaticamente quaisquer links de aplicativo relacionados aos arquivos {@code statements.json} - hospedados nos respectivos domínios da web que são definidos pelos nomes de host - nos elementos de dados dos seus filtros de intenções. Para solicitar a verificação de link de aplicativo, adicione um atributo {@code android:autoVerify} - a cada filtro de intenção desejado no manifesto, como exibido no seguinte fragmento - de código do manifesto: -

- -
-<activity ...>
-    <intent-filter android:autoVerify="true">
-        <action android:name="android.intent.action.VIEW" />
-        <category android:name="android.intent.category.DEFAULT" />
-        <category android:name="android.intent.category.BROWSABLE" />
-        <data android:scheme="http" android:host="www.android.com" />
-        <data android:scheme="https" android:host="www.android.com" />
-    </intent-filter>
-</activity>
-
- -

- Quando o atributo {@code android:autoVerify} está presente em um manifesto do aplicativo, a plataforma - tenta verificar os links do aplicativo quando o aplicativo é instalado. Se a plataforma não conseguir - verificar os links do aplicativo, o aplicativo não será definido como o aplicativo preferencial para lidar com os links da web. Na próxima vez - que um usuário tentar abrir um dos links, a plataforma voltará a apresentar ao usuário - uma caixa de diálogo. -

- -

- Observação: no teste, há chances de ocorrer um falso positivo se a verificação - falhar, mas o usuário explicitamente permitiu que o aplicativo abrisse links sem solicitar, usando - o aplicativo do sistema Settings (Configurações). Neste caso, nenhum diálogo é exibido e o link vai direto para o - aplicativo, mas somente devido à configuração do usuário, e não porque a verificação foi bem-sucedida. -

- - -

Gerenciar configurações de link de aplicativo

- -

- Os usuários podem alterar as configurações de link de aplicativo para que os URLs sejam tratados da maneira que preferirem. É possível revisar e - gerenciar os links de aplicativo no aplicativo Settings (Configurações) do sistema, em Settings (Configurações) > Apps (Aplicativos) > App Info (Informações do aplicativo) > - Open by default (Abrir por padrão). -

diff --git a/docs/html-intl/intl/pt-br/preview/features/runtime-permissions.jd b/docs/html-intl/intl/pt-br/preview/features/runtime-permissions.jd deleted file mode 100644 index 762e1ae8243d88962a3d39690bb9748cee2f96bf..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/features/runtime-permissions.jd +++ /dev/null @@ -1,794 +0,0 @@ -page.title=Permissões -page.tags=previewresources, androidm -page.keywords=permissions, runtime, preview -page.image={@docRoot}preview/images/permissions_check.png -@jd:body - - -
-
-

Visualização rápida

-
    -
  • Se o aplicativo direciona o M Preview SDK, ele indica aos usuários para conceder - permissões no tempo de execução, em vez de tempo de instalação.
  • -
  • Os usuários podem revogar as permissões a qualquer momento na tela de configurações -do aplicativo.
  • -
  • O aplicativo precisa verificar se tem as permissões necessárias -sempre que for executado.
  • -
- -

Neste documento

-
    -
  1. Visão geral
  2. -
  3. Codificação para permissões de tempo de execução
  4. -
  5. Teste de permissões de tempo de execução
  6. -
  7. Práticas recomendadas
  8. -
- - - - -
-
- - -

- O M Developer Preview introduz um novo modelo de permissões de aplicativo -que agiliza o processo de instalação e atualização de aplicativos para os usuários. Se um aplicativo - que está sendo executado no M Preview suporta o novo modelo de permissões, o usuário não precisa conceder - permissões ao instalar ou atualizar o aplicativo. Em vez disso, o aplicativo - solicita as permissões à medida que precisar e o sistema exibe um diálogo - ao usuário pedindo a permissão. -

- -

- Se um aplicativo suportar o novo modelo de permissões, ele - ainda poderá ser instalado e executado em versões mais antigas do Android, usando o antigo modelo - de permissões nesses dispositivos. -

- -

- Visão geral -

- -

- Com o M Developer Preview, a plataforma introduz um novo modelo - de permissões. Eis um resumo dos componentes essenciais deste novo modelo: -

- -
    -
  • - Declaração de permissões: O aplicativo declara todas - as permissões necessárias no manifesto, como nas plataformas anteriores do Android. -
  • - -
  • - Grupos de permissão: As permissões são divididas em -grupos de permissão, baseados na funcionalidade. Por exemplo: o grupo de permissão - CONTACTS contém permissões para ler e escrever - informações de perfil e contatos do usuário. -
  • - -
  • -

    Permissões limitadas concedidas no tempo de instalação: Quando o usuário - instala ou atualiza o aplicativo, o sistema concede todas - as permissões que o aplicativo solicita que estão em {@link - android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}. - Por exemplo: as permissões de internet e despertador estão em {@link - android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}. Portanto, - as permissões são concedidas automaticamente no tempo de instalação. -

    - -

    O sistema pode também conceder as permissões de sistema e assinatura de aplicativo, - como descrito em permissões de assinatura - e aplicativos do sistema. O usuário não é alertado a conceder permissões - no tempo de instalação.

    -
  • - -
  • - O usuário concede permissões no tempo de execução: Quando um aplicativo solicita - uma permissão, o sistema exibe um diálogo ao usuário e, em seguida, - chama a função de retorno de chamada do aplicativo para notificá-lo se a permissão foi concedida. Se um - usuário concede uma permissão, o aplicativo recebe todas - as permissões na área funcional desta permissão que foram declaradas no manifesto do aplicativo. -
  • - -
- -

- Este modelo de permissões altera a forma como o aplicativo se comporta diante os recursos - que precisam de permissões. Eis um resumo das práticas de desenvolvimento que devem - ser seguidas para ajustar para este modelo: -

- -
    - -
  • - Sempre verificar as permissões: Quando o aplicativo - precisa realizar uma ação que requer uma permissão, ele deve primeiro verificar - se já a tem. Caso não tenha, ele solicita - o concedimento desta permissão. -
  • - -
  • - Lidar com falta de permissões dignamente: Se o aplicativo não - recebe a permissão adequada, ele deve lidar com a falha de forma limpa. - Por exemplo, se a permissão é necessária para um recurso adicionado, - o aplicativo pode desativar este recurso. Se a permissão for essencial - para que o aplicativo funcione, ele desativará toda sua funcionalidade - e informará ao usuário que precisa desta permissão. -
  • - -
    - -

    - Figura 1. Tela de permissões nas configurações do aplicativo. -

    -
    - -
  • - As permissões são revogáveis: Os usuários podem revogar as permissões - de um aplicativo a qualquer momento. Se um usuário desativar as permissões de um aplicativo, - o aplicativo não é notificado. Novamente, o aplicativo deve verificar - se tem todas as permissões necessárias antes de realizar qualquer ação restrita. -
  • -
- -

- Observação: se um aplicativo direcionar o M Developer Preview, ele - deve usar o novo modelo de permissões. -

- -

- No momento do lançamento do M Developer Preview, - nem todos os aplicativos Google implementam completamente o novo modelo de permissões. A Google está atualizando estes aplicativos - durante o curso do M Developer Preview para respeitar adequadamente a configuração - de alternação de permissões. -

- -

- Observação: se o aplicativo tiver a própria superfície de API, - não represente permissões sem antes garantir que o autor da chamada tenha as permissões necessárias - para acessar esses dados. -

- -

- Permissões de assinatura e aplicativos do sistema -

- -

- Geralmente, quando um usuário instala um aplicativo, o sistema somente fornece ao aplicativo o - {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL - PROTECTION_NORMAL}. No entanto, sob algumas circunstâncias, o sistema concede - ao aplicativo mais permissões: -

- -
    -
  • Se um aplicativo faz parte da imagem do sistema, ele recebe automaticamente - todas as permissões listadas no manifesto. -
  • - -
  • Se o aplicativo solicitar as permissões no manifesto que está em {@link - android.content.pm.PermissionInfo#PROTECTION_SIGNATURE PROTECTION_SIGNATURE}, - e estiver assinado com o mesmo certificado que o aplicativo que declarou essas permissões, - o sistema concederá essas permissões na instalação ao aplicativo - que fez a solicitação. -
  • -
- -

- Em ambos os casos, o usuário ainda pode revogar as permissões a qualquer - momento acessando a tela de configurações do sistema e escolhendo Aplicativos - > app_name > Permissões. O aplicativo - deve continuar com a verificação das permissões no tempo de execução e solicitá-las - se necessário. -

- -

- Compatibilidade anterior e posterior -

- -

- Se um aplicativo não direciona para o M Developer Preview, ele deve continuar a usar - o modelo antigo de permissões mesmo nos dispositivos M Preview. Quando o usuário instala - o aplicativo, o sistema pede para que ele conceda todas as permissões - listadas no manifesto do aplicativo. -

- -

- Observação: em dispositivos que são executados no M Developer Preview, - um usuário pode desativar as permissões para qualquer aplicativo (incluindo aplicativos de legado) - na tela de configurações do aplicativo. Se um usuário desativa as permissões de um aplicativo de legado, o sistema - silenciosamente desativa a funcionalidade adequada. Quando um aplicativo tentar realizar - uma operação que requer esta permissão, a operação não necessariamente causará - uma exceção. Em vez disso, ele retornará um conjunto de dados vazio, - sinalizará um erro ou exibirá um comportamento inesperado. Por exemplo, caso queira - consultar um calendário sem permissão, o método retorna um conjunto de dados vazio. -

- -

- Se instalar um aplicativo usando o novo modelo de permissões em um dispositivo - que não está executando o M Preview, - o sistema o tratará da mesma forma que qualquer outro aplicativo: o sistema pedirá - para que o usuário conceda todas as permissões declaradas no momento da instalação. -

- -

- Observação: para a liberação de prévia, deve-se definir a versão mínima de SDK - para o M Preview SDK para compilar com o SDK de prévia. Isto significa que você - não poderá testar tais aplicativos em plataformas mais antigas durante a prévia - de desenvolvedor. -

- -

Permissões versus intenções

- -

- Em vários casos, é possível escolher entre duas maneiras para que o aplicativo realize - uma tarefa. É possível fazer com que o aplicativo solicite uma permissão para realizar a operação - por conta própria. Alternativamente, é possível fazer com que o aplicativo use uma intenção para que outro aplicativo - realize a tarefa. -

- -

- Por exemplo, imagine que o aplicativo precisa da função de tirar fotos com - a câmera do dispositivo. O aplicativo pode solicitar a permissão -android.permission.CAMERA, que permite que ele acesse - a câmera diretamente. O aplicativo então usará as APIs da câmera - para controlar a câmera e tirar uma foto. Esta abordagem fornece ao aplicativo - controle completo sobre o processo de fotografia e permite - que você incorpore a IU da câmera. -

- -

- No entanto, caso não precise de tal controle, é possível apenas usar uma intenção {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} - para solicitar uma imagem. Ao iniciar a intenção, o usuário deve escolher - um aplicativo de câmera (se não houver um aplicativo padrão de câmera) - para tirar a foto. O aplicativo da câmera retorna a imagem ao método {@link - android.app.Activity#onActivityResult onActivityResult()} do aplicativo. -

- -

- De forma semelhante, caso precise realizar uma ligação, - acessar os contatos do usuário etc., é possível fazer estas ações criando uma intenção adequada - ou solicitar a permissão e o acesso aos objetos adequados diretamente. Essas são - as vantagens e desvantagens de cada abordagem. -

- -

- Se usar permissões: -

- -
    -
  • O aplicativo tem controle completo sobre a experiência do usuário ao realizar - a operação. No entanto, esse controle amplo é adicionado à complexidade da tarefa, - levando em consideração a necessidade de projetar uma IU adequada. -
  • - -
  • O usuário deve fornecer a permissão uma vez: na primeira realização - da operação. Depois, o aplicativo pode realizar a operação sem - precisar de mais interações do usuário. No entanto, - se o usuário não conceder a permissão (ou revogá-la posteriormente), - o aplicativo não conseguirá realizar a operação. -
  • -
- -

- Se usar uma intenção: -

- -
    -
  • Você não terá que projetar a IU para a operação. O aplicativo que lida com - a intenção fornece a IU. No entanto, isso significa que você não terá controle - completo sobre a experiência de usuário. O usuário poderá interagir com um aplicativo - que você nunca viu. -
  • - -
  • Se o usuário não tem um aplicativo padrão para a operação, - o sistema pede para que o usuário escolha um aplicativo. - Se o usuário não designar um manipulador padrão, - ele terá que acessar uma caixa de diálogo extra sempre que realizar a operação. -
  • -
- -

Codificação para permissões de tempo de execução

- -

- Se um aplicativo direciona o novo M Developer Preview, ele deve usar o novo - modelo de permissões. Isto significa que, além de declarar as permissões necessárias no manifesto, - deve-se também verificar se o aplicativo - tem as permissões no tempo de execução e, - caso ainda não as tenha, solicitá-las. -

- -

- Possibilitar um novo modelo de permissões -

- -

- Para possibilitar o modelo de permissões do M Developer Preview, configure o atributo -targetSdkVersion do aplicativo para "MNC" e -compileSdkVersion para "android-MNC". Isto ativará - todos os novos recursos de permissão. -

- -

- Para a liberação de uma prévia, deve-se definir minSdkVersion para -"MNC" para compilar com o SDK de prévia. -

- -

- Designar uma permissão somente para o M Preview -

- -

- É possível usar o novo elemento <uses-permission-sdk-m> no manifesto do aplicativo - para indicar que uma permissão é necessária apenas no M Developer Preview. Se você - declarar uma permissão desta maneira, sempre que o aplicativo for instalado - em um dispositivo mais antigo, o sistema não solicitará ao usuário - nem concederá a permissão ao aplicativo. Usando o elemento <uses-permission-sdk-m> -, é possível adicionar novas permissões - a versões atualizadas do aplicativo sem forçar os usuários a conceder permissões - ao instalar a atualização. -

- -

- Se o aplicativo está sendo executado em um dispositivo com M Developer Preview, o -<uses-permission-sdk-m> se comporta da mesma forma que -<uses-permission>. - O sistema não solicita ao usuário que conceda quaisquer permissões ao instalar o aplicativo - e o aplicativo solicita as permissões à medida que forem necessárias. -

- -

- Solicitação de permissões -

- -

- Se o aplicativo usa o novo modelo de permissões do M Developer Preview, - o usuário não recebe solicitações para conceder todas as permissões quando o aplicativo é iniciado pela primeira vez em um dispositivo - que está sendo executado no M Preview. Em vez disso, o aplicativo solicita as permissões à medida - que forem necessárias. Quando um aplicativo solicita uma permissão, o sistema exibe uma caixa de diálogo - ao usuário. -

- -

- Se o aplicativo executar em um dispositivo que tem SDK 22 ou inferior, - ele usará o antigo modelo de permissões. Quando o usuário instala o aplicativo, ele é solicitado a conceder - todas as permissões que o aplicativo lista no manifesto, - exceto as permissões que forem marcadas com <uses-permission-sdk-m>. -

- -

Verifique em qual plataforma o aplicativo está sendo executado

- -

- Este modelo de permissões é suportado apenas em dispositivos que estão executando - o M Developer Preview. Antes de chamar qualquer um destes métodos, - o aplicativo deve verificar em qual plataforma está sendo executado - verificando o valor de {@link android.os.Build.VERSION#CODENAME - Build.VERSION.CODENAME}. Se o dispositivo estiver sendo executado no M Developer Preview, - {@link android.os.Build.VERSION#CODENAME CODENAME} será "MNC". -

- -

Verifique se o aplicativo tem a permissão necessária

- -

Quando o usuário tenta fazer algo que requer uma permissão, - o aplicativo verifica se tem a permissão para realizar esta operação. Para fazer isto, - o aplicativo chama - Context.checkSelfPermission(permission_name). O aplicativo - deve realizar isto para verificar se sabe que o usuário já concedeu esta permissão, - levando em consideração que o usuário pode revogar - as permissões do aplicativo a qualquer momento. Por exemplo, - se um usuário quiser usar um aplicativo para tirar uma foto, o aplicativo chamará - Context.checkSelfPermission(Manifest.permission.CAMERA).

- -

- Tabela 1. Permissões e grupos de permissões.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Grupo de permissõesPermissões
android.permission-group.CALENDAR -
    -
  • - android.permission.READ_CALENDAR -
  • -
-
    -
  • - android.permission.WRITE_CALENDAR -
  • -
-
android.permission-group.CAMERA -
    -
  • - android.permission.CAMERA -
  • -
-
android.permission-group.CONTACTS -
    -
  • - android.permission.READ_CONTACTS -
  • -
  • - android.permission.WRITE_CONTACTS -
  • -
  • - android.permission.READ_PROFILE -
  • -
  • - android.permission.WRITE_PROFILE -
  • -
-
android.permission-group.LOCATION -
    -
  • - android.permission.ACCESS_FINE_LOCATION -
  • -
  • - android.permission.ACCESS_COARSE_LOCATION -
  • -
-
android.permission-group.MICROPHONE -
    -
  • - android.permission.RECORD_AUDIO -
  • -
-
android.permission-group.PHONE -
    -
  • - android.permission.READ_PHONE_STATE -
  • -
  • - android.permission.CALL_PHONE -
  • -
  • - android.permission.READ_CALL_LOG -
  • -
  • - android.permission.WRITE_CALL_LOG -
  • -
  • - com.android.voicemail.permission.ADD_VOICEMAIL -
  • -
  • - android.permission.USE_SIP -
  • -
  • - android.permission.PROCESS_OUTGOING_CALLS -
  • -
-
android.permission-group.SENSORS -
    -
  • - android.permission.BODY_SENSORS -
  • -
-
    -
  • - android.permission.USE_FINGERPRINT -
  • -
-
android.permission-group.SMS -
    -
  • - android.permission.SEND_SMS -
  • -
  • - android.permission.RECEIVE_SMS -
  • -
  • - android.permission.READ_SMS -
  • -
  • - android.permission.RECEIVE_WAP_PUSH -
  • -
  • - android.permission.RECEIVE_MMS -
  • -
  • - android.permission.READ_CELL_BROADCASTS -
  • -
-
- -

Solicitar permissões se necessário

- -

Se o aplicativo já não tem a permissão necessária, ele chama o método - Activity.requestPermissions(String[], int) para solicitar - as permissões necessárias. O aplicativo passa - as permissões que deseja e também um "código de solicitação" do inteiro. - Este método funciona de forma assíncrona: ele retorna imediatamente e, - depois que o usuário responde à caixa de diálogo, o sistema chama - o método de retorno de chamada do aplicativo com os resultados, passando o mesmo "código de solicitação" que o aplicativo passou - para requestPermissions().

- -

O seguinte código verifica se o aplicativo tem a permissão - para ler os contatos do usuário e solicita a permissão, se necessário:

- -
-if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
-        != PackageManager.PERMISSION_GRANTED) {
-    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
-            MY_PERMISSIONS_REQUEST_READ_CONTACTS);
-
-    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
-    // app-defined int constant
-
-    return;
-}
-
- -

Lidar com a resposta de solicitação das permissões

- -

- Quando um aplicativo solicita as permissões, o sistema apresenta uma caixa de diálogo - ao usuário. Quando o usuário responde, o sistema invoca o - Activity.onRequestPermissionsResult(int, String[], int[]) - do aplicativo, passando a ele a resposta do usuário. O aplicativo precisa substituir este método. O retorno de chamada - recebe o mesmo código de solicitação passado para - requestPermissions(). Por exemplo, se um aplicativo solicita o acesso - READ_CONTACTS, ele pode ter o seguinte - método de retorno de chamada: -

- -
-@Override
-public void onRequestPermissionsResult(int requestCode,
-        String permissions[], int[] grantResults) {
-    switch (requestCode) {
-        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
-            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-
-                // permission was granted, yay! do the
-                // calendar task you need to do.
-
-            } else {
-
-                // permission denied, boo! Disable the
-                // functionality that depends on this permission.
-            }
-            return;
-        }
-
-        // other 'switch' lines to check for other
-        // permissions this app might request
-    }
-}
-
- -

Se o usuário concede a permissão, o sistema fornece ao aplicativo todas as permissões - que o manifesto do aplicativo lista para esta área funcional. Se o usuário negar a solicitação, - deve-se tomar a ação adequada. Por exemplo, deve-se desativar - quaisquer ações de menu que dependam desta permissão. - -

- -

- Quando o sistema pede para que o usuário conceda uma permissão, esse usuário tem a opção - de dizer ao sistema para que não peça esta permissão novamente. Nesse caso, - quando um aplicativo usa requestPermissions() para solicitar esta permissão, - o sistema nega imediatamente. Neste caso, o sistema chama - onRequestPermissionsResult() da mesma forma que faria se o usuário tivesse - rejeitado explicitamente a solicitação novamente. Por este motivo, o aplicativo - não pode presumir que uma interação direta com o usuário ocorreu. -

- -

Teste de permissões de tempo de execução

- - -

- Se o aplicativo for direcionado para o M Developer Preview, deve-se testar - se ele lida com as permissões corretamente. Não se pode presumir que o aplicativo - terá qualquer permissão quando executado. Quando o aplicativo é iniciado pela primeira vez, - é provável que não tenha permissões. O usuário pode revogar e restaurar permissões - a qualquer momento. -

- -

- Deve-se testar o aplicativo para garantir que ele se comporte corretamente em todas - as situações de permissão. Com o M Preview SDK, fornecemos os novos comandos - de Android - Debug Bridge (adb) para possibilitar que o aplicativo seja testado com quaisquer - configurações de permissões necessárias. -

- -

- Novas opções e comandos adb -

- -

- As ferramentas da plataforma M Preview SDK fornecem vários comandos novos para permitir - que você teste como o aplicativo lida com permissões. -

- -

- Instalar com permissões -

- -

- É possível usar a nova opção -g do comando adb - install, que instala o aplicativo - e fornece todas as permissões listadas em seu manifesto: -

- -
-$ adb install -g <path_to_apk>
-
- -

- Conceder e revogar permissões -

- -

- É possível usar os novos comandos do gerenciador - de pacotes (pm) de ADB para conceder e revogar as permissões de um aplicativo instalado. - Esta funcionalidade pode ser útil para testes automatizados. -

- -

- Para conceder uma permissão, use o comando grant do gerenciador de pacote: -

- -
-$ adb pm grant <package_name> <permission_name>
-
- -

- Por exemplo: para conceder a permissão do pacote com.example.myapp para gravar áudios, - use este comando: -

- -
-$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO
-
- -

- Para revogar uma permissão, use o comando revoke do gerenciador de pacote: -

- -
-$ adb pm revoke <package_name> <permission_name>
-
- -

Práticas recomendadas

- -

- O novo modelo de permissões fornece aos usuários uma experiência mais suave - e facilita a instalação de aplicativos, deixando-os mais confortáveis - com o que os aplicativos estão fazendo. Sugerimos que você siga as práticas recomendadas para aproveitar - todas as vantagens do novo modelo. -

- - -

Peça somente as permissões necessárias

- -

- Sempre que você pede uma permissão, o usuário é forçado a tomar uma decisão. - Se o usuário negar a solicitação, a funcionalidade do aplicativo será reduzida. - Deve-se minimizar o número de solicitações realizadas. -

- -

- Por exemplo: o aplicativo pode frequentemente adquirir a funcionalidade necessária usando - uma intenção em vez - de solicitar permissões. Se o aplicativo precisa tirar fotos com a câmera do telefone, - é possível usar uma intenção {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE - MediaStore.ACTION_IMAGE_CAPTURE}. Quando o aplicativo executa a intenção, - o sistema pede para que o usuário escolha um aplicativo de câmera já instalado - para tirar a foto. -

- -

- Não oprima o usuário -

- -

- Se você confrontar o usuário com várias solicitações de permissão de uma só vez, - é possível que ele se sinta oprimido e saia do aplicativo. - Em vez disso, deve-se solicitar as permissões somente quando necessário. -

- -

- Em alguns casos, uma ou mais permissões podem ser absolutamente essenciais para o aplicativo. - Nesta situação, faz sentido solicitar todas as permissões assim - que o aplicativo é iniciado. Por exemplo: se você fizer um aplicativo de fotografia, - ele precisará de acesso à câmera do dispositivo. Quando o usuário iniciar o aplicativo - pela primeira vez, ele não se surpreenderá quando receber - uma solicitação de permissão para usar a câmera. Mas, se o mesmo aplicativo tiver um recurso - para compartilhar fotos com os contatos do usuário, não se deve - pedir esta permissão na primeira inicialização. Em vez disso, espere o usuário tentar usar - o recurso de compartilhamento para pedir a permissão. -

- -

- Se o aplicativo fornecer um tutorial, faz sentido solicitar as permissões - necessárias no final da sequência do tutorial. -

- -

- Explique o porquê da necessidade das permissões -

- -

- O diálogo de permissões exibido pelo sistema ao chamar - requestPermissions() diz quais permissões o aplicativo requer, - mas não diz o porquê. Em alguns casos, o usuário pode achar isto confuso. - É uma boa ideia explicar ao usuário o porquê da necessidade das permissões - para o aplicativo antes de chamar requestPermissions(). -

- -

- Por exemplo: um aplicativo de fotografia pode precisar usar serviços de localização - para poder marcar as fotos geograficamente. Um usuário normal pode não entender que uma foto - pode conter informações de localização e ficar confuso quando - o aplicativo de fotografia quiser saber a localização. Portanto, neste caso, é uma boa ideia o aplicativo explicar - ao usuário sobre este recurso antes de chamar - requestPermissions(). -

- -

- Uma maneira de fazer isto é incorporar estas solicitações em um tutorial do aplicativo. O tutorial pode exibir cada um dos recursos - do aplicativo e, à medida que fizer isto, - pode também explicar quais permissões são necessárias. Por exemplo, o tutorial do aplicativo de fotografia - pode demonstrar os recursos de compartilhamento de fotos com os contatos e, - em seguida, dizer ao usuário que ele precisa fornecer as permissões - para que o aplicativo possa visualizar os contatos. O aplicativo pode então chamar requestPermissions() para solicitar - ao usuário este acesso. É claro que nem todos os usuários seguirão o tutorial. - Portanto, ainda é necessário verificar e solicitar as permissões durante - a operação normal do aplicativo. -

diff --git a/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline-crop.png b/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline-crop.png deleted file mode 100644 index 724a6af8bc513583d0ee8fdad3ea7cc44f13fd3d..0000000000000000000000000000000000000000 Binary files a/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline-crop.png and /dev/null differ diff --git a/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline.png b/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline.png deleted file mode 100644 index e9a339ef8276432c6be19489f9ac216578d9b8eb..0000000000000000000000000000000000000000 Binary files a/docs/html-intl/intl/pt-br/preview/images/m-preview-timeline.png and /dev/null differ diff --git a/docs/html-intl/intl/pt-br/preview/index.jd b/docs/html-intl/intl/pt-br/preview/index.jd deleted file mode 100644 index 69367181e508831e3f0afe933d0678dc6351d20e..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/index.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Android M Developer Preview -page.tags="preview", -meta.tags="preview, M preview", androidm -fullpage=true -section.landing=true -header.hide=1 -footer.hide=1 -@jd:body - -
-
-
-
- -
-
-

Android M Developer Preview

-

- Prepare-se para a próxima versão do Android. Teste os aplicativos no Nexus 5, 6, 9 e - Player. Explore o que há de novo — permissões em tempo de execução, - recursos de economia de energia aplicativo em espera e soneca, novas - tecnologias de auxílio e muito mais. -

- - - - Comece! -
- - - Developer Preview 3 (final SDK) -
-
-
-
-
-
-
- -
-

Recursos

-
- Informações essenciais para ajudar você a preparar os aplicativos para Android M. -
- -
- - -
-
- diff --git a/docs/html-intl/intl/pt-br/preview/license.jd b/docs/html-intl/intl/pt-br/preview/license.jd deleted file mode 100644 index 9e28b8a57d849f182cfda8bbacde0d63885edd0f..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/license.jd +++ /dev/null @@ -1,143 +0,0 @@ -page.title=Contrato de licença - -@jd:body - -

-Para começar a usar o Android SDK Preview, você deve concordar com os seguintes termos e condições. -Como descrito abaixo, observe que isto é uma versão de prévia do Android SDK, sujeita a alterações, que deve ser usada a seu risco. O Android SDK Preview não é uma versão estável e pode conter erros e defeitos que podem resultar em danos sérios aos sistemas de computador, aos dispositivos e aos dados. -

- -

-Este é o contrato de licença do Android SDK Preview (o “Contrato de Licença”). -

-
-1. Introdução - -1.1 O Android SDK Preview (que este Contrato de licença chama de "Preview", incluindo especificamente os arquivos de sistema do Android, APIs integradas e arquivos da biblioteca Preview, se e quando estiverem disponíveis) é licenciado por meio da concordância com os termos deste contrato. O Contrato de licença forma um vínculo contratual legal entre o contratante e a Google em relação ao uso do Preview. - -1.2 "Android" refere-se à pilha de software do Android para dispositivos, conforme disponibilizado no Projeto de código aberto do Android, localizado no URL a seguir: http://source.android.com/, atualizado periodicamente. - -1.3 "Google" refere-se à Google Inc, uma corporação de Delaware, com sede em 1600 Amphitheatre Parkway, Mountain View, CA 94043, Estados Unidos. - -2. Aceitação do Contrato de Licença - -2.1 A fim de usar o Preview, é necessário concordar com este Contrato de licença. O uso do Preview é proibido àqueles que não concordam com este Contrato de licença. - -2.2 Ao clicar em aceitar e/ou usar o Preview, você concorda com os termos do Contrato de licença - -2.3 É proibido o uso do Preview e a aceitação deste contrato pelo indivíduo que tenha impedimento legal sobre o recebimento do Preview sob as leis dos Estados Unidos ou de outros países, incluindo o país de residência ou no qual usa o Preview. - -2.4 Se for usar o Preview internamente na empresa ou organização, você deverá concordar com o vínculo com este contrato em nome do empregador ou de outra entidade e declarar e garantir que tem total autoridade legal para tanto. Se você não tem a autoridade necessária, não deve concordar com este contrato nem usar o Preview em nome do empregador ou de outra entidade. - -3. Licença do Preview da Google - -3.1 Sujeito aos termos do Contrato de licença, a Google confere uma licença limitada, revogável, livre de taxas, intransmissível, não sub-licenciável e não exclusiva para o uso apenas do Preview, pessoal ou internamente dentro da sua empresa ou organização, para fins de desenvolvimento de aplicativos executados na plataforma do Android. - -3.2 Você concorda que a Google ou terceiros detêm todos os direitos legais, títulos e interesses relativos ao Preview, incluindo quaisquer direitos de propriedade intelectual que subsistam no Preview. "Direitos de propriedade intelectual" se referem a todo e qualquer direito sob as leis de patentes, de direitos autorais, de segredo comercial, de marca registrada e todos os outros direitos de propriedade. A Google reserva todos os direitos não conferidos expressamente a você. - -3.3 O uso do Preview não é autorizado para qualquer finalidade não expressamente permitida por este Contrato de licença. Salvo na extensão exigida por licenças aplicáveis de terceiros, é proibido: (a) copiar (exceto para fins de cópia de segurança), modificar, adaptar, redistribuir, descompilar, fazer engenharia reversa, desmontar ou criar trabalhos derivados do Preview ou qualquer parte dele; ou (b) carregar qualquer parte do Preview em um aparelho celular ou outro dispositivo de hardware, exceto em computador pessoal, combinar qualquer parte do Preview com outros softwares ou distribuir qualquer software ou dispositivo que contenha uma parte do Preview. - -3.4 Você concorda que não tomará quaisquer medidas que possam causar ou resultar em fragmentação do Android, incluindo, sem limitar-se, a distribuição e a participação na criação ou na promoção, sob quaisquer formas, de um conjunto de desenvolvimento de software derivado do Preview. - -3.5 O uso, a reprodução e a distribuição de componentes do Preview licenciado sob licença de software de código aberto são regidos exclusivamente pelos termos daquela licença de software de código aberto, e não por este Contrato de licença. Você concorda em manter uma licença em bom estado para as licenças de software de código aberto sob todos os direitos concedidos e deter quaisquer ações que possam limitar, suspender ou romper tais direitos. - -3.6 Você concorda que a forma e a natureza do SDK que a Google fornece podem mudar sem aviso prévio e que as versões futuras do SDK podem ser incompatíveis com aplicativos desenvolvidos em versões anteriores do SDK. Você concorda que a Google pode cessar (permanente ou temporariamente) o fornecimento do Preview (ou quaisquer recursos dentro dele) a você ou a usuários em geral sob critério exclusivo da Google, sem aviso prévio. - -3.7 Nada neste Contrato de licença confere o direito de uso de quaisquer nomes comerciais, marcas comerciais, marcas de serviço, logomarcas, nomes de domínios e outros recursos de marcas especiais da Google. - -3.8 Você concorda que não removerá, ocultará nem alterará quaisquer observações de direitos de propriedade (incluindo observações de direitos autorais e de marcas registradas) que possam estar afixadas ou contidas no Preview. - -4. Uso do Preview por você - -4.1 A Google compreende que nada no Contrato de licença da a ela direito, título nem interesse no usuário (ou em seus licenciadores), sob o presente Contrato de licença, no que tange ao desenvolvimento de aplicativos de software através do uso do Preview, incluindo quaisquer direitos de propriedade intelectual que subsistam nos referidos aplicativos. - -4.2 Você concorda em usar o Preview e desenvolver aplicativos somente para as finalidades permitidas por (a) este Contrato de licença e (b) quaisquer leis, normas, diretrizes geralmente aceitas ou orientações aplicáveis nas jurisdições relevantes (incluindo quaisquer leis acerca da exportação e da importação de dados ou softwares nos Estados Unidos ou em outros países relevantes). - -4.3 Você concorda que, se usar o Preview para o desenvolvimento de aplicativos, deverá proteger a privacidade e os direitos legais dos usuários. Se nomes de usuário, senhas ou outras informações de acesso ou informações pessoais forem fornecidos ao aplicativo, deve-se informá-los de que tais dados estarão disponíveis para o aplicativo, além de fornecer observações de privacidade e proteção legalmente adequadas a esses usuários. Se o aplicativo armazenar informações pessoais ou confidenciais fornecidas pelos usuários, deve fazê-lo com segurança. Se o usuário fornecer informações da conta do Google, o aplicativo poderá usar essas informações exclusivamente para acessar a conta da Google do usuário quando houver autorização para fazê-lo e para os fins limitados pela autorização. - -4.4 Você concorda que não se envolverá em qualquer atividade com o Preview, incluindo o desenvolvimento e a distribuição de um aplicativo que interfira, perturbe, danifique ou acesse, de modo não autorizado, servidores, redes ou outras propriedades ou serviços da Google ou qualquer outro terceiro. - -4.5 Você concorda que é exclusivamente responsável por (e que a Google não tem qualquer responsabilidade com você ou terceiro) quaisquer dados, conteúdo ou recursos que criar, transmitir ou exibir por meio do Android e/ou de aplicativos do Android e pelas consequências que suas ações (incluindo perda ou dano que a Google possa sofrer) podem gerar. - -4.6 Você concorda que é exclusivamente responsável por (e que a Google não tem qualquer responsabilidade com você ou terceiro) qualquer violação das obrigações exigidas neste Contrato de licença, qualquer contrato ou termos de serviço aplicáveis a terceiros, qualquer lei ou norma aplicável e pelas consequências (incluindo a perda ou dano que a Google ou qualquer terceiro possa sofrer) de quaisquer violações. - -4.7 O Preview está em desenvolvimento e o seu teste e feedback são uma parte importante deste processo. Ao usar o Preview, você está ciente de que a implementação de alguns recursos ainda estão em desenvolvimento e que não se deve confiar que o Preview tem a funcionalidade completa de uma versão estável. Você concorda em não distribuir nem enviar publicamente quaisquer aplicativos usando este Preview, pois ele não será mais suportado após o lançamento oficial do Android SDK. - -5. Suas credenciais de desenvolvedor - -5.1 Você é responsável pela manutenção da confidencialidade de quaisquer credenciais de desenvolvedor que possam ser emitidas pela Google ou escolhidas por você e será o único responsável por todos os aplicativos que forem desenvolvidos sob suas credenciais de desenvolvedor. - -6. Privacidade e informações - -6.1 A fim de inovar e aprimorar continuamente o Preview, a Google pode coletar certas estatísticas de uso do software, incluindo, sem limitar-se, um identificador exclusivo, endereço IP associado, número de versão do software e informações sobre quais ferramentas e/ou serviços no Preview estão sendo usados e como estão sendo usados. Antes de coletar quaisquer dessas informações, o Preview o notificará e buscará seu consentimento. Se você recusar, as informações não serão coletadas. - -6.2 Os dados coletados são examinados coletivamente para aprimorar o Preview e são mantidos em conformidade com a Política de privacidade da Google acessível em http://www.google.com/policies/privacy/. - -7. Aplicativos de terceiros - -7.1 Ao usar o Preview para executar aplicativos desenvolvidos por terceiros ou que acessam dados, conteúdo ou recursos fornecidos por terceiros, você concorda que a Google não é responsável por tais aplicativos, dados, conteúdo ou recursos. Você compreende que quaisquer dados, conteúdo ou recursos passíveis de aceitação por meio de tais aplicativos de terceiros imputam responsabilidade exclusiva ao indivíduo que os originou. A Google não é responsável por qualquer perda ou dano que possa ocorrer como resultado do uso ou acesso de quaisquer aplicativos, dados, conteúdo ou recursos de terceiros. - -7.2 Você deve estar ciente de que os dados, conteúdo e recursos apresentados a você por aplicativos de terceiros podem ser protegidos pelos direitos de propriedade intelectual de posse dos fornecedores (ou de outras pessoas ou empresas em seus nomes). Não é permitido modificar, alugar, arrendar, emprestar, vender, distribuir nem criar trabalhos derivados com base nestes dados, conteúdo ou recursos (na totalidade ou em parte), salvo se houver permissão explícita especificada pelos respectivos detentores de direitos. - -7.3 Você reconhece que o uso de tais aplicativos, dados, conteúdo ou recursos de terceiros pode estar sujeito a termos adicionais entre você e o terceiro em questão. - -8. Uso de APIs da Google - -8.1 APIs da Google - -8.1.1 Ao usar qualquer API para recuperar dados da Google, você reconhece que eles podem ser protegidos por direitos de propriedade intelectual de posse da Google ou dos terceiros que fornecem os dados (ou de pessoas ou empresas em nomes deles). O uso de tal API pode estar sujeito a termos de serviço adicionais. Não é permitido modificar, alugar, arrendar, emprestar, vender, distribuir nem criar trabalhos derivados baseados nesses dados (na totalidade ou em parte), salvo se permitido pelos termos de serviço pertinentes. - -8.1.2 Se você usar qualquer API para recuperar dados de um usuário a partir da Google, reconhece e concorda que deve recuperar dados somente com consentimento explícito do usuário e somente quando, e para os fins limitados aos quais, o usuário conceder permissão para fazê-lo. - -9. Rescisão do Contrato de licença - -9.1 O Contrato de licença continuará a se aplicar até que ocorra uma rescisão sua ou da Google, como definido abaixo. - -9.2 Caso queira rescindir o Contrato de licença, você pode fazer isto cessando o uso do Preview e de qualquer credencial de desenvolvedor relevante. - -9.3 A Google pode, a qualquer momento, rescindir o Contrato de licença, com ou sem causa, com uma notificação. - -9.4 O Contrato de licença será encerrado automaticamente sem aviso ou outras ações na ocorrência de: -(A) a Google interromper o fornecimento do Preview ou de determinadas partes do Preview aos usuários no país em que você reside ou de onde o serviço é usado; e -(B) a Google emitir uma versão de lançamento final do Android SDK. - -9.5 Quando o Contrato de licença é rescindido, a licença concedida a você no Contrato de licença é finalizada, todo o uso do Preview será interrompido e as provisões dos parágrafos 10, 11, 12 e 14 deverão permanecer indefinidamente. - -10. EXCLUSÕES - -10.1 VOCÊ COMPREENDE E CONCORDA EXPRESSAMENTE QUE O RISCO DO USO DO PREVIEW É EXCLUSIVAMENTE SEU E QUE O PREVIEW É FORNECIDO NA FORMA EM QUE SE ENCONTRA E COMO DISPONIBILIZADO, SEM GARANTIA DE QUALQUER TIPO DA GOOGLE. - -10.2 O USO DO PREVIEW E DE QUALQUER MATERIAL BAIXADO OU OBTIDO DE OUTRO MODO PELO USO DO PREVIEW ESTÁ A SEU CRITÉRIO E RISCO E VOCÊ É O ÚNICO RESPONSÁVEL POR QUALQUER DANO AO SEU SISTEMA OPERACIONAL OU OUTRO DISPOSITIVO OU PELA PERDA DE DADOS QUE RESULTEM DE TAL USO. SEM LIMITAR OS PRECEDENTES, VOCÊ ENTENDE QUE O PREVIEW NÃO É UMA VERSÃO ESTÁVEL E QUE PODE CONTER ERROS, DEFEITOS E VULNERABILIDADES DE SEGURANÇA QUE PODEM RESULTAR EM DANOS SIGNIFICANTES, INCLUINDO A PERDA IRRECUPERÁVEL OU COMPLETA DO USO DO SISTEMA DO COMPUTADOR OU DE OUTROS DISPOSITIVOS. - -10.3 A GOOGLE EXCLUI EXPRESSAMENTE TODAS AS GARANTIAS E CONDIÇOES DE QUALQUER TIPO, EXPRESSAS OU IMPLÍCITAS, INCLUINDO, MAS NÃO LIMITADO A, GARANTIAS E CONDIÇÕES DE COMERCIALIZAÇÃO IMPLÍCITAS, ADEQUAÇÃO A UMA FINALIDADE PARTICULAR E A NÃO VIOLAÇÃO. - -11. LIMITAÇÃO DE RESPONSABILIDADE - -11.1 VOCÊ COMPREENDE E CONCORDA EXPRESSAMENTE QUE A GOOGLE, SUAS SUBSIDIÁRIAS, AFILIADAS E SEUS LICENCIADORES NÃO SERÃO RESPONSABILIZADOS POR VOCÊ SOB QUALQUER TEORIA DE RESPONSABILIDADE POR QUAISQUER DANOS, SEJAM ELES DIRETOS, INDIRETOS, INCIDENTAIS, ESPECIAIS, CONSEQUENCIAIS OU DE EXEMPLO QUE POSSAM INCORRER, INCLUINDO QUALQUER PERDA DE DADOS, INDEPENDENTE DE AVISO À GOOGLE OU A SEUS REPRESENTANTES OU DA NECESSIDADE DE AVISO SOBRE A POSSIBILIDADE DA INCORRÊNCIA DE TAIS PERDAS. - -12. Indenização - -12.1 Ao limite máximo permitido por lei, você concorda em defender, indenizar e isentar a Google, suas afiliadas e respectivos conselheiros, diretores, empregados e agentes com relação a todas e quaisquer reivindicações, ações, processos ou procedimentos, bem como todas e quaisquer perdas, responsabilidades, danos, custos e despesas (incluindo honorários advocatícios) decorrentes ou provenientes de: (a) seu uso do Preview, (b) qualquer aplicativo desenvolvido no Preview que infrinja direitos de propriedade intelectual de qualquer pessoa, difame qualquer pessoa ou viole seus direitos de publicidade ou privacidade e (c) qualquer não cumprimento deste Contrato de licença. - -13. Mudanças no Contrato de licença - -13.1 A Google pode realizar mudanças no Contrato de licença à medida que distribui novas versões do Preview. Quando essas mudanças forem realizadas, a Google fará uma nova versão do Contrato de licença disponível no site em que o Preview estiver disponível. - -14. Termos legais gerais - -14.1 Esse Contrato de licença constitui o contrato legal integral entre você e a Google e rege o uso do Preview (excluindo quaisquer serviços que a Google possa fornecer a você sob um contrato escrito em separado), e substitui inteiramente quaisquer contratos anteriores entre você e a Google em relação ao Preview. - -14.2 Você concorda que, se a Google não exercer nem impetrar qualquer direito ou recurso legal que esteja contido no Contrato de licença (ou que a Google detenha direitos nos termos de qualquer lei aplicável), não se considerará esse fato como uma renúncia formal aos direitos da Google e esses direitos ou recursos continuarão disponíveis à Google. - -14.3 Se qualquer tribunal de justiça que tiver a competência para decidir sobre esse tema determinar que qualquer cláusula do Contrato de licença é inválida, tal cláusula será removida do contrato sem afetar as cláusulas restantes ou sua vigência. As cláusulas restantes do Contrato de licença continuarão válidas e obrigatórias. - -14.4 Você reconhece e concorda que cada membro do grupo de empresas das quais a Google é a empresa controladora deve ser beneficiário terceiro do Contrato de licença e que essas outras empresas terão o poder de aplicar diretamente, e apoiar-se em, qualquer cláusula do Contrato de licença que confira um direito (ou direitos em favor) deles. Além disso, nenhuma outra pessoa nem empresa deve ser beneficiário terceiro do Contrato de licença. - -14.5 RESTRIÇÕES DE EXPORTAÇÃO. O PREVIEW ESTÁ SUJEITO ÀS LEIS E NORMAS DE EXPORTAÇÃO DOS ESTADOS UNIDOS. VOCÊ DEVE CUMPRIR TODAS AS LEIS E NORMAS DOMÉSTICAS E INTERNACIONAIS QUE SE APLICAREM AO PREVIEW. ESSAS LEIS INCLUEM RESTRIÇÕES SOBRE DESTINOS, USUÁRIOS FINAIS E USO FINAL. - -14.6 O Contrato de licença não pode ser atribuído nem transferido por você sem a aprovação prévia por escrito da Google. Qualquer tentativa de atribuição sem a aprovação será inválida. Você não deve delegar as próprias responsabilidades ou obrigações nos termos do Contrato de licença sem aprovação prévia por escrito da Google. - -14.7 O Contrato de licença e sua relação com a Google nos termos do contrato serão regidos pelas leis do estado da Califórnia sem considerar conflitos de disposições legais. Você e a Google concordam em se submeter à competência exclusiva dos tribunais localizados na comarca de Santa Clara, Califórnia, para dirimir quaisquer questões legais decorrentes do Contrato de licença. Não obstante a isso, você concorda que a Google continua habilitada a impetrar medidas cautelares (ou mecanismo legal urgente equivalente) em qualquer jurisdição. - - -
\ No newline at end of file diff --git a/docs/html-intl/intl/pt-br/preview/overview.jd b/docs/html-intl/intl/pt-br/preview/overview.jd deleted file mode 100644 index e81ccd7916e9f10ad7225ec1fed184914b5c05a4..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/overview.jd +++ /dev/null @@ -1,389 +0,0 @@ -page.title=Visão geral do programa -page.metaDescription=Boas-vindas ao Android M Developer Preview, um programa que fornece tudo que é necessário para testar e otimizar os aplicativos para a próxima versão do Android. -page.image=images/cards/card-preview_16-9_2x.png -page.tags="preview", "developer", "android" - -@jd:body - -
-

- Developer Preview 2 is now available -

- - -
- -

- Boas-vindas ao Android M Developer Preview, um programa que fornece - tudo que é necessário para testar e otimizar os aplicativos para a próxima versão - do Android. É de graça e você pode começar agora mesmo: basta fazer - o download das ferramentas M Developer Preview. -

- -
-
-
-
-
- Imagens de sistema de emulador e hardware -
- -

- Execute e teste os aplicativos no Nexus 5, 6, 9 e Player (para TV), bem como - em emuladores. -

-
- -
-
- Código de plataforma mais recente -
- -

- Nós forneceremos várias atualizações durante a prévia. Portanto, você - testará de acordo com as alterações mais recentes da plataforma. -

-
- -
-
- Atualizações entregues por OTA -
- -

- É possível obter atualizações por OTA (over-the-air) ao programar o dispositivo - em flash para a prévia inicial. -

-
-
- -
- - -
-
- Novos comportamentos e capacidades -
- -

- Inicie o trabalho cedo para suportar os novos comportamentos da plataforma, - como novo modelo de permissões de tempo de execução e recursos de economia de energia. -

-
- -
-
- Janela de prioridades para problemas informados por desenvolvedores -
- -

- Durante as primeiras semanas, nós daremos prioridade aos problemas informados - por desenvolvedores. Portanto, teste e forneça-nos feedback o quanto antes. -

-
- -
-
- Feedback e suporte -
- -

- Informe problemas e dê-nos feedback usando o issue tracker. - Conecte-se a outros desenvolvedores na Comunidade M Developer. - -

-
-
-
-
- - - - -

- Linha do tempo e atualizações -

-Preview program timeline -

- O M Developer Preview estará em execução de 28 de maio até o Android M SDK final, que - disponibilizaremos brevemente antes do lançamento público durante - o 3º trimestre de 2015. -

- -

- Nos marcos de desenvolvimento principais, entregamos atualizações para os dispositivos de teste. - Os marcos de experiência são -

- -
    -
  • - Preview 1 (lançamento inicial do Preview, final de maio), -
  • - -
  • - Preview 2 (final de junho/início de julho) e -
  • - -
  • - Preview 3 (próximo ao final de julho) -
  • -
- -

- Estas atualizações culminam no SDK final (no 3º trimestre), - que fornecerá APIs oficiais para a nova versão do Android, - bem como os recursos e comportamentos do sistema final. -

- -

- Ao testar e desenvolver no Android M, recomendamos que você mantenha - o ambiente de desenvolvimento atualizado à medida que atualizações do Preview são lançadas. - Para facilitar o processo, fornecemos atualizações "over-the-air" - (OTA) aos dispositivos já programados em flash para uma versão do Preview, além - de fornecemos imagens do sistema que estão disponíveis para download e programação em flash manualmente. -

-

- Observação: as imagens do sistema e o SDK final não podem ser entregues - por OTA. Em vez disso, precisarão ser programadas em flash manualmente para - os dispositivos de teste. -

- -

- Notificaremos você quando as atualizações do Preview estiverem disponíveis por meio do Blogue de desenvolvedores do Android, além - deste site - e da Comunidade Android M Developer. -

- -

- O que há no Preview? -

- -

- O M Developer Preview inclui tudo que é necessário para testar os aplicativos existentes - em uma variedade de tamanhos de tela, tecnologias de rede, chipsets de CPU/GPU - e arquiteturas de hardware. -

- -

- Ferramentas SDK -

- -

- É possível fazer o download destes componentes pelo SDK Manager no Android Studio: -

- -
    -
  • M Developer Preview ferramentas SDK -
  • - -
  • M Developer Preview imagem do sistema de emulador (32 bits -e 64 bits) -
  • - -
  • M Developer Preview imagem do sistema de emulador para Android TV - (32 bits) -
  • -
- -

- Imagens do sistema de hardware -

- -

- É possível fazer o download destas imagens de sistema de hardware para dispositivos Nexus a partir - da página de download: -

- -
    -
  • - Nexus 5 (GSM/LTE) imagem do sistema de dispositivo “hammerhead” -
  • - -
  • - Nexus 6 imagem do sistema de dispositivo “shamu” -
  • - -
  • - Nexus 9 (Wi-Fi) imagem do sistema de dispositivo “volantis” -
  • - -
  • - Nexus Player (Android TV) imagem do sistema de dispositivo “fugu” -
  • -
- -

- Exemplo de código e documentação -

- -

- Estes recursos de documentação ajudam você a aprender sobre o Preview: -

- - - -

- Recursos de suporte -

- -

- Use estes recursos de suporte ao testar e desenvolver no - M Developer Preview: -

- -
    -
  • O Issue Tracker do M - Developer Preview é o canal principal - de feedback. É possível informar erros, problemas de desempenho e feedback - geral pelo issue tracker. Também é possível verificar os erros conhecidos - e encontrar etapas de resolução. -
  • - -
  • A Comunidade Android M Developer - é uma comunidade do Google+ onde é possível se conectar - a outros desenvolvedores que trabalham com o Android M. É possível compartilhar - observações ou ideias, além de encontrar respostas para as dúvidas sobre o Android M. -
  • -
- - -

- Destinação, APIs de prévia e publicação -

- -

- O Android M Developer Preview é uma versão apenas para desenvolvimento - e não possui um nível da API padrão. Caso opte - pelos comportamentos de compatibilidade para testar o aplicativo (o que é muito recomendado), - é possível destinar o M Developer Preview configurando o targetSdkVersion - do aplicativo para “MNC”. -

- -

- O Android M Developer Preview fornece APIs de prévia - — as APIs não serão oficiais até o lançamento do SDK final, - atualmente planejado para o terceiro trimestre de 2015. Isto significa que é possível - esperar alterações secundárias de APIs com o tempo, especialmente - durante as semanas iniciais do programa. Forneceremos um resumo das alterações - com cada atualização do Android M Developer Preview. -

- -

- Observe que, apesar das APIs da prévia poderem ser alteradas, os comportamentos essenciais do sistema, - como permissões de tempo de execução e recursos de economia de energia, são estáveis e estão prontos - para serem testados. -

- -

- Em termos de publicação, o Google Play evita a publicação de aplicativos - destinados para o M Developer Preview. Quando o SDK final do Android M estiver - disponível, você poderá destinar o nível da API do Android M oficial - e publicar o aplicativo no Google Play. Enquanto isso, caso queira distribuir um aplicativo - destinado para Android M para testadores, é possível fazê-lo por e-mail ou por download direto - a partir do site. -

- -

- Como começar -

- -

- Para começar o teste do aplicativo: -

- -
    -
  1. Revise a Visão geral da API - e as Mudanças de comportamento para - ter uma ideia do que há de novo e como isto afeta os aplicativos. Em particular, aprenda mais sobre - o novo modelode permissões - de tempo de execução, recursos de economia de energia e backup automático. -
  2. - -
  3. Configure o ambiente seguindo as instruções para - Configurar o Preview SDK - e ajustar os dispositivos de teste. -
  4. - -
  5. Siga - as instruções de programação em flash para programar em flash a imagem do sistema do M Developer Preview mais recente - para Nexus 5, 6, 9 e Player. Após programar em flash o dispositivo de desenvolvimento, - as atualizações do Preview serão entregues por atualizações OTA (over-the-air). -
  6. - -
  7. Faça o download da Referência da API do - M Preview e dos exemplos do M Preview - para obter mais informações sobre os novos recursos de API e como usá-los - no aplicativo. -
  8. - -
  9. Junte-se à Comunidade Android M - Developer para obter as notícias mais recentes e conecte-se a outros - desenvolvedores que trabalham com a nova plataforma. -
  10. -
- -

- Agradecemos a sua participação no programa M Developer Preview do Android! -

diff --git a/docs/html-intl/intl/pt-br/preview/samples.jd b/docs/html-intl/intl/pt-br/preview/samples.jd deleted file mode 100644 index a6648376e251a776c86d8ae45dcd92e5d2a14ce6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/samples.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Exemplos -page.image=images/cards/samples-new_2x.png -@jd:body - -

- Os seguintes exemplos de código são fornecidos para o M Developer Preview. Para fazer o download - dos exemplos no Android Studio, selecione a opção do menu File (Arquivo) > Import Samples (Importar exemplos). -

- -

- Observação: estes projetos disponíveis para download foram feitos -para serem usados com Gradle e Android Studio. -

- - -

Permissões em tempo de execução

- -

- O Android M altera a maneira como as permissões do sistema funcionam. Os usuários são solicitados a aprovar - as permissões em tempo de execução em vez de aprovar durante a instalação. Este exemplo mostra como solicitar - essas permissões. -

- -

Obtenha isto no GitHub

- -

Confirmação de credencial

- -

- Este exemplo demonstra como usar as credenciais do dispositivo como um método de autenticação no aplicativo. -

- -

Obtenha isto -no GitHub

- -

Diálogo de impressão digital

- -

- Este exemplo demonstra como reconhecer as impressões digitais registradas para autenticar o usuário - no aplicativo. -

- -

Obtenha isto no GitHub

- -

Backup automático para aplicativos

- -

- O Android M introduz o backup automático para as configurações de aplicativos. Este exemplo demonstra como adicionar - regras de filtro a um aplicativo para gerenciar o backup de configurações. -

- -

Obtenha isto no GitHub

- -

Câmera 2 Bruta

- -

- Demonstra como usar a API Camera2 API para capturar buffers de câmera RAW e salvá-los - como arquivos DNG. -

- -

Obtenha isto no GitHub

- -

Notificação ativa

- -

- Este exemplo demonstra como o - NotificationManager - pode dizer quantas notificações o aplicativo está exibindo. -

- -

Obtenha isto no GitHub

diff --git a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd b/docs/html-intl/intl/pt-br/preview/setup-sdk.jd deleted file mode 100644 index 894fe430c9800b7e45d6629e493f9673157e0ac0..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/setup-sdk.jd +++ /dev/null @@ -1,207 +0,0 @@ -page.title=Configuração do Preview SDK -page.image=images/cards/card-set-up_16-9_2x.png - -@jd:body - - - - -

O M Developer Preview SDK está disponível a partir do Android SDK Manager. -Este documento assume que você está familiarizado com o desenvolvimento de aplicativos do Android, -como o uso do Android SDK Manager e criação de projetos. Caso seja novo no -Android, consulte a lição de treinamento Como criar o primeiro -aplicativo primeiro.

- -

Obter o Android Studio 1.3

- -

A prévia de desenvolvedor é melhor usada com o Android Studio 1.3, que está no estado -de prévia. É altamente recomendado que você instale a versão de prévia -do Android Studio 1.3 para que funcione com o Preview SDK.

- -

Cuidado: a prévia canário do Android -Studio 1.3 ainda está em desenvolvimento ativo. Caso esteja usando a máquina de desenvolvimento principal -para testar a prévia de desenvolvedor, é possível criar uma segunda instalação -do Android Studio para usar em testes.

- -

Para instalar a prévia do Android Studio 1.3:

- -
    -
  1. Faça o download e inicie o Android - Studio. -
  2. - -
  3. Abra a janela Settings (Configurações) (no Windows, é possível fazer isto - escolhendo File (Arquivo) > Settings (Configurações)). Escolha o painel - Appearance & Behavior (Aparência e comportamento) > System - Settings (Configurações do sistema) > Updates (Atualizações). - -

    No OSX, é possível encontrar o painel Appearance & - Behavior (Aparência e comportamento) - na janela Preferences (Preferências) do Android Studio.

    -
  4. - -
  5. No painel Updates (Atualizações), escolha a opção - Automatically check updates for: Canary Channel (Verificar atualizações automaticamente para: canal canário). -
  6. - -
  7. No painel Updates (Atualizações), selecione Check Now (Verificar agora) - para verificar a versão mais recente da versão canário. Faça o download e instale a versão - quando solicitado. -
  8. -
- -

Obter o Preview SDK

- -

Para adicionar os componentes do Preview SDK ao ambiente de desenvolvimento:

- -
    -
  1. Inicie a prévia do Android Studio 1.3. -
  2. - -
  3. Abra a janela Settings (Configurações) (no Windows, é possível fazer isto - escolhendo File (Arquivo) > Settings (Configurações)). Escolha o painel - Appearance & Behavior (Aparência e comportamento) > System - Settings (Configurações do sistema) > Updates (Atualizações). - -

    No OSX, é possível encontrar o painel Appearance & - Behavior (Aparência e comportamento) - na janela Preferences (Preferências) do Android Studio.

    -
  4. - -
  5. No painel Updates (Atualizações), escolha a opção - Automatically check updates for: Canary Channel (Verificar atualizações automaticamente para: canal canário) e - Automatically check atualização for Android SDK: Preview Channel (Verificar atualizações automaticamente para: canal de pré-visualização). -
  6. - -
  7. Inicie o Android SDK Manager. (Com o Android Studio 1.3, - o SDK Manager está integrado no Android Studio, em vez de ser - um aplicativo independente.) -
  8. - -
  9. Na seção Platforms (Plataformas), selecione Android MNC - Preview. -
  10. - -
  11. Na seção Tools (Ferramentas), selecione o Android -SDK Tools, Platform-tools e -Build-tools mais recentes. -
  12. - -
  13. Clique em Install packages (Instalar pacotes) e aceite o contrato de licença - para todos os pacotes. -
  14. - -
  15. Verifique se o M Developer Preview está instalado abrindo a janela -Settings (Configurações) e escolhendo o painel Appearance & Behavior (Aparência e comportamento) -> System Settings (Configurações do sistema) > Android SDK.
  16. - -
  17. No painel Android SDK, escolha - a guia SDK Platforms (Plataformas SDK). Android MNC - Preview deve estar listado como Installed (Instalado). Além disso, abra a guia - SDK Tools (Ferramentas SDK) para verificar se as ferramentas mais recentes - estão instaladas. -
  18. -
-

Após concluir estas etapas, os componentes de prévia estarão disponíveis - no ambiente de desenvolvimento.

- - -

Criar ou atualizar um projeto

- -

- Para usar as APIs de prévia, deve-se criar ou atualizar um projeto de desenvolvimento para usar - os componentes de prévia. -

- - -

Criar um novo projeto

- -

- Recomendamos o uso do Android Studio para criar um projeto com a prévia. Siga as etapas - descritas em Criar um projeto - até chegar na tela Form Factors (Novas especificações) no assistente do projeto. Em seguida, - realize as seguintes etapas para criar um projeto configurado para a prévia. -

- -
    -
  • Verifique Phone and Tablet (Telefone e tablet).
  • -
  • Selecione MNC: Android M (Preview) em Minimum - SDK (SDK mínimo).
  • -
- - -

Atualizar um projeto existente

- -

- Para projetos existentes, deve-se modificar a configuração de projeto para ativar as APIs de prévia. No - ambiente de desenvolvimento, abra o arquivo build.gradle para o módulo - e defina estes valores da seguinte forma: -

- -
    -
  • compileSdkVersion para 'android-MNC'
  • -
  • minSdkVersion para 'MNC'
  • -
  • targetSdkVersion para 'MNC'
  • -
- - -

Configurar para teste

- -

- Testar um aplicativo com a prévia requer que você tenha um dispositivo ou dispositivo virtual configurado - com a versão de prévia da plataforma. Caso tenha um dispositivo compatível, é possível instalar a plataforma - de prévia para teste. Caso contrário, é possível configurar um dispositivo virtual para o teste. -

- -

Configurar um dispositivo físico

- -

- Caso tenha um Nexus 5, Nexus 6, Nexus 9 ou Android TV, é possível instalar uma imagem do sistema - de prévia nestes dispositivos para testar o aplicativo. - É possível configurar um dispositivo virtual com a versão de prévia da plataforma a partir do Android Studio - usando a ferramenta Android Virtual Device Manager. -

- -

- Importante: instalar uma imagem de prévia em um dispositivo remove todos os dados - dele. Portanto, deve-se realizar o backup de quaisquer dados antes de instalar uma imagem de prévia. -

- -

Configurar um dispositivo virtual

- -

- É possível configurar um dispositivo virtual com a versão de prévia da plataforma dentro do Android Studio - usando a ferramenta Android Virtual Device Manager. -

- -

Para criar um AVD com o AVD Manager:

- -
    -
  1. Instale o Preview SDK no ambiente de desenvolvimento, como descrito - em Configurar o Preview - SDK.
  2. -
  3. Siga as etapas em - Como gerenciar AVDs com - o Gerenciador de AVD. Use as seguintes configurações: -
      -
    • Dispositivo: Nexus 5, Nexus 6, Nexus 9 ou Android TV
    • -
    • Alvo: - Android M (Preview) - Nível da API M
    • -
    • ABI: x86
    • -
    -
  4. -
- -

- Para obter mais informações sobre como criar dispositivos virtuais para teste, consulte Gerenciamento de dispositivos virtuais. -

diff --git a/docs/html-intl/intl/pt-br/preview/testing/guide.jd b/docs/html-intl/intl/pt-br/preview/testing/guide.jd deleted file mode 100644 index b995f8baaf45f9c5da395472474857759b9ab304..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/testing/guide.jd +++ /dev/null @@ -1,187 +0,0 @@ -page.title=Guia de teste -page.image=images/cards/card-build_16x9_2x.png -page.keywords=previewresources,androidm,testing,permissions - -@jd:body - - - -

- O Android M Developer Preview fornece uma oportunidade de garantir que os aplicativos funcionem - na próxima versão da plataforma. Esta prévia inclui um número de mudanças de comportamento e APIs que podem - ter impacto no aplicativo, como descrito em Visão geral da API - e Mudanças de comportamento. No teste - do aplicativo com a prévia, há algumas alterações de sistema específicas em que você deve se concentrar - para garantir que os usuários tenham uma boa experiência. -

- -

- Este guia descreve quais recursos de prévia testar e como testá-los com o aplicativo. Você deve - priorizar o teste destes recursos de prévia específicos, devido ao grande impacto potencial no - comportamento do aplicativo: -

- - - -

- Para obter mais informações sobre como configurar dispositivos físicos ou virtuais com uma imagem do sistema de prévia - para teste, consulte Configuração do Preview SDK. -

- - -

Teste de permissões

- -

- O novo modelo de permissões - altera a maneira que as permissões são alocadas ao aplicativo pelo usuário. Em vez de conceder todas as permissões - durante o procedimento de instalação, o aplicativo deve pedir ao usuário permissões individuais - em tempo de execução. Para os usuários, este comportamento fornece um controle mais granular sobre as atividades de cada aplicativo, bem - como um melhor contexto para entender o porquê do aplicativo estar solicitando uma permissão específica. Os usuários - podem conceder ou revogar as permissões concedidas a um aplicativo individualmente a qualquer momento. É provável que este recurso - da prévia tenha um impacto no comportamento do aplicativo e pode impedir que alguns - dos recursos do aplicativo funcionem, ou funcionem em um estado degradado. -

- -

- Esta alteração afeta todos os aplicativos em execução na nova plataforma, mesmo aqueles que não são destinados - para a versão nova da plataforma. A plataforma fornece um comportamento de compatibilidade limitado para aplicativos legados. No entanto, - você deve começar a planejar a migração do aplicativo para o novo modelo de permissões agora, com o objetivo - de publicar uma versão atualizada do aplicativo no lançamento oficial da plataforma. -

- - -

Dicas de teste

- -

- Use as seguintes dicas de teste para ajudar você a planejar e executar os testes do aplicativo com o novo - comportamento de permissões. -

- -
    -
  • Identifique as permissões atuais do aplicativo e os caminhos de código relacionados.
  • -
  • Teste o fluxo de usuário entre serviços protegidos por permissão e dados.
  • -
  • Teste com várias combinações de permissões revogadas/concedidas.
  • -
  • Use a ferramenta {@code adb} para gerenciar as permissões da linha de comando: -
      -
    • Liste as permissões e o status por grupos: -
      adb shell pm list permissions -d -g
      -
    • -
    • Conceda ou revogue uma ou mais permissões usando a seguinte sintaxe:
      -
      adb shell pm [grant|revoke] <permission.name> ...
      -
    • -
    -
  • -
  • Analise o aplicativo para encontrar os serviços que usam permissões.
  • -
- -

Estratégia de teste

- -

- A mudança de permissões afeta a estrutura e o projeto do aplicativo, bem como - a experiência dos usuários e os fluxos fornecidos a eles. Você deve avaliar o uso das permissões atuais - do aplicativo e começar a planejar novos fluxos que deseja oferecer. O lançamento oficial - da plataforma fornece comportamento de compatibilidade, mas deve-se planejar a atualização do aplicativo e - não confiar nestes comportamentos. -

- -

- Identifique as permissões que o aplicativo realmente precisa e usa e, em seguida, encontre os vários caminhos - de código que usam os serviços protegidos por permissões. É possível fazer isto por meio de uma combinação de - testes na nova plataforma e análise de códigos. Nos testes, você deve se concentrar em usar - as permissões em tempo de execução alterando {@code targetSdkVersion} do aplicativo para a versão da prévia. Para - obter mais informações, consulte Configuração do Preview SDK. -

- -

- Teste com várias combinações de permissões revogadas e concedidas para destacar os fluxos de usuário -que dependem de permissões. Onde uma dependência não for óbvia ou lógica, considere -refatorar ou compartimentalizar este fluxo para eliminar a dependência ou para esclarecer por que -a permissão é necessária. -

- -

- Para obter mais informações sobre o comportamento das permissões em tempo de execução, de testes e de melhores práticas, consulte a página - Permissões do Developer - Preview. -

- - -

Teste de soneca e aplicativo em espera

- -

- Os recursos de economia de energia de aplicativo em espera e soneca limitam a quantidade de processamento de segundo plano que o aplicativo - pode realizar quando um dispositivo está no estado ocioso ou enquanto não está em foco. As - restrições que o sistema pode impor nos aplicativos inclui acesso a rede limitado ou restrito, - tarefas de segundo plano suspensas, notificações suspensas, solicitações de soneca ignoradas e despertadores. Para garantir - que o aplicativo se comportará adequadamente com essas otimizações de economia de energia, deve-se testá-lo - simulando estes estados de baixa energia. -

- -

Testar o aplicativo com Soneca

- -

Para testar a Soneca com o aplicativo:

- -
    -
  1. Configure um dispositivo de hardware ou virtual com uma imagem do sistema M Preview.
  2. -
  3. Conecte o dispositivo à máquina de desenvolvimento e instale o aplicativo.
  4. -
  5. Execute o aplicativo e deixe-o ativo.
  6. -
  7. Simule o dispositivo acessando o modo Soneca executando os seguintes comandos: - -
    -$ adb shell dumpsys battery unplug
    -$ adb shell dumpsys deviceidle step
    -$ adb shell dumpsys deviceidle -h
    -
    - -
  8. -
  9. Observe o comportamento do aplicativo quando o dispositivo é reativado. Certifique-se de que - ele se recupere corretamente quando o dispositivo sai do modo Soneca.
  10. -
- - -

Testar o aplicativo no modo de espera

- -

Para testar o modo de espera do aplicativo:

- -
    -
  1. Configure um dispositivo de hardware ou virtual com uma imagem do sistema M Preview.
  2. -
  3. Conecte o dispositivo à máquina de desenvolvimento e instale o aplicativo.
  4. -
  5. Execute o aplicativo e deixe-o ativo.
  6. -
  7. Simule o aplicativo acessando o modo de espera executando os seguintes comandos: - -
    -$ adb shell am broadcast -a android.os.action.DISCHARGING
    -$ adb shell am set-idle <packageName> true
    -
    - -
  8. -
  9. Simule o despertar do aplicativo usando o seguinte comando: -
    $ adb shell am set-idle <packageName> false
    -
  10. -
  11. Observe o comportamento do aplicativo quando ele é despertado. Certifique-se de que ele se recupere corretamente - do modo de espera. Particularmente, deve-se verificar se as notificações e os trabalho de segundo plano - do aplicativo continuam a funcionar como o esperado.
  12. -
- -

Backup automático para aplicativos e identificadores específicos do dispositivo

- -

Caso o aplicativo esteja persistindo qualquer identificador específico do dispositivo, como o ID de registro do Google -Cloud Messaging, no armazenamento interno, -certifique-se de seguir as práticas recomendadas para excluir o local de armazenamento -do backup automático, como descrito em Backup automático -para aplicativos.

diff --git a/docs/html-intl/intl/pt-br/preview/testing/performance.jd b/docs/html-intl/intl/pt-br/preview/testing/performance.jd deleted file mode 100644 index d541be11c0a7542cfc858f0edcf8f9ba686b9454..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/pt-br/preview/testing/performance.jd +++ /dev/null @@ -1,656 +0,0 @@ -page.title=Teste de desempenho de exibição -page.image=images/cards/card-test-performance_2x.png -page.keywords=performance, fps, tools - -@jd:body - - - - - -

- O teste de desempenho da interface do usuário (IU) garante que o aplicativo - não só esteja de acordo com os requisitos funcionais, como também que as interações de usuários - sejam muito mais suaves, executando a 60 quadros por segundo de forma consistente (por que - 60 qps?), sem nenhum quadro atrasado ou descartado ou, como gostamos de chamar, "jank". Este documento explica as ferramentas disponíveis - para medir o desempenho da IU e dispõe uma abordagem para integrar - as medidas de desempenho de IU nas práticas de teste. -

- - -

Medir desempenho da IU

- -

- Para aprimorar o desempenho, deve-se primeiro ter a habilidade de medir o desempenho - do sistema e, em seguida, diagnosticar e identificar problemas que podem ocorrer - em várias partes do processo. -

- -

- dumpsys é uma ferramenta - do Android que é executada no dispositivo e despeja informações interessantes sobre o estado - dos serviços do sistema. Passar o comando gfxinfo para dumpsys fornece uma saída no logcat - com informações de desempenho relacionada aos quadros de animação que ocorrem - durante a fase de registro. -

- -
-> adb shell dumpsys gfxinfo <PACKAGE_NAME>
-
- -

- Este comando pode produzir diversas variáveis de dados de precisão de quadro. -

- -

Agregar estatísticas de quadro

- -

- Com o M Preview, o comando emite uma análise agregada dos dados de quadro para logcat, - coletados em todo o ciclo de vida do processo. Por exemplo: -

- -
-Stats since: 752958278148ns
-Total frames rendered: 82189
-Janky frames: 35335 (42.99%)
-90th percentile: 34ms
-95th percentile: 42ms
-99th percentile: 69ms
-Number Missed Vsync: 4706
-Number High input latency: 142
-Number Slow UI thread: 17270
-Number Slow bitmap uploads: 1542
-Number Slow draw: 23342
-
- -

- Estas estatísticas de alto nível carregam um alto nível de desempenho de renderização do aplicativo, - bem como a estabilidade em vários quadros. -

- - -

Informações de precisão de quadro

- -

- Com o M Preview, há um novo comando para gfxinfo, o framestats, que fornece - informações de precisão de quadros extremamente detalhadas dos quadros recentes para que você possa rastrear - e depurar os problemas de forma mais precisa. -

- -
->adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
-
- -

- Este comando emite informações de precisão de quadros, com marcações de data e hora, dos últimos 120 - quadros produzidos pelo aplicativo. Abaixo, há um exemplo de saída bruta das estatísticas - de quadro de adb dumpsys gfxinfo <PACKAGE_NAME>: -

- -
-0,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954,
-0,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120,
-0,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527,
-0,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683,
-
- -

- Cada linha desta saída representa um quadro produzido pelo aplicativo. Cada linha tem um número fixo - de colunas que descrevem o tempo gasto em cada estágio do pipeline que produz quadros. A próxima seção descreve - este formato com detalhes, incluindo o que cada coluna representa. -

- - -

Formato de dados de estatísticas de quadro

- -

- Como o bloco de dados é a saída no formato CSV, basta colá-lo na ferramenta - de planilha de sua escolha ou coletá-lo e analisá-lo com o script. A tabela a seguir explica o formato - das colunas de dados de saída. Todas as marcações de data e hora estão em nanossegundos. -

- -
    -
  • SINALIZADORES -
      -
    • Linhas com ‘0’ para a coluna FLAGS podem ter o tempo total de quadros calculado - subtraindo a coluna INTENDED_VSYNC da coluna FRAME_COMPLETED. -
    • - -
    • Se for um número diferente de zero, a linha deverá ser ignorada, pois o quadro será determinado - como exceção em comparação ao desempenho normal, onde espera-se que o layout e o desenho - demorem mais do que 16 ms. Eis alguns motivos para que isto ocorra: -
        -
      • Layout da janela alterado (como o primeiro quadro do aplicativo - ou após uma rotação) -
      • - -
      • Também é possível que o quadro seja ignorado, quando alguns valores - ainda terão marcações de data e hora de lixo. Um quadro pode ser ignorado se, por exemplo, - estiver executando a 60 quadros por segundo ou se nada na tela estiver incorreto, o que não necessariamente - é um sinal de problema no aplicativo. -
      • -
      -
    • -
    -
  • - -
  • INTENDED_VSYNC -
      -
    • O ponto inicial planejado para o quadro. Se este valor for diferente de VSYNC, - significa que há um trabalho ocorrendo no encadeamento da IU que não permitiu que ele respondesse - ao sinal de vsync de forma precisa. -
    • -
    -
  • - -
  • VSYNC -
      -
    • O valor de tempo foi usado em todos os escutadores vsync e no desenho para o quadro - (retorno de chamada do quadro Choreographer, animações, View.getDrawingTime(), etc.) -
    • - -
    • Para entender mais sobre VSYNC e como ele influencia o aplicativo, assista ao vídeo - -Entendimento do VSYNC. -
    • -
    -
  • - -
  • OLDEST_INPUT_EVENT -
      -
    • A marcação de data e hora do evento de entrada mais antigo na fila de entrada, ou Long.MAX_VALUE - se não houver eventos de entrada para o quadro. -
    • - -
    • Este valor é principalmente planejado para o trabalho da plataforma e tem utilidade limitada - para desenvolvedores de aplicativos. -
    • -
    -
  • - -
  • NEWEST_INPUT_EVENT -
      -
    • A marcação de data e hora do evento de entrada mais recente na fila de entrada - ou 0 se não houver eventos de entrada para o quadro. -
    • - -
    • Este valor é principalmente planejado para o trabalho da plataforma e tem utilidade limitada - para desenvolvedores de aplicativos. -
    • - -
    • No entanto, é possível ter uma breve ideia da quantidade de latência que o aplicativo - está adicionado verificando (FRAME_COMPLETED - NEWEST_INPUT_EVENT). -
    • -
    -
  • - -
  • HANDLE_INPUT_START -
      -
    • A marcação de data e hora em que os eventos de entrada foram despachados para o aplicativo. -
    • - -
    • Ao olhar o horário entre isto e ANIMATION_START, é possível medir o tempo que o aplicativo - gastou para lidar com os eventos de entrada. -
    • - -
    • Se este número for alto (> 2 ms), indica que o aplicativo está gastando - tempo demais processando os eventos de entrada, como View.onTouchEvent(), o que pode indicar - que este trabalho precisa ser otimizado ou descarregado para um encadeamento diferente. Observe que há algumas situações, - como eventos de clique que iniciam novas atividades, - em que é esperado e aceitável que este número seja grande. -
    • -
    -
  • - -
  • ANIMATION_START -
      -
    • A marcação de data e hora em que as animações registradas com Choreographer foram executadas. -
    • - -
    • Ao olhar para o tempo entre isto e PERFORM_TRANVERSALS_START, - é possível determinar o tempo levado para avaliar todos os animadores - (ObjectAnimator, ViewPropertyAnimator e Transitions sendo as mais comuns) que estão sendo executados. -
    • - -
    • Se este número for alto (> 2 ms), verifique se o aplicativo gravou qualquer animador - personalizado ou quais campos de ObjectAnimators estão animando - e certifique-se de que eles sejam adequados para uma animação. -
    • - -
    • Para saber mais sobre Choreographer, assista ao vídeo - Para melhor ou pior. -
    • -
    -
  • - -
  • PERFORM_TRAVERSALS_START -
      -
    • Se você subtrair o DRAW_START deste valor, será possível extrair o tempo que as fases de medida e layout - levaram para serem concluídas (observação: durante uma rolagem ou animação, - espera-se que este valor seja próximo a zero). -
    • - -
    • Para saber mais sobre as fases de medida e layout do pipeline de renderização, - assista ao vídeo -Invalidações, layouts e desempenho -
    • -
    -
  • - -
  • DRAW_START -
      -
    • O horário em que a fase de desenho de performTraversals foi iniciada. Este é o ponto inicial - do registro de listas de exibição de vistas que foram invalidadas. -
    • - -
    • O tempo entre isto e SYNC_START é o tempo levado para chamar View.draw() - em todas as vistas invalidadas na árvore. -
    • - -
    • Para obter mais informações sobre o modelo de desenho, assista aos vídeos Aceleração de hardware - ou - Invalidações, layouts e desempenho -
    • -
    -
  • - -
  • SYNC_START -
      -
    • O horário em que a fase de sincronização do desenho foi iniciada. -
    • - -
    • Se o tempo entre isto e ISSUE_DRAW_COMMANDS_START for substancial (aproximadamente > 0,4ms), - geralmente indicará que vários Bitmaps novos que foram desenhados deverão - ser enviados para o GPU. -
    • - -
    • Para entender mais sobre a fase de sincronização, assista ao vídeo -Renderização de GPU de perfil -
    • -
    -
  • - -
  • ISSUE_DRAW_COMMANDS_START -
      -
    • O horário em que o renderizador de hardware começou a emitir comandos de desenho para a GPU. -
    • - -
    • O tempo entre isto e FRAME_COMPLETED fornece uma breve ideia da quantidade de trabalho de GPU - que o aplicativo está produzindo. Problemas com excesso ou efeitos de renderização - ineficientes são exibidos aqui. -
    • -
    -
  • - -
  • SWAP_BUFFERS -
      -
    • O horário em que o eglSwapBuffers foi chamado, relativamente desinteressante - fora do trabalho da plataforma. -
    • -
    -
  • - -
  • FRAME_COMPLETED -
      -
    • Tudo feito! O tempo total gasto trabalhando neste quadro pode ser calculando - realizando FRAME_COMPLETED - INTENDED_VSYNC. -
    • -
    -
  • - -
- -

- É possível usar estes dados de várias formas. Uma visualização simples mas útil é o histograma - exibindo a distribuição dos tempos dos quadros (FRAME_COMPLETED - INTENDED_VSYNC) - em diferentes espaços de latência. Consulte a figura abaixo. Este gráfico mostra brevemente que a maioria - dos quadros estavam bons — bem abaixo do limite de 16 ms (representado em vermelho) —, - mas que alguns quadros estavam bem acima do limite. Podemos verificar as alterações neste histograma - com o tempo para encontrar as mudanças indiscriminadas ou novas exceções sendo criadas. Também é possível colocar em gráfico a latência de entrada, - o tempo gasto no layout ou outras medidas interessantes com base - nas várias marcações de data e hora nos dados. -

- - - - -

Despejo de precisão de quadro simples

- -

- Se a renderização de GPU de perfil for definida para em adb shell dumpsys gfxinfo - nas opções de desenvolvedor, o comando adb shell dumpsys gfxinfo emitirá informações - de precisão para os 120 quadros mais recentes, divididas em algumas categorias - com valores separados por guias. Estes dados podem ser úteis para indicar quais partes do pipeline - de desenho podem estar lentas em um nível alto. -

- -

- Semelhante às estatísticas de quadro acima, - basta colá-los na ferramenta de planilha de sua escolha ou coletá-los e analisá-los - com um script. O gráfico a seguir exibe um detalhamento de onde os vários quadros produzidos - pelo aplicativo gastaram o tempo. -

- - - -

- O resultado de executar gfxinfo, copiar a saída, colá-lo no aplicativo de planilha - e gerar um gráfico dos dados como barras empilhadas. -

- -

- Cada barra vertical representa um quadro da animação; sua altura representa a quantidade - de milissegundos para calcular este quadro de animação. Cada segmento colorido da barra - representa um estágio diferente do pipeline de renderização para que você possa ver que partes do aplicativo - podem estar criando um afunilamento. Para obter mais informações sobre o entendimento do pipeline - de renderização e como otimizá-lo, assista ao vídeo -Invalidações, layouts e desempenho. -

- - -

Controlar janela de coleta de estatísticas

- -

- As precisões de quadro simples e estatísticas de quadro coletam dados - em um período muito curto — cerca de dois segundos de renderização. Para controlar este período de forma precisa — por exemplo, - restringir os dados para uma animação em particular —, é possível redefinir - todos os contadores e agregar as estatísticas coletadas. -

- -
->adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
-
- -

- Isto também pode ser usado em conjunto com os próprios comandos de despejo para coletar - e redefinir em uma cadência regular, capturando períodos de menos de dois segundos de quadros - continuamente. -

- - -

Diagnosticar regressões de desempenho

- -

- A identificação de regressões é uma boa primeira etapa para rastrear os problemas - e manter o bem-estar do aplicativo. No entanto, o dumpsys identifica apenas a existência - e a gravidade relativa dos problemas. Ainda é necessário diagnosticar a causa particular dos problemas - de desempenho e encontrar maneiras adequadas de resolvê-los. Para isso, é altamente recomendado - o uso da ferramenta systrace. -

- - -

Recursos adicionais

- -

- Para obter mais informações sobre como o pipeline de renderização do Android funciona, - problemas comuns que podem ser encontrados e como resolvê-los, - alguns dos seguintes recursos podem ser úteis: -

- -
    -
  • Desempenho de renderização 101 -
  • -
  • Por que 60 qps? -
  • -
  • GPU e IU do Android -
  • -
  • Invalidações, layouts e desempenho -
  • -
  • Análise do desempenho de IU com Systrace -
  • -
- - -

Automatizar teste de desempenho da IU

- -

- Uma abordagem para o teste de desempenho da IU é fazer com que um testador humano - realize uma série de operações de usuário no aplicativo-alvo e procure visualmente problemas - ou gaste uma grande quantidade de tempo usando uma abordagem de ferramenta para encontrá-los. No entanto, esta abordagem manual - é repleta de riscos: a habilidade humana de notar alterações na taxa de quadros varia tremendamente, - além de consumir tempo, ser tedioso e propenso a erros. -

- -

- Uma abordagem mais eficiente é registrar e analisar as métricas de desempenho essenciais - dos testes de IU automatizados. O Android M Developer Preview inclui novas capacidades de registro que facilitam - a determinação da quantidade e da gravidade de erros nas animações do aplicativo - e que podem ser usadas para compilar um processo rigoroso para determinar o desempenho atual - e rastrear os futuros objetivos de desempenho. -

- -

- Este artigo mostra uma abordagem recomendada para usar estes dados para automatizar - o teste de desempenho. -

- -

- Ele é geralmente dividido em duas ações principais. Primeiro: identificar o que está testando - e como será testado. Segundo: configuração e manutenção - de um ambiente de teste automatizado. -

- - -

Configurar de testes da IU

- -

- Antes de iniciar o teste automatizado, é importante determinar algumas decisões de alto nível - para entender corretamente o espaço de teste e as possíveis necessidades. -

- -

- Identificar principais animações/fluxos a testar -

- -

- Lembre-se que um desempenho ruim é mais visível aos usuários quando - interrompe uma animação suave. Portanto, ao identificar que tipos de ações de IU serão testadas, é útil se concentrar - nas animações principais que os usuários veem - ou nas mais importantes para a experiência. Por exemplo, eis alguns cenários comuns que pode ser útil identificar: -

- -
    -
  • Rolagem de um ListView ou RecyclerView principal -
  • - -
  • Animações durante ciclos de espera assíncrona -
  • - -
  • Qualquer animação que possa ter manipulação ou carregamento de bitmap -
  • - -
  • Animações com mistura alpha -
  • - -
  • Desenho de vista personalizada com Canvas -
  • -
- -

- Trabalhe com engenheiros, designers, e gerentes de produto em sua equipe para priorizar - as animações de produto essenciais para a cobertura de teste. -

- -

- Defina os futuros objetivos e faça um rastreamento -

- -

- De um alto nível, talvez seja essencial identificar os objetivos específicos de desempenho - e concentrar-se em escrever testes e coletar dados. Por exemplo: -

- -
    -
  • Quer apenas iniciar o rastreamento de desempenho de IU pela primeira vez para aprender mais? -
  • - -
  • Quer evitar regressões que possam ser introduzidas no futuro? -
  • - -
  • Está com 90% de quadros suaves hoje e deseja chegar a 98% neste trimestre? -
  • - -
  • Está com 98% de quadros suaves e não quer regredir? -
  • - -
  • O seu objetivo é aprimorar o desempenho em dispositivos de baixo nível? -
  • -
- -

- Em todos esses casos, você optará pelo rastreamento histórico, que exibe o desempenho - entre várias versões do aplicativo. -

- -

- Identificar dispositivos para realizar testes -

- -

- O desempenho do aplicativo varia dependendo do dispositivo em que está sendo executado. Alguns dispositivos podem - conter menos memória, GPUs menos eficientes ou chips de CPU mais lentos. Isto significa que as animações que podem - ter um bom desempenho em um conjunto de hardwares podem não ter o mesmo desempenho em outras, - podendo ser o resultado de um afunilamento em uma parte diferente do pipeline. Portanto, para contabilizar - esta variação em o que o usuário pode ver, escolha uma variação de dispositivos - para executar os testes: dispositivos e tablets de alto e baixo nível etc. Procure variações no desempenho de CPU, - RAM, densidade da tela, tamanho etc. Testes que passam em dispositivos de alto nível - podem falhar em dispositivos de baixo nível. -

- -

- Estruturas básicas para teste de IU -

- -

- Conjuntos de ferramenta, como UI Automator e - Espresso, - são integrados para ajudar na automatização da ação de um usuário movendo-se pelo aplicativo. São estruturas simples - que imitam a interação de usuário no dispositivo. Para usar estas estruturas, - você cria efetivamente scripts únicos que executam um conjunto - de ações de usuário e reproduzem-nas no próprio dispositivo. -

- -

- Ao combinar estes testes automatizados, juntamente com dumpsys gfxinfo, é possível criar - rapidamente um sistema reproduzível que permite a execução de um teste e a medição das informações - de desempenho desta condição em particular. -

- - -

Configurar testes automatizados da IU

- -

- Com a habilidade de executar um teste de IU e um pipeline para coletar - os dados de um único teste, a próxima etapa importante é adotar uma estrutura que pode executar - esse teste várias vezes, em vários dispositivos, e agregar os dados de desempenho resultados - para futuras análises da equipe de desenvolvimento. -

- -

- Uma estrutura para automatização de testes -

- -

- Vale observar que as estruturas de teste de IU (como o UI Automator) - são executadas diretamente no emulador/dispositivo alvo. Enquanto as informações de coleta de desempenho feita - pelo dumpsys gfxinfo forem direcionadas por uma máquina hospedeira, envie comandos pelo ADB. Para ajudar a transmitir - a automatização dessas entidades separadas, a estrutura MonkeyRunner - foi desenvolvida: um sistema de script que é executado na máquina host que pode - emitir comandos para um conjunto de dispositivos conectados, bem como receber dados deles. -

- -

- A compilação de um conjunto de scripts para uma automatização adequada de teste de desempenho de IU, - no mínimo, deve ser capaz de utilizar o MonkeyRunner para realizar as seguintes tarefas: -

- -
    -
  • Carregar e iniciar um APK desejado para um emulador ou dispositivo alvo. -
  • - -
  • Iniciar um teste de IU do UI Automator e permitir que ele seja executado. -
  • - -
  • Coletar informações de desempenho por meio de dumpsys gfxinfo. -
  • - -
  • Agregar informações e exibi-las de forma útil para o desenvolvedor. -
  • -
- - -

Triagem e resolução de problemas observados

- -

- Quando os padrões ou regressões dos problemas forem identificados, a próxima etapa - é identificar e aplicar a resolução. Se a estrutura de teste automatizado preservar o detalhamento preciso para os quadros, - ela poderá ajudar na inspeção de alterações de layout/código suspeitos recentes (em caso - de regressão) ou reduzir a parte do sistema que você está analisando ao alternar - para a investigação manual. Para a investigação manual, systrace é um ótimo lugar para começar, - exibindo as informações de precisão sobre cada estágio do pipeline de renderização, - cada encadeamento e núcleo no sistema, bem como quaisquer marcadores de evento personalizados definidos. -

- -

- Geração de perfis adequada de precisões temporais -

- -

- É importante observar as dificuldades em obter e medir as precisões - do desempenho de renderização. Esses números são, por natureza, não determinísticos e frequentemente - oscilam dependendo do estado do sistema, da quantidade de memória disponível, - da diminuição termal e da última vez em que a luz do sol atingiu a sua área da Terra. Ou seja, é possível executar - o mesmo teste duas vezes e receber números levemente diferentes - que podem ser muito próximos, mas não idênticos. -

- -

- A coleta e a geração de perfil de dados nesta maneira significa executar o mesmo teste, - várias vezes, e acumular os resultados como uma média ou valor mediano (para a simplicidade, - chamemos de "lote"). Isto fornece uma aproximação do desempenho do teste, - já que precisões exatas não são necessárias. -

- -

- Os lotes podem ser usados entre alterações de códigos para verificar o impacto relativo - dessas alterações no desempenho. Se a taxa de quadros média para o lote antes da alteração - for maior do que o lote após a alteração, então o resultado de desempenho wrt geral será um sucesso - para esta alteração em particular. -

- -

- Isto significa que qualquer teste de IU automatizado feito deve levar este conceito - em consideração, bem como quaisquer anomalias que possam ocorrer durante um teste. Por exemplo, - se o desempenho do aplicativo repentinamente cair devido a algum problema do dispositivo - (que não tenha sido causado pelo aplicativo), então talvez seja necessário executar - o lote novamente para obter precisões menos caóticas. -

- -

- Logo, quantas vezes deve-se realizar um teste antes de as medidas terem algum sentido? 10 vezes deve ser o mínimo, - com números altos como 50 ou 10 oferecendo resultados mais precisos - (é claro que se deve levar em consideração o tempo para ter mais precisão). -

diff --git a/docs/html-intl/intl/pt-br/sdk/index.jd b/docs/html-intl/intl/pt-br/sdk/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..c8f82927e71c88609d2023f33324925751db886f --- /dev/null +++ b/docs/html-intl/intl/pt-br/sdk/index.jd @@ -0,0 +1,430 @@ +page.title=Como baixar o Android Studio e o SDK Tools +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=Baixar o Android IDE e ferramentas do desenvolvedor para compilar aplicativos para celulares, tablets, dispositivos de uso junto ao corpo, TVs e muito mais do Android. + +@jd:body + + + + + + + +
+ + + + + + + + + +
+ +
 
+ + + +
+ +

Android Studio

+ +

O IDE oficial do Android

+ +
    +
  • IDE do Android Studio
  • +
  • Ferramentas do Android SDK
  • +
  • Plataforma do Android 6.0 (Marshmallow)
  • +
  • Imagem do sistema do emulador do Android 6.0 com APIs da Google
  • +
+ + +Download Android Studio
+ + +

+Para obter o Android Studio ou a versão independente das ferramentas SDK, acesse developer.android.com/sdk/ +

+
+ + + + + + +

Editor de código inteligente

+ +
+ +
+ +
+

No núcleo do Android Studio, há um editor de código inteligente capaz de realizar, +refatorar e analisar códigos avançados.

+

O poderoso editor de código auxilia na maior produtividade do desenvolvedor de aplicativos Android.

+
+ + + + + +

Modelos de códigos e integração GitHub

+ +
+ +
+ +
+

Os novos assistentes de projeto facilitam iniciar um projeto como nunca antes.

+ +

Inicie projetos usando códigos de modelo para padrões como menus de navegação e ViewPagers, +e até importe exemplos de código da Google a partir do GitHub.

+
+ + + + +

Desenvolvimento de aplicativos multitelas

+ +
+ +
+ +
+

Programe aplicativos para celulares Android, tablets, Android Wear, +Android TV, Android Auto e Google Glass.

+

Com a nova visualização de projeto Android e a compatibilidade com módulos no Android Studio, +é mais fácil gerenciar projetos e recursos de aplicativos. +

+ + + + +

Dispositivos virtuais para todas as formas e tamanhos

+ +
+ +
+ +
+

O Android Studio já é pré-configurado com uma imagem do emulador otimizada.

+

O Gerenciador de dispositivos virtual atualizado e simplificado fornece +perfis de dispositivos pré-definidos para dispositivos Android comuns.

+
+ + + + +

+As versões do Android evoluíram com o Gradle

+ +
+ +
+ +
+

Crie diversos APKs para seu aplicativo Android com diferentes recursos usando o mesmo projeto.

+

Gerencie dependências de aplicativo com o Maven.

+

Crie APKs com o Android Studio ou com a linha de comando.

+
+ + + + +

Saiba mais sobre o Android Studio

+
+ +Download + +
    +
  • Programado no IntelliJ IDEA Edição de Comunidade, o popular IDE da Java da JetBrains.
  • +
  • Sistema flexível de programação baseado em Gradle.
  • +
  • Crie variantes e várias gerações de APK.
  • +
  • Compatibilidade com modelos do Google Services ampliada e com diversos tipos de dispositivos.
  • +
  • Editor de layout completo compatível com edição de tema.
  • +
  • Ferramentas de identificação de construção suspeita para identificar problemas de desempenho, usabilidade, compatibilidade de versão e outros problemas.
  • +
  • ProGuard e recursos de assinatura de aplicativo.
  • +
  • Compatibilidade embutida na Google Cloud Platform, facilitando a integração com o Google Cloud +Messasing e com o App Engine.
  • +
+ +

+Para obter mais detalhes sobre recursos disponíveis no Android Studio, +leia o guia Conceitos básicos do Android Studio.

+
+ + +

Se você usou o Eclipse com ADT, esteja ciente de que o Android Studio é agora o IDE oficial +para Android, portanto, é preciso migrar para o Android Studio para receber todas +as últimas atualizações do IDE. Para obter ajuda para mover projetos, +consulte Como migrar para o Android +Studio.

+ + + + + + + +

Requisitos do sistema

+ +

Windows

+ +
    +
  • Microsoft® Windows® 8/7/Vista/2003 (32 ou 64 bits)
  • +
  • Mínimo de 2 GB de RAM, 4 GB de RAM recomendado
  • +
  • Espaço de 400 MB no disco rígido
  • +
  • Pelo menos 1 GB para o Android SDK, imagens do sistema de emulador e caches
  • +
  • Resolução de tela de 1.280 x 800 no mínimo
  • +
  • Kit de desenvolvimento Java (JDK) 7
  • +
  • Opcional para emulador acelerado: Processador Intel® compatível com Intel® VT-x, Intel® EM64T +(Intel® 64) e Desativador de bit executável (XD)
  • +
+ + +

Mac OS X

+ +
    +
  • Mac® OS X® 10.8.5 ou posterior, até o 10.9 (Mavericks)
  • +
  • Mínimo de 2 GB de RAM, 4 GB de RAM recomendado
  • +
  • Espaço de 400 MB no disco rígido
  • +
  • Pelo menos 1 GB para o Android SDK, imagens do sistema de emulador e caches
  • +
  • Resolução de tela de 1.280 x 800 no mínimo
  • +
  • Ambiente de tempo de execução Java (JRE) 6
  • +
  • Kit de desenvolvimento Java (JDK) 7
  • +
  • Opcional para emulador acelerado: Processador Intel® compatível com Intel® VT-x, Intel® EM64T +(Intel® 64) e Desativador de bit executável (XD)
  • +
+ +

No Mac OS, execute o Android Studio com o Ambiente de tempo de execução Java (JRE) 6 para otimizar +a renderização de fontes. Você pode, então, configurar o projeto para usar o Kit de desenvolvimento Java (JDK) 6 ou o JDK 7.

+ + + +

Linux

+ +
    +
  • Área de trabalho GNOME ou KDE
  • +
  • Biblioteca GNU C (glibc) 2.15 ou posterior
  • +
  • Mínimo de 2 GB de RAM, 4 GB de RAM recomendado
  • +
  • Espaço de 400 MB no disco rígido
  • +
  • Pelo menos 1 GB para o Android SDK, imagens do sistema de emulador e caches
  • +
  • Resolução de tela de 1.280 x 800 no mínimo
  • +
  • Kit de desenvolvimento Oracle® Java (JDK) 7
  • +
+

Testado no Trusty Tahr do Ubuntu® 14.04 (distribuição de 64 bits capaz de executar +aplicativos de 32 bits).

+ + + + +

Outras opções de download

+ + diff --git a/docs/html-intl/intl/pt-br/sdk/installing/adding-packages.jd b/docs/html-intl/intl/pt-br/sdk/installing/adding-packages.jd new file mode 100644 index 0000000000000000000000000000000000000000..bda33b666b99ed972545e8172c1651f11a0ca55d --- /dev/null +++ b/docs/html-intl/intl/pt-br/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=Como adicionar pacotes SDK + +page.tags=sdk manager + +@jd:body + + + + +

+Por padrão, o Android SDK não inclui tudo que é necessário para começar a desenvolver. +O SDK separa ferramentas, plataformas e outros componentes em pacotes que podem ser +baixados conforme necessário usando o +Android SDK Manager. +Portanto, antes de iniciar, há alguns pacotes que você deve adicionar ao Android SDK.

+ +

Para começar a adicionar pacotes, execute o Android SDK Manager de uma das formas a seguir:

+
    +
  • No Android Studio, clique em SDK Manager + na barra de ferramentas.
  • +
  • Se não estiver usando o Android Studio: +
      +
    • Windows: Clique duas vezes no arquivo SDK Manager.exe na raiz do diretório do Android + SDK.
    • +
    • Mac/Linux: Abra um terminal e navegue para o diretório tools/ no + local em que o Android SDK foi instalado. Em seguida, execute android sdk.
    • +
    +
  • +
+ +

Ao abrir o SDK Manager pela primeira vez, vários pacotes são selecionados +por padrão. Deixe-os selecionados, mas certifique-se de ter tudo o que é necessário +para começar seguindo os seguintes passos:

+ + +
    +
  1. +

    Obtenha as ferramentas mais recentes do SDK

    + + + +

    No mínimo, ao configurar o Android SDK, + é preciso baixar as ferramentas mais recentes e a plataforma Android:

    +
      +
    1. Abra o diretório Tools e selecione: +
        +
      • Ferramentas do Android SDK
      • +
      • Ferramentas-plataforma Android SDK
      • +
      • Ferramentas-Android SDK Build (versão mais recente)
      • +
      +
    2. +
    3. Abra a pasta do primeiro Android X.X (a versão mais recente) e selecione: +
        +
      • Plataforma SDK
      • +
      • Uma imagem do sistema para o emulador, como
        + Imagem do sistema ARM EABI v7a
      • +
      +
    4. +
    +
  2. + +
  3. +

    Obtenha a biblioteca de suporte para APIs adicionais

    + + + +

    A Biblioteca de Suporte do Android + fornece um conjunto estendido de APIs compatíveis com a maioria das versões do Android.

    + +

    Abra o diretório Extras e selecione:

    +
      +
    • Repositório de Suporte do Android
    • +
    • Biblioteca de Suporte do Android
    • +
    + +

     

    +

     

    + +
  4. + + +
  5. +

    Obtenha os serviços do Google Play para obter ainda mais APIs

    + + + +

    Para desenvolver com as APIs do Google, você precisa do pacote de serviços do Google Play:

    +

    Abra o diretório Extras e selecione:

    +
      +
    • Repositório do Google
    • +
    • Serviços Google Play
    • +
    + +

    Observação: as APIs dos serviços do Google Play não estão disponíveis em todos os dispositivos + com Android, mas estão disponíveis em todos os dispositivos com Google Play Store. Para usar essas + APIs no emulador do Android, também é preciso instalar a imagem do sistema das APIs do Google + do diretório Android X.X mais recente no SDK Manager.

    +
  6. + + +
  7. +

    Instale os pacotes

    +

    Depois de selecionar todos os pacotes desejados, prossiga para a instalação:

    +
      +
    1. Clique em Install X packages (Instalar X pacotes).
    2. +
    3. Na janela seguinte, clique duas vezes no nome de cada pacote à esquerda + para aceitar o contrato de licença de cada um.
    4. +
    5. Clique em Install (Instalar).
    6. +
    +

    O andamento do download é exibido na parte inferior da tela do SDK Manager. + Não saia do SDK Manager, pois isso cancelará o download.

    +
  8. + +
  9. +

    Crie alguma coisa!

    + +

    Com os pacotes acima agora no seu Android SDK, você está pronto para criar aplicativos +para o Android. À medida que novas ferramentas e outras APIs forem disponibilizadas, basta executar o SDK Manager + para baixar os novos pacotes para o SDK.

    + +

    A seguir há algumas opções sobre como prosseguir:

    + +
    +
    +

    Introdução

    +

    Se você é novo no desenvolvimento para Android, aprenda os fundamentos de aplicativos para Android seguindo +o guia para Criação do primeiro aplicativo.

    + +
    +
    +

    Desenvolva para vestíveis

    +

    Se você está pronto para começar a desenvolver aplicativos para vestíveis Android, consulte o guia para +Criação de aplicativos para Android Wear.

    + +
    +
    +

    Use as APIs do Google

    +

    Para começar a usar as APIs do Google, como os serviços Maps ou +Play Game, consulte o guia para +Configuração dos serviços do +Google Play.

    + +
    +
    + + +
  10. + +
+ + diff --git a/docs/html-intl/intl/pt-br/training/material/animations.jd b/docs/html-intl/intl/pt-br/training/material/animations.jd new file mode 100644 index 0000000000000000000000000000000000000000..6597db63e135f4f5b1091f74ce67a958882072cc --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=Como definir animações personalizadas + +@jd:body + + + + +

As animações no Material Design dão feedback aos usuários sobre as ações deles e fornecem +continuidade visual à medida que interagem com o seu aplicativo. O tema do Material fornece algumas animações padrão para +botões e transições de atividades e o Android 5.0 (API de nível 21) e posteriores permitem a personalização +dessas animações e a criação de novas:

+ +
    +
  • Feedback de toque
  • +
  • Revelação circular
  • +
  • Transições de atividades
  • +
  • Movimento curvado
  • +
  • Mudanças de estado da visualização
  • +
+ + +

Personalizar feedback de toque

+ +

Os feedbacks de toque no Material Design fornecem confirmação visual instantânea no +ponto de contato quando os usuários interagem com elementos da interface do usuário. As animações de feedback de toque padrão +para botões usam a nova classe {@link android.graphics.drawable.RippleDrawable}, que passa por transições +entre diferentes estados com um efeito de ondulação.

+ +

Na maioria dos casos, você deve aplicar essa funcionalidade no XML de visualização especificando o segundo plano da +visualização como:

+ +
    +
  • ?android:attr/selectableItemBackground para uma ondulação delimitada.
  • +
  • ?android:attr/selectableItemBackgroundBorderless para uma ondulação que se estenda além +da visualização. Ele será desenhado sobre e delimitado pelo pai mais próximo da visualização com um segundo plano +não nulo.
  • +
+ +

Observação: selectableItemBackgroundBorderless é um novo +atributo introduzido na API de nível 21.

+ + +

Alternativamente, você pode definir um {@link android.graphics.drawable.RippleDrawable} +como um recurso XML usando o elemento ripple.

+ +

Você pode atribuir uma cor para objetos {@link android.graphics.drawable.RippleDrawable}. Para alterar +a cor do feedback de toque padrão, use o atributo android:colorControlHighlight +do tema.

+ +

Para obter mais informações, consulte a referência de API para a classe {@link +android.graphics.drawable.RippleDrawable}.

+ + +

Usar o efeito de revelação

+ +

Revelar animações fornece continuidade visual aos usuários ao exibir ou esconder um grupo +de elementos da interface do usuário. O método {@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()} permite animar um círculo de recorte para +revelar ou ocultar uma visualização.

+ +

Para revelar uma visualização anteriormente invisível usando esse efeito:

+ +
+// previously invisible view
+View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the final radius for the clipping circle
+int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
+
+// create the animator for this view (the start radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
+
+// make the view visible and start the animation
+myView.setVisibility(View.VISIBLE);
+anim.start();
+
+ +

Para esconder uma visualização anteriormente visível usando esse efeito:

+ +
+// previously visible view
+final View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the initial radius for the clipping circle
+int initialRadius = myView.getWidth();
+
+// create the animation (the final radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
+
+// make the view invisible when the animation is done
+anim.addListener(new AnimatorListenerAdapter() {
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        super.onAnimationEnd(animation);
+        myView.setVisibility(View.INVISIBLE);
+    }
+});
+
+// start the animation
+anim.start();
+
+ + +

Personalizar transições de atividades

+ + +
+
+ +
+
+

Figura 1 - uma + transição com elementos compartilhados.

+ Para reproduzir o filme, clique na tela do dispositivo +
+
+ +

As transições de atividades em aplicativos com Material Design fornecem conexões visuais +entre estados diferentes por meio de movimentos e transformações entre elementos comuns. Você pode especificar animações personalizadas para +transições de entrada e de saída e para transições de elementos compartilhados entre atividades.

+ +
    +
  • Uma transição de entrada determina como as visualizações em uma atividade entram em cena. +Por exemplo, na transição de entrada explodir, as visualizações entram em cena por fora +e voam em direção ao centro da tela.
  • + +
  • Uma transição de saída determina como as visualizações em uma atividade saem de cena. Por + exemplo, na transição de saída explodir, as visualizações saem de cena a partir do +centro.
  • + +
  • Uma transição de elementos compartilhados determina como as visualizações compartilhadas +entre duas atividades fazem transição entre essas atividades. Por exemplo, se duas atividades têm a mesma +imagem em posições e tamanhos diferentes, a transição de elemento compartilhado changeImageTransform converte +e dimensiona a imagem suavemente entre essas atividades.
  • +
+ +

O Android 5.0 (API de nível 21) é compatível com estas transições de entrada e de saída:

+ +
    +
  • explodir - move as visualizações para dentro ou para fora partindo do centro da cena.
  • +
  • deslizar - move as visualizações para dentro ou para fora partindo de um dos cantos da cena.
  • +
  • esmaecer - adiciona ou remove uma visualização de uma cena alterando a opacidade.
  • +
+ +

Qualquer transição que amplie a classe {@link android.transition.Visibility} é suportada +como uma transição de entrada ou de saída. Para obter mais informações, consulte a referência de API para a classe +{@link android.transition.Transition}.

+ +

O Android 5.0 (API de nível 21) também é compatível com estas transições de elementos compartilhados:

+ +
    +
  • changeBounds - anima as mudanças das visualizações desejadas em limites do layout.
  • +
  • changeBounds - anima as mudanças das visualizações desejadas em limites de corte.
  • +
  • changeBounds - anima as mudanças das visualizações desejadas em escala e rotação.
  • +
  • changeImageTransform - anima as mudanças das imagens desejadas em tamanho e escala.
  • +
+ +

Ao habilitar as transições de atividades no seu aplicativo, a transição de esmaecimento cruzado padrão é +ativada entre as atividades de entrada e saída.

+ + +

Figura 2 - uma transição de cena com um elemento compartilhado. +

+ +

Especificar transições de atividades

+ +

Primeiro, habilite as transições de conteúdo da janela com o atributo android:windowContentTransitions +ao definir um estilo herdado do tema do Material. Você também pode especificar +transições de entrada, saída e elemento compartilhado na definição de estilo:

+ +
+<style name="BaseAppTheme" parent="android:Theme.Material">
+  <!-- enable window content transitions -->
+  <item name="android:windowContentTransitions">true</item>
+
+  <!-- specify enter and exit transitions -->
+  <item name="android:windowEnterTransition">@transition/explode</item>
+  <item name="android:windowExitTransition">@transition/explode</item>
+
+  <!-- specify shared element transitions -->
+  <item name="android:windowSharedElementEnterTransition">
+    @transition/change_image_transform</item>
+  <item name="android:windowSharedElementExitTransition">
+    @transition/change_image_transform</item>
+</style>
+
+ +

A transição change_image_transform nesse exemplo é definida a seguir:

+ +
+<!-- res/transition/change_image_transform.xml -->
+<!-- (see also Shared Transitions below) -->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+  <changeImageTransform/>
+</transitionSet>
+
+ +

O elemento changeImageTransform corresponde à classe +{@link android.transition.ChangeImageTransform}. Para obter mais informações, consulte a referência de +API para {@link android.transition.Transition}.

+ +

Para habilitar transições de conteúdo da janela no código como alternativa, chame o método +{@link android.view.Window#requestFeature Window.requestFeature()}:

+ +
+// inside your activity (if you did not enable transitions in your theme)
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+
+ +

Para especificar transições no código, chame os métodos a seguir com um objeto {@link +android.transition.Transition}:

+ +
    +
  • {@link android.view.Window#setEnterTransition Window.setEnterTransition()}
  • +
  • {@link android.view.Window#setExitTransition Window.setExitTransition()}
  • +
  • {@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()}
  • +
  • {@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}
  • +
+ +

Os métodos {@link android.view.Window#setExitTransition setExitTransition()} e {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} definem +a transição de saída para a atividade de chamada. Os métodos {@link android.view.Window#setEnterTransition +setEnterTransition()} e {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} definem a transição de entrada para a atividade chamada.

+ +

Para obter o efeito completo de uma transição, você deve habilitar as transições de conteúdo da janela tanto na atividade +chamada quanto na atividade de chamada. Caso contrário, a atividade de chamada acionará a transição de saída, +mas você verá uma transição de janela (como dimensionamento ou esmaecimento).

+ +

Para iniciar uma transição de entrada o mais cedo possível, use o método +{@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} +na atividade chamada. Isso faz com que haja transições de entrada mais dramáticas.

+ +

Iniciar uma atividade usando transições

+ +

Se você habilita as transições e define uma transição de saída para uma atividade, a transição +será ativada ao iniciar outra atividade, como a seguir:

+ +
+startActivity(intent,
+              ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
+
+ +

Se você configurou uma transição de entrada para a segunda atividade, a transição também +será ativada quando a atividade for iniciada. Para desabilitar as transições ao iniciar outra atividade, forneça um pacote de +opções null.

+ +

Iniciar uma atividade com um elemento compartilhado

+ +

Para criar uma animação de transição de tela entre duas atividades que têm um elemento compartilhado:

+ +
    +
  1. Habilite transições de conteúdo da janela no tema.
  2. +
  3. Especifique uma transição de elementos compartilhados no estilo.
  4. +
  5. Defina a transição como um recurso XML.
  6. +
  7. Atribua um nome comum aos elementos compartilhados em ambos os layouts com o atributo + android:transitionName.
  8. +
  9. Use o método {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()}.
  10. +
+ +
+// get the element that receives the click event
+final View imgContainerView = findViewById(R.id.img_container);
+
+// get the common element for the transition in this activity
+final View androidRobotView = findViewById(R.id.image_small);
+
+// define a click listener
+imgContainerView.setOnClickListener(new View.OnClickListener() {
+    @Override
+    public void onClick(View view) {
+        Intent intent = new Intent(this, Activity2.class);
+        // create the transition animation - the images in the layouts
+        // of both activities are defined with android:transitionName="robot"
+        ActivityOptions options = ActivityOptions
+            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
+        // start the new activity
+        startActivity(intent, options.toBundle());
+    }
+});
+
+ +

Para obter visualizações dinâmicas compartilhadas geradas no código, use o método +{@link android.view.View#setTransitionName View.setTransitionName()} para especificar um nome de +elemento comum em ambas as atividades.

+ +

Para reverter a animação de transição de cena ao finalizar a segunda atividade, chame o método +{@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} +em vez de {@link android.app.Activity#finish Activity.finish()}.

+ +

Iniciar uma atividade com diversos elementos compartilhados

+ +

Para criar uma animação de transição de cena entre duas atividades que têm mais de um elemento +compartilhado, defina os elementos compartilhados em ambos os layouts com o atributo android:transitionName +(ou use o método {@link android.view.View#setTransitionName View.setTransitionName()} em ambas +as atividades) e crie um objeto {@link android.app.ActivityOptions}, como a seguir:

+ +
+ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
+        Pair.create(view1, "agreedName1"),
+        Pair.create(view2, "agreedName2"));
+
+ + +

Usar movimento curvado

+ +

As animações no Material Design dependem das curvas para obter padrões de interpolação +de tempo e de movimentos espaciais. Com o Android 5.0 (API de nível 21) e posteriores, você pode definir padrões de curvas de +temporização personalizada e de movimentos curvados para animações.

+ +

A classe {@link android.view.animation.PathInterpolator} é um novo interpolador baseado em uma +curva Bézier ou em um objeto {@link android.graphics.Path}. Esse interpolador especifica uma curva de movimento em +um quadrado de 1x1, com pontos de ancoragem em (0,0) e (1,1) e pontos de controle conforme especificado usando +os argumentos do construtor. Você também pode definir um interpolador de caminho como um recurso XML:

+ +
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1"/>
+
+ +

O sistema fornece recursos XML para três curvas básicas na especificação do +Material Design:

+ +
    +
  • @interpolator/fast_out_linear_in.xml
  • +
  • @interpolator/fast_out_slow_in.xml
  • +
  • @interpolator/linear_out_slow_in.xml
  • +
+ +

Você pode passar um objeto {@link android.view.animation.PathInterpolator} para o método {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()}.

+ +

A classe {@link android.animation.ObjectAnimator} tem novos construtores que permitem a animação de +coordenadas ao longo de um caminho usando duas ou mais propriedades simultaneamente. Por exemplo, o animador a seguir usa um +objeto {@link android.graphics.Path} para animar as propriedades X e Y de uma visualização:

+ +
+ObjectAnimator mAnimator;
+mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
+...
+mAnimator.start();
+
+ + +

Animar mudança de estado da visualização

+ +

A classe {@link android.animation.StateListAnimator} permite a definição de animadores que são executados +quando o estado de uma visualização muda. O exemplo a seguir mostra como definir um {@link +android.animation.StateListAnimator} como um recurso XML:

+ +
+<!-- animate the translationZ property of a view when pressed -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_pressed="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="@android:integer/config_shortAnimTime"
+        android:valueTo="2dp"
+        android:valueType="floatType"/>
+        <!-- you could have other objectAnimator elements
+             here for "x" and "y", or other properties -->
+    </set>
+  </item>
+  <item android:state_enabled="true"
+    android:state_pressed="false"
+    android:state_focused="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="100"
+        android:valueTo="0"
+        android:valueType="floatType"/>
+    </set>
+  </item>
+</selector>
+
+ +

Para anexar animações de estado de visualização personalizadas a uma visualização, defina um animador usando o elemento +selector em um arquivo de recurso XML, como nesse exemplo, e +atribua-o à visualização com o atributo android:stateListAnimator. Para atribuir um animador de lista de estado +a uma visualização no código, use o método {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()} e atribua o animador à visualização com o método +{@link android.view.View#setStateListAnimator View.setStateListAnimator()}.

+ +

Quando o tema amplia o tema do Material, os botões têm uma animação Z por padrão. Para evitar esse +comportamento nos botões, defina o atributo android:stateListAnimator como +@null.

+ +

A classe {@link android.graphics.drawable.AnimatedStateListDrawable} permite a criação de +desenháveis que exibem animações entre mudanças de estado da visualização associada. Alguns dos widgets de sistema no +Android 5.0 usam essas animações por padrão. O exemplo a seguir mostra como +definir um {@link android.graphics.drawable.AnimatedStateListDrawable} como um recurso XML:

+ +
+<!-- res/drawable/myanimstatedrawable.xml -->
+<animated-selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- provide a different drawable for each state-->
+    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
+        android:state_pressed="true"/>
+    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
+        android:state_focused="true"/>
+    <item android:id="@id/default"
+        android:drawable="@drawable/drawableD"/>
+
+    <!-- specify a transition -->
+    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
+        <animation-list>
+            <item android:duration="15" android:drawable="@drawable/dt1"/>
+            <item android:duration="15" android:drawable="@drawable/dt2"/>
+            ...
+        </animation-list>
+    </transition>
+    ...
+</animated-selector>
+
+ + +

Animar desenháveis de vetor

+ +

Desenháveis de vetor são +dimensionáveis sem perder definição. A classe {@link android.graphics.drawable.AnimatedVectorDrawable} +permite a animação de propriedades de um desenhável de vetor.

+ +

Você normalmente define desenháveis de vetor animados em três arquivos XML:

+ +
    +
  • Um desenhável de vetor com o elemento <vector> em +res/drawable/
  • +
  • Um desenhável de vetor animado com o elemento <animated-vector> em +res/drawable/
  • +
  • Um ou mais animadores de objeto com o elemento <objectAnimator> em +res/anim/
  • +
+ +

Desenháveis de vetor animados podem animar os atributos dos elementos <group> e +<path>. Os elementos <group> definem um conjunto de +caminhos ou subgrupos e o elemento <path> define caminhos a serem desenhados.

+ +

Ao definir um desenhável de vetor que você queira animar, use o atributo android:name +para atribuir um nome único a grupos e caminhos para poder referenciá-los nas definições +do animador. Por exemplo:

+ +
+<!-- res/drawable/vectordrawable.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:width="64dp"
+    android:viewportHeight="600"
+    android:viewportWidth="600">
+    <group
+        android:name="rotationGroup"
+        android:pivotX="300.0"
+        android:pivotY="300.0"
+        android:rotation="45.0" >
+        <path
+            android:name="v"
+            android:fillColor="#000000"
+            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+    </group>
+</vector>
+
+ +

A definição de desenhável de vetor animado se refere a grupos e caminhos no desenhável de vetor +pelos respectivos nomes:

+ +
+<!-- res/drawable/animvectordrawable.xml -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:drawable="@drawable/vectordrawable" >
+    <target
+        android:name="rotationGroup"
+        android:animation="@anim/rotation" />
+    <target
+        android:name="v"
+        android:animation="@anim/path_morph" />
+</animated-vector>
+
+ +

As definições de animação representam objetos {@link android.animation.ObjectAnimator} ou {@link +android.animation.AnimatorSet}. O primeiro animador nesse exemplo gira o grupo +desejado em 360º:

+ +
+<!-- res/anim/rotation.xml -->
+<objectAnimator
+    android:duration="6000"
+    android:propertyName="rotation"
+    android:valueFrom="0"
+    android:valueTo="360" />
+
+ +

O segundo animador nesse exemplo transforma a forma do caminho do desenhável +de vetor. Ambos os caminhos devem ser compatíveis com a transformação: eles precisam ter o mesmo +número de comandos e de parâmetros para cada comando.

+ +
+<!-- res/anim/path_morph.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:duration="3000"
+        android:propertyName="pathData"
+        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
+        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
+        android:valueType="pathType" />
+</set>
+
+ +

Para obter mais informações, consulte a referência de API para {@link +android.graphics.drawable.AnimatedVectorDrawable}.

diff --git a/docs/html-intl/intl/pt-br/training/material/compatibility.jd b/docs/html-intl/intl/pt-br/training/material/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..2540df118aeb641b199bd276567986ddd7ec0a93 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=Como manter a compatibilidade + +@jd:body + + + + +

Alguns recursos do Material Design, como o tema do Material e transições personalizadas de atividades, estão +disponíveis apenas no Android 5.0 (API de nível 21) e posteriores. Contudo, você pode projetar os aplicativos +para usar esses recursos ao executá-los em dispositivos compatíveis com o Material Design e ainda mantê-los +compatíveis com dispositivos executando versões anteriores do Android.

+ + +

Definir estilos alternativos

+ +

Você pode configurar o aplicativo para usar o tema do Material em dispositivos compatíveis +com ele e reverter para um tema antigo em dispositivos que executem versões anteriores do Android:

+ +
    +
  1. Defina um tema herdado de um tema antigo (como Holo) em + res/values/styles.xml.
  2. +
  3. Defina um tema com o mesmo nome herdado do tema do Material em + res/values-v21/styles.xml.
  4. +
  5. Defina esse tema como o tema do seu aplicativo no arquivo de manifesto.
  6. +
+ +

Observação: +se o seu aplicativo usa o tema do Material mas não fornece um tema alternativo dessa maneira, +o aplicativo não rodará em versões mais antigas do que o Android 5.0. +

+ + +

Fornecer layouts alternativos

+ +

Se os layouts que você projetou de acordo com as orientações do Material Design não usarem +quaisquer dos novos atributos XML introduzidos no Android 5.0 (API de nível 21), eles funcionarão em +versões antigas do Android. Caso contrário, você pode fornecer layouts alternativos. Você também pode fornecer +layouts alternativos para personalizar a aparência do seu aplicativo em versões anteriores do Android.

+ +

Crie arquivos de layout para Android 5.0 (API de nível 21) dentro de res/layout-v21/ e arquivos de +layout alternativo para versões anteriores do Android dentro de res/layout/. +Por exemplo, res/layout/my_activity.xml é um layout alternativo de +res/layout-v21/my_activity.xml.

+ +

Para evitar duplicação do código, defina os estilos dentro de res/values/, modifique-os +em res/values-v21/ para as novas APIs e use herança de estilo, definindo +estilos de base em res/values/ e herdando daqueles em res/values-v21/.

+ + +

Usar a Biblioteca de Suporte

+ +

A v7 da Biblioteca de Suporte +r21 e posteriores incluem os seguintes recursos do Material Design:

+ + + +

Widgets de sistema

+ +

Os temas Theme.AppCompat fornecem estilos do Material Design para os seguintes widgets:

+ +
    +
  • {@link android.widget.EditText}
  • +
  • {@link android.widget.Spinner}
  • +
  • {@link android.widget.CheckBox}
  • +
  • {@link android.widget.RadioButton}
  • +
  • {@link android.support.v7.widget.SwitchCompat}
  • +
  • {@link android.widget.CheckedTextView}
  • +
+ +

Paleta de cores

+ +

Para obter estilos do Material Design e personalizar a paleta de cores com a v7 da Biblioteca de Suporte +do Android, aplique um dos temas Theme.AppCompat:

+ +
+<!-- extend one of the Theme.AppCompat themes -->
+<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
+    <!-- customize the color palette -->
+    <item name="colorPrimary">@color/material_blue_500</item>
+    <item name="colorPrimaryDark">@color/material_blue_700</item>
+    <item name="colorAccent">@color/material_green_A200</item>
+</style>
+
+ +

Listas e cartões

+ +

Os widgets {@link android.support.v7.widget.RecyclerView} e {@link +android.support.v7.widget.CardView} estão disponíveis em versões anteriores do Android na +Biblioteca de Suporte v7 do Android com as seguintes limitações:

+
    +
  • {@link android.support.v7.widget.CardView} volta a uma implementação de sombra programática + usando preenchimento adicional.
  • +
  • {@link android.support.v7.widget.CardView} não recorta as visualizações filhas que cortam + as bordas arredondadas.
  • +
+ + +

Dependências

+ +

Para usar esses recursos em versões anteriores ao Android 5.0 (API de nível 21), inclua a +Biblioteca de Suporte v7 do Android no projeto como uma dependência de Gradle:

+ +
+dependencies {
+    compile 'com.android.support:appcompat-v7:21.0.+'
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
+ + +

Verificar a versão do sistema

+ +

Os recursos a seguir estão disponíveis somente no Android 5.0 (API de nível 21) e em posteriores:

+ +
    +
  • Transições de atividades
  • +
  • Feedback de toque
  • +
  • Animações de revelação
  • +
  • Animações com base em caminhos
  • +
  • Desenháveis de vetor
  • +
  • Tingimento desenhável
  • +
+ +

Para preservar a compatibilidade com versões anteriores do Android, verifique a {@link +android.os.Build.VERSION#SDK_INT version} do sistema em tempo de execução antes de chamar as APIs para quaisquer +destes recursos:

+ +
+// Check if we're running on Android 5.0 or higher
+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+    // Call some material design APIs here
+} else {
+    // Implement this feature without material design
+}
+
+ +

Observação: Para especificar quais versões do Android são compatíveis com o seu aplicativo, +use os atributos android:minSdkVersion e android:targetSdkVersion +no arquivo de manifesto. Para usar os recursos do Material Design no Android 5.0, defina o +atributo android:targetSdkVersion como 21. Para obter mais informações, consulte +o guia de API +<uses-sdk>.

diff --git a/docs/html-intl/intl/pt-br/training/material/drawables.jd b/docs/html-intl/intl/pt-br/training/material/drawables.jd new file mode 100644 index 0000000000000000000000000000000000000000..4eb9f3602037e8de9e637b371b2fe93648ccd0f5 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=Como trabalhar com desenháveis + +@jd:body + + + +

As seguintes capacidades dos desenháveis ajudam na implementação do Material Design nos aplicativos:

+ +
    +
  • Tingimento desenhável
  • +
  • Extração de cor proeminente
  • +
  • Desenháveis de vetor
  • +
+ +

Esta lição mostra como usar esses recursos no seu aplicativo.

+ + +

Colorir recursos desenháveis

+ +

Com o Android 5.0 (API de nível 21) e posteriores, você pode colorir bitmaps e nine-patches definidos como +máscaras alfa. Você pode colori-los com recursos de cor ou atributos de tema que determinam os recursos +de cor (por exemplo, ?android:attr/colorPrimary). Normalmente, você cria esses ativos +somente uma vez e colore-os automaticamente para combinar com seu tema.

+ +

Você pode aplicar um tingimento aos objetos {@link android.graphics.drawable.BitmapDrawable} ou {@link +android.graphics.drawable.NinePatchDrawable} com o método {@code setTint()}. Você também +pode configurar a cor e o modo do tingimento nos layouts com os atributos android:tint e +android:tintMode.

+ + +

Extrair cores proeminentes de uma imagem

+ +

A Biblioteca de Suporte r21 do Android e posteriores incluem a classe {@link +android.support.v7.graphics.Palette}, que permite a extração de cores proeminentes de uma imagem. +Essa classe extrai as seguintes cores proeminentes:

+ +
    +
  • Vibrante
  • +
  • Escuro vibrante
  • +
  • Claro vibrante
  • +
  • Suave
  • +
  • Escuro suave
  • +
  • Claro suave
  • +
+ +

Para extrair essas cores, passe um objeto {@link android.graphics.Bitmap} para o método estático +{@link android.support.v7.graphics.Palette#generate Palette.generate()} no encadeamento do segundo +plano em que você carrega as imagens. Se você não puder usar aquele encadeamento, chame o método +{@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} e forneça um escutador +como alternativa.

+ +

Você pode recuperar as cores proeminentes da imagem usando métodos de obtenção na classe +Palette, como Palette.getVibrantColor.

+ +

Para usar a classe {@link android.support.v7.graphics.Palette} no projeto, adicione a seguinte +dependência do Gradle ao +módulo do aplicativo:

+ +
+dependencies {
+    ...
+    compile 'com.android.support:palette-v7:21.0.0'
+}
+
+ +

Para obter mais informações, consulte a referência de API para a classe {@link android.support.v7.graphics.Palette} +.

+ + +

Criar desenháveis de vetor

+ + + +
+

Vídeos

+

Gráficos de vetor do Android

+
+
+ +

No Android 5.0 (API de nível 21) e posteriores, você pode definir desenháveis de vetor que são dimensionáveis +sem perder definição. Você precisa apenas de um arquivo de ativos para uma imagem de vetor, em vez de um arquivo de ativos +para cada densidade de tela no caso de imagens de bitmap. Para criar uma imagem de vetor, defina os detalhes +da forma dentro de um elemento XML <vector>.

+ +

O exemplo a seguir define uma imagem de vetor com a forma de um coração:

+ +
+<!-- res/drawable/heart.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    <!-- intrinsic size of the drawable -->
+    android:height="256dp"
+    android:width="256dp"
+    <!-- size of the virtual canvas -->
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+  <!-- draw a path -->
+  <path android:fillColor="#8fff"
+      android:pathData="M20.5,9.5
+                        c-1.955,0,-3.83,1.268,-4.5,3
+                        c-0.67,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
+
+ +

Imagens de vetor são representadas no Android como objetos {@link android.graphics.drawable.VectorDrawable} +. Para obter mais informações sobre a sintaxe pathData, consulte a Referência do caminho SVG. Para obter informações sobre +como animar propriedades de desenháveis de vetor, consulte +Animar desenháveis de vetor.

diff --git a/docs/html-intl/intl/pt-br/training/material/get-started.jd b/docs/html-intl/intl/pt-br/training/material/get-started.jd new file mode 100644 index 0000000000000000000000000000000000000000..e7b56a3790d859ba186302f8237982e3c5e78633 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=Como iniciar + +@jd:body + + + + +

Para criar aplicativos com o Material Design:

+ +
    +
  1. + Reveja as especificações do Material Design.
  2. +
  3. + Aplique o tema do Material no seu aplicativo.
  4. +
  5. + Crie layouts de acordo com as orientações do Material Design.
  6. +
  7. + Especifique a elevação das visualizações para inserir sombras.
  8. +
  9. + Use widgets do sistema para listas e cartões.
  10. +
  11. + Personalize animações no aplicativo.
  12. +
+ +

Manter compatibilidade com versões anteriores

+ +

Você pode adicionar muitos recursos do Material Design ao aplicativo e, ao mesmo tempo, manter +a compatibilidade com versões anteriores ao Android 5.0. Para obter mais informações, consulte +Como manter a compatibilidade.

+ +

Atualizar o aplicativo com o Material Design

+ +

Para atualizar um aplicativo existente para incorporar o Material Design, atualize os layouts +de acordo com as orientações do Material Design. Certifique-se também de incorporar profundidade, feedbacks de toque e +animações.

+ +

Criar novos aplicativos com o Material Design

+ +

Se você está criando um novo aplicativo com recursos do Material Design, as orientações do Material Design fornecem uma +estrutura de projeto coesa. Siga tais orientações e use a nova funcionalidade na estrutura do Android para +projetar e desenvolver o aplicativo.

+ + +

Aplicar o tema do Material

+ +

Para aplicar o tema do Material no aplicativo, especifique um estilo herdado de +android:Theme.Material:

+ +
+<!-- res/values/styles.xml -->
+<resources>
+  <!-- your theme inherits from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- theme customizations -->
+  </style>
+</resources>
+
+ +

O tema do Material fornece widgets do sistema atualizados que permitem definir a paleta de cores e as animações +padrão para feedback de toque e transições de atividades. Para obter mais detalhes, consulte +Como usar o tema do Material.

+ + +

Projetar layouts

+ +

Além de aplicar e personalizar o tema do Material, os layouts devem estar em conformidade +com as orientações do Material Design. Ao projetar os +layouts, dê atenção especial ao seguinte:

+ +
    +
  • Grades das linhas de base
  • +
  • Linhas-chave
  • +
  • Espaçamento
  • +
  • Tamanho do alvo de toque
  • +
  • Estrutura do layout
  • +
+ + +

Especificar a elevação em visualizações

+ +

As visualizações podem lançar sombras e o valor da elevação delas +determina o tamanho da sombra e a ordem dos desenhos. Para definir a elevação de uma visualização, use o atributo +android:elevation nos layouts:

+ +
+<TextView
+    android:id="@+id/my_textview"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:text="@string/next"
+    android:background="@color/white"
+    android:elevation="5dp" />
+
+ +

A nova propriedade translationZ permite a criação de animações que refletem mudanças +temporárias na elevação de uma visualização. As mudanças de elevação podem ser úteis ao +responder a gestos +de toque.

+ +

Para obter mais detalhes, consulte Como definir +sombras e recortar visualizações.

+ + +

Criar listas e cartões

+ +

{@link android.support.v7.widget.RecyclerView} é uma versão mais completa de {@link +android.widget.ListView} compatível com diferentes tipos de layout e que fornece melhor desempenho. +{@link android.support.v7.widget.CardView} permite a exibição de informações dentro dos cartões com uma +aparência consistente nos aplicativos. O exemplo de código a seguir mostra como incluir um +{@link android.support.v7.widget.CardView} no layout:

+ +
+<android.support.v7.widget.CardView
+    android:id="@+id/card_view"
+    android:layout_width="200dp"
+    android:layout_height="200dp"
+    card_view:cardCornerRadius="3dp">
+    ...
+</android.support.v7.widget.CardView>
+
+ +

Para obter mais informações, consulte Como criar +listas e cartões.

+ + +

Personalizar animações

+ +

O Android 5.0 (API de nível 21) inclui novas APIs para criar animações personalizadas no aplicativo. +Por exemplo, você pode habilitar transições de atividades e definir uma transição de saída dentro +de uma atividade:

+ +
+public class MyActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // enable transitions
+        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+        setContentView(R.layout.activity_my);
+    }
+
+    public void onSomeButtonClicked(View view) {
+        getWindow().setExitTransition(new Explode());
+        Intent intent = new Intent(this, MyOtherActivity.class);
+        startActivity(intent,
+                      ActivityOptions
+                          .makeSceneTransitionAnimation(this).toBundle());
+    }
+}
+
+ +

Ao iniciar outra atividade a partir desta, a transição de saída é ativada.

+ +

Para saber mais sobre as novas APIs de animação, consulte Como definir animações personalizadas.

diff --git a/docs/html-intl/intl/pt-br/training/material/index.jd b/docs/html-intl/intl/pt-br/training/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..76580c83005c4bc04dffbba9ab398de687ba28bc --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=Material Design para desenvolvedores +page.type=design +page.image=images/cards/material_2x.png +page.metaDescription=Saiba como aplicar o Material Design em seus aplicativos. + + +@jd:body + +
+
+

Dependências e pré-requisitos

+
    +
  • Android 5.0 (API de nível 21)
  • +
+
+
+ +

O Material Design é um guia abrangente para design visual, de movimento e de interação para +diversas plataformas e dispositivos. Para usar o Material Design nos aplicativos Android, siga as orientações +descritas nas +especificações do +Material Design e use os novos componentes e funcionalidades disponíveis no Android 5.0 +(API de nível 21).

+ +

Essa lição mostra como criar aplicativos do Material Design com os seguintes elementos:

+ +
    +
  • O tema do Material
  • +
  • Widgets para cartões e listas
  • +
  • Sombras personalizadas e recorte de visualizações
  • +
  • Desenháveis de vetor
  • +
  • Animações personalizadas
  • +
+ +

Esta lição também ensina a manter a compatibilidade com versões anteriores ao Android +5.0 (API de nível 21) ao usar recursos do Material Design no aplicativo.

+ +

Lições

+ +
+
Como iniciar
+
Aprenda a atualizar o aplicativo com recursos do Material Design.
+ +
Como usar o tema do Material
+
Saiba como aplicar estilos do Material Design ao aplicativo.
+ +
Como criar listas e cartões
+
Aprenda a criar listas e cartões com uma aparência consistente usando widgets do sistema.
+ +
Como definir sombras e recortar visualizações
+
Saiba como definir elevação para as visualizações para criar sombras personalizadas e como recortar visualizações.
+ +
Como trabalhar com desenháveis
+
Aprenda a criar desenháveis de vetor e a colorir recursos desenháveis.
+ +
Como definir animações personalizadas
+
Saiba como criar animações personalizadas para visualizações e transições de atividades com elementos compartilhados.
+ +
Como manter a compatibilidade
+
Aprenda a manter a compatibilidade com versões de plataforma anteriores ao Android 5.0.
+
diff --git a/docs/html-intl/intl/pt-br/training/material/lists-cards.jd b/docs/html-intl/intl/pt-br/training/material/lists-cards.jd new file mode 100644 index 0000000000000000000000000000000000000000..eb336acb1ed262d57096afe5a352f3255bc69fa6 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=Como criar listas e cartões + +@jd:body + +
+ +
+ + +

Para criar listas e cartões complexos com estilos do Material Design no seu aplicativo, você pode usar os widgets +{@link android.support.v7.widget.RecyclerView} e {@link android.support.v7.widget.CardView} +.

+ + +

Criar listas

+ +

O widget {@link android.support.v7.widget.RecyclerView} é uma versão mais avançada e flexível +do {@link android.widget.ListView}. Esse widget é um contêiner para exibir grandes conjuntos +de dados que podem ser rolados com muita eficiência ao manter um número limitado de visualizações. Use o widget +{@link android.support.v7.widget.RecyclerView} quando tiver coletas de dados cujos elementos mudam +durante a execução baseados na ação do usuário ou em eventos de rede.

+ +

A classe {@link android.support.v7.widget.RecyclerView} simplifica a exibição e o tratamento de +grandes conjuntos de dados oferecendo:

+ +
    +
  • Gerenciadores de layout para posicionar itens
  • +
  • Animações padrão para operações de item comuns, como remoção ou adição de itens
  • +
+ +

Você também tem a flexibilidade de definir gerenciadores de layout e animações personalizadas para widgets {@link +android.support.v7.widget.RecyclerView}.

+ + +

+Figura 1. O widget RecyclerView. +

+ +

Para usar o widget {@link android.support.v7.widget.RecyclerView}, você deve especificar um +adaptador e um gerenciador de layout. Para criar um adaptador, amplie a classe {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}. Os detalhes +da implementação dependem das especificações do conjunto de dados e do tipo de visualização. Para obter +mais informações, veja os exemplos abaixo.

+ +
+ +

+Figura 2 - Listas com RecyclerView. +

+
+ +

Um gerenciador de layout posiciona as visualizações de item dentro de um {@link +android.support.v7.widget.RecyclerView} e determina quando reutilizar visualizações de item que não +estão mais visíveis ao usuário. Para reutilizar (ou reciclar) uma visualização, um gerenciador de layout pode solicitar +ao adaptador a substituição do conteúdo da visualização com um elemento diferente do conjunto de dados. Visualizações +recicladas dessa maneira aprimoram o desempenho ao evitar a criação de visualizações desnecessárias ou a +realização de pesquisas {@link android.app.Activity#findViewById findViewById()} caras.

+ +

{@link android.support.v7.widget.RecyclerView} fornece esses gerenciadores de layout embutidos:

+ +
    +
  • {@link android.support.v7.widget.LinearLayoutManager} exibe itens em uma lista de rolagem +vertical ou horizontal.
  • +
  • {@link android.support.v7.widget.GridLayoutManager} exibe itens em uma grade.
  • +
  • {@link android.support.v7.widget.StaggeredGridLayoutManager} exibe itens em uma grade escalonada.
  • +
+ +

Para criar um gerenciador de layout personalizado, amplie a classe {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager}.

+ +

Animações

+ +

As animações para a adição e a remoção de itens são habilitadas, por padrão, em {@link +android.support.v7.widget.RecyclerView}. Para personalizá-las, amplie a classe +{@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} e use o +método {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} +.

+ +

Exemplos

+ +

O exemplo de código a seguir demonstra como adicionar o +{@link android.support.v7.widget.RecyclerView} a um layout:

+ +
+<!-- A RecyclerView with some commonly used attributes -->
+<android.support.v7.widget.RecyclerView
+    android:id="@+id/my_recycler_view"
+    android:scrollbars="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
+
+ +

Depois de adicionar um widget {@link android.support.v7.widget.RecyclerView} ao seu layout, +obtenha um identificador para o objeto, conecte-o a um gerenciador de layout e anexe um adaptador para que os +dados sejam exibidos:

+ +
+public class MyActivity extends Activity {
+    private RecyclerView mRecyclerView;
+    private RecyclerView.Adapter mAdapter;
+    private RecyclerView.LayoutManager mLayoutManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.my_activity);
+        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
+
+        // use this setting to improve performance if you know that changes
+        // in content do not change the layout size of the RecyclerView
+        mRecyclerView.setHasFixedSize(true);
+
+        // use a linear layout manager
+        mLayoutManager = new LinearLayoutManager(this);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+
+        // specify an adapter (see also next example)
+        mAdapter = new MyAdapter(myDataset);
+        mRecyclerView.setAdapter(mAdapter);
+    }
+    ...
+}
+
+ +

O adaptador dá acesso aos itens no conjunto de dados, cria visualizações para os itens e +substitui o conteúdo de algumas das visualizações por novos itens de dados quando o item original não está +mais visível. O exemplo de código a seguir mostra uma implementação simples para um conjunto de dados +que consiste em uma matriz de strings exibidas usando widgets {@link android.widget.TextView}:

+ +
+public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
+    private String[] mDataset;
+
+    // Provide a reference to the views for each data item
+    // Complex data items may need more than one view per item, and
+    // you provide access to all the views for a data item in a view holder
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        // each data item is just a string in this case
+        public TextView mTextView;
+        public ViewHolder(TextView v) {
+            super(v);
+            mTextView = v;
+        }
+    }
+
+    // Provide a suitable constructor (depends on the kind of dataset)
+    public MyAdapter(String[] myDataset) {
+        mDataset = myDataset;
+    }
+
+    // Create new views (invoked by the layout manager)
+    @Override
+    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+                                                   int viewType) {
+        // create a new view
+        View v = LayoutInflater.from(parent.getContext())
+                               .inflate(R.layout.my_text_view, parent, false);
+        // set the view's size, margins, paddings and layout parameters
+        ...
+        ViewHolder vh = new ViewHolder(v);
+        return vh;
+    }
+
+    // Replace the contents of a view (invoked by the layout manager)
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        // - get element from your dataset at this position
+        // - replace the contents of the view with that element
+        holder.mTextView.setText(mDataset[position]);
+
+    }
+
+    // Return the size of your dataset (invoked by the layout manager)
+    @Override
+    public int getItemCount() {
+        return mDataset.length;
+    }
+}
+
+ + +
+ +

+Figura 3. Exemplos de cartões. +

+
+ +

Criar cartões

+ +

{@link android.support.v7.widget.CardView} amplia a classe {@link android.widget.FrameLayout} e +permite que você exiba informações dentro dos cartões que têm uma aparência consistente em toda a plataforma. Os widgets {@link +android.support.v7.widget.CardView} podem ter sombras e bordas arredondadas.

+ +

Para criar um cartão com sombra, use o atributo card_view:cardElevation. +{@link android.support.v7.widget.CardView} usa elevação real e sombras dinâmicas no Android 5.0 +(API de nível 21) e posteriores e volta para uma implementação de sombra programática em versões anteriores. +Para obter mais informações, consulte Como manter +a compatibilidade.

+ +

Use estas propriedades para personalizar a aparência do widget +{@link android.support.v7.widget.CardView}:

+ +
    +
  • Para definir o raio do canto nos layouts, use o atributo card_view:cardCornerRadius +.
  • +
  • Para definir o raio do canto no seu código, use o método CardView.setRadius.
  • +
  • Para definir a cor de segundo plano de um cartão, use o atributo card_view:cardBackgroundColor +.
  • +
+ +

O exemplo de código a seguir mostra como incluir um widget {@link android.support.v7.widget.CardView} +no layout:

+ +
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    ... >
+    <!-- A CardView that contains a TextView -->
+    <android.support.v7.widget.CardView
+        xmlns:card_view="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/card_view"
+        android:layout_gravity="center"
+        android:layout_width="200dp"
+        android:layout_height="200dp"
+        card_view:cardCornerRadius="4dp">
+
+        <TextView
+            android:id="@+id/info_text"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </android.support.v7.widget.CardView>
+</LinearLayout>
+
+ +

Para obter mais informações, consulte a referência de API para {@link android.support.v7.widget.CardView}.

+ + +

Adicionar dependências

+ +

Os widgets {@link android.support.v7.widget.RecyclerView} e {@link android.support.v7.widget.CardView} +são parte da Biblioteca de +Suporte v7. Para usar esses widgets no projeto, adicione estas +dependências do Gradle ao módulo +do aplicativo:

+ +
+dependencies {
+    ...
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
diff --git a/docs/html-intl/intl/pt-br/training/material/shadows-clipping.jd b/docs/html-intl/intl/pt-br/training/material/shadows-clipping.jd new file mode 100644 index 0000000000000000000000000000000000000000..4c4f1549f019376310fb78d66ce8063b30df0c12 --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=Como definir sombras e recortar visualizações + +@jd:body + + + +

O Material Design introduz a elevação em elementos da interface do usuário. A elevação ajuda os usuários a entender +a importância relativa de cada elemento e concentrar a atenção deles nas tarefas à mão.

+ +

A elevação de uma visualização, representada pela propriedade Z, determina a aparência visual das +sombras: visualizações com valores de Z mais altos lançam sombras maiores e mais suaves. Visualizações com valores de Z mais altos ocultam +visualizações com valores de Z mais baixos, mas o valor de Z de uma visualização não afeta o tamanho dela.

+ +

As sombras são desenhadas pelo pai da visualização elevada e, por isso, estão sujeitas a recortes padrão de +visualização, recortadas pelo pai por padrão.

+ +

A elevação também é útil para criar animações em que os widgets se elevam temporariamente +sobre o plano de visualização ao realizar alguma ação.

+ +

Para obter mais informações sobre elevação no Material Design, consulte +Objetos +no espaço 3D.

+ + +

Atribuir elevação a visualizações

+ +

O valor de Z para uma visualização tem dois componentes: + +

    +
  • Elevação: o componente estático.
  • +
  • Movimentação: o componente dinâmico usado para animações.
  • +
+ +

Z = elevation + translationZ

+ + +

Figura 1 - sombras para diferentes elevações de visualização.

+ +

Para definir a elevação de uma visualização em uma definição de layout, use o atributo android:elevation +. Para definir a elevação de uma visualização no código de uma atividade, use o método +{@link android.view.View#setElevation View.setElevation()}.

+ +

Para definir a conversão de uma visualização, use o método {@link android.view.View#setTranslationZ +View.setTranslationZ()}.

+ +

Os novos métodos {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} e {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} permitem +animar facilmente a elevação de visualizações. Para obter mais informações, consulte a referência de API para +{@link android.view.ViewPropertyAnimator} e o guia do desenvolvedor Animação de +propriedade.

+ +

Você também pode usar um {@link android.animation.StateListAnimator} para +especificar essas animações de um modo declarativo, o que é especialmente útil para casos em que o estado muda +as animações de acionamento, como quando um usuário pressiona um botão. Para obter mais informações, consulte +Mudanças de estado de visualização da animação.

+ +

Os valores de Z são medidos em dp (pixels independentes de densidade).

+ + +

Personalizar sombras e contornos de visualizações

+ +

Os limites de um desenhável do segundo plano da visualização determinam a forma padrão da sombra. +Contornos representam a forma externa de um objeto gráfico e definem a área +de ondulação para o feedback de toque.

+ +

Analise esta visualização, definida com um desenhável de segundo plano:

+ +
+<TextView
+    android:id="@+id/myview"
+    ...
+    android:elevation="2dp"
+    android:background="@drawable/myrect" />
+
+ +

O desenhável do segundo plano é definido como um retângulo com bordas arredondadas:

+ +
+<!-- res/drawable/myrect.xml -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#42000000" />
+    <corners android:radius="5dp" />
+</shape>
+
+ +

A visualização lança uma sombra com bordas arredondadas, uma vez que o desenhável de segundo plano +define o contorno da visualização. Fornecer um contorno personalizado neutraliza a forma padrão de uma sombra da visualização.

+ +

Para definir um contorno personalizado para uma visualização no seu código:

+ +

    +
  1. Amplie a classe {@link android.view.ViewOutlineProvider}.
  2. +
  3. Neutralize o método {@link android.view.ViewOutlineProvider#getOutline getOutline()}.
  4. +
  5. Atribua o novo fornecedor de contorno à visualização com o método {@link +android.view.View#setOutlineProvider View.setOutlineProvider()}.
  6. +
+ +

Você pode criar contornos ovais e retangulares com bordas arredondadas usando os métodos na classe +{@link android.graphics.Outline}. O fornecedor de contorno padrão para visualizações obtém o contorno +do segundo plano da visualização. Para evitar que uma visualização lance uma sombra, defina o fornecedor de contorno +dela como null.

+ + +

Recortar visualizações

+ +

Recortar visualizações permite mudar facilmente a forma de uma visualização. Você pode recortar visualizações +para obter consistência com outros elementos de design ou para mudar a forma de uma visualização em resposta à interação do usuário. +É possível recortar uma visualização na área do contorno usando o método {@link android.view.View#setClipToOutline +View.setClipToOutline()} ou o atributo android:clipToOutline. Somente +contornos retangulares, circulares e retangulares redondos são compatíveis com recorte, conforme determinado pelo método +{@link android.graphics.Outline#canClip Outline.canClip()}.

+ +

Para recortar uma visualização na forma de um desenhável, defina o desenhável como o segundo plano da visualização +(conforme exibido acima) e chame o método {@link android.view.View#setClipToOutline View.setClipToOutline()} +.

+ +

O recorte de visualizações é uma operação cara, por isso, não anime a forma que você +usa para recortar uma visualização. Para atingir esse efeito, use a animação Efeito de Revelação.

diff --git a/docs/html-intl/intl/pt-br/training/material/theme.jd b/docs/html-intl/intl/pt-br/training/material/theme.jd new file mode 100644 index 0000000000000000000000000000000000000000..df0e1ce661e0eca97cf23664da66dca37d7ff4dd --- /dev/null +++ b/docs/html-intl/intl/pt-br/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=Como usar o tema do Material + +@jd:body + + + + +

O novo tema do Material fornece:

+ +
    +
  • Widgets do sistema que permitem a configuração da paleta de cores
  • +
  • Animações de feedback de toque para os widgets do sistema
  • +
  • Animações de transição de atividades
  • +
+ +

É possível personalizar a aparência do tema do Material com +uma paleta de cores que você controla, de acordo com a identidade da sua marca. Você pode atribuir cor à barra de ações e +à barra de status usando atributos de tema, conforme exibido na Figura 3.

+ +

Os widgets de sistema têm um novo design e animações de feedback de toque. Você pode personalizar a +paleta de cores, as animações de feedback de toque e as transições de atividades do aplicativo.

+ +

O tema do Material é definido como:

+ +
    +
  • @android:style/Theme.Material (versão escura)
  • +
  • @android:style/Theme.Material.Light (versão clara)
  • +
  • @android:style/Theme.Material.Light.DarkActionBar
  • +
+ +

Para obter uma lista de estilos do Material que podem ser usados, consulte a referência de API para +{@link android.R.style R.style}.

+ + +
+
+ +
+

Figura 1. Tema escuro do Material

+
+
+
+ +
+

Figura 2. Tema claro do Material

+
+
+
+
+ +

+Observação: o tema do Material só está disponível no Android 5.0 (API de nível 21) e +posteriores. A Biblioteca de Suporte v7 +fornece temas com estilos do Material Design para alguns widgets e compatibilidade com a personalização da +paleta de cores. Para obter mais informações, consulte +Como manter a compatibilidade. +

+ + +

Personalizar a paleta de cores

+ +

Para personalizar as cores de base do tema para adequá-lo à sua marca, defina +as cores personalizadas usando os atributos de tema ao herdar do tema do Material:

+ +
+<resources>
+  <!-- inherit from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- Main theme colors -->
+    <!--   your app branding color for the app bar -->
+    <item name="android:colorPrimary">@color/primary</item>
+    <!--   darker variant for the status bar and contextual app bars -->
+    <item name="android:colorPrimaryDark">@color/primary_dark</item>
+    <!--   theme UI controls like checkboxes and text fields -->
+    <item name="android:colorAccent">@color/accent</item>
+  </style>
+</resources>
+
+ +
+ +

+Figura 3. Como personalizar o tema do Material

+
+ + +

Personalizar a barra de status

+ +

O tema do Material permite a fácil personalização da barra de status, para você poder +especificar uma cor adequada à sua marca, e fornece contraste suficiente para exibir os ícones de status brancos. Para +definir uma cor personalizada para a barra de status, use o atributo android:statusBarColor ao +ampliar o tema do Material. Por padrão, android:statusBarColor herda o +valor de android:colorPrimaryDark.

+ +

Você também pode personalizar a parte de trás da barra de status. Por exemplo, se você quiser mostrar +uma barra de status transparente sobre uma foto, com um degradê escuro sutil, para garantir que os ícones de status +brancos sejam visíveis, defina o atributo android:statusBarColor como +@android:color/transparent e ajuste os marcadores de janela conforme necessário. Você também +pode usar o método {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} para +animações ou esmaecimento.

+ +

+Observação: a barra de status deve ter, quase sempre, um traçado distante da barra de ferramentas +principal, exceto em casos em que haja imagens ricas de ponta a ponta, conteúdo de mídia por trás +dessas barras e quando se usa degradê, para garantir que os ícones continuem visíveis. +

+ +

Ao personalizar as barras de navegação e de status, deixe-as transparentes ou modifique +somente a barra de status. A barra de navegação deve permanecer preta em todos os outros casos.

+ + +

Atribuir tema a visualizações individuais

+ +

Elementos nas definições do layout XML podem especificar o atributo android:theme, +que referencia um recurso do tema. Esse atributo modifica o tema do elemento e quaisquer +elementos-filho, o que é útil para alterar as paletas de cores do tema em uma parte específica +de uma interface.

diff --git a/docs/html-intl/intl/ru/design/get-started/principles.jd b/docs/html-intl/intl/ru/design/get-started/principles.jd index 7f4977cef6a3fd81c65a42a067899428cfb337b8..7e4ea1214b9f2007794b0fa5f312fb07ace18018 100644 --- a/docs/html-intl/intl/ru/design/get-started/principles.jd +++ b/docs/html-intl/intl/ru/design/get-started/principles.jd @@ -14,8 +14,8 @@ page.title=Принципы проектирования Android

Постарайтесь понравиться пользователю

-
-
+
+

Вызовите у пользователя восхищение неожиданными решениями

Красивые экраны, тщательно продуманная анимация или своевременные звуковые сигналы создают у пользователя @@ -23,7 +23,7 @@ page.title=Принципы проектирования Android что его возможности не ограничены.

-
+
@@ -32,15 +32,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Реальные объекты доставляют больше удовольствия, чем кнопки и меню

-

Позвольте пользователю касаться объектов приложения и манипулировать ими. Это сокращает когнитивные усилия, -необходимые для выполнения задачи, повышая эмоциональное удовлетворение.

+

Позвольте пользователю касаться объектов приложения и манипулировать ими. Это делает +понятнее процесс выполнения задачи, повышая эмоциональное удовлетворение.

-
+
@@ -49,8 +49,8 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Позвольте пользователю сделать интерфейс индивидуальным

Люди любят вносить в интерфейс что-то личное, чтобы чувствовать себя более комфортно и считать, что всё под контролем. Предоставьте @@ -58,7 +58,7 @@ page.title=Принципы проектирования Android выполнению основных задач.

-
+
@@ -67,15 +67,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Изучите пользователя

-

Постепенно выясняйте предпочтения пользователя Вместо того, чтобы снова и снова заставлять его выбирать одно и то же, +

Постепенно выясняйте предпочтения пользователя. Вместо того, чтобы снова и снова заставлять его выбирать одно и то же, сделайте непосредственно доступными ранее выбранные варианты.

-
+
@@ -84,14 +84,14 @@ page.title=Принципы проектирования Android

Упростите жизнь пользователю

-
-
+
+

Будьте лаконичны

Используйте короткие фразы с простыми словами. Люди, скорее всего, пропустят длинные предложения.

-
+
@@ -100,15 +100,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Картинки воспринимаются быстрее чем слова

Используйте картинки в своих объяснениях. Они завладевают вниманием и действуют гораздо эффективнее слов.

-
+
@@ -117,15 +117,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Принимайте решения за пользователя, но оставляйте последнее слово за ним

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

-
+
@@ -134,15 +134,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Показывайте только самое необходимое и в нужный момент времени

Люди не в состоянии справиться сразу с большим количеством информации. Разбивайте задачи и информацию на небольшие легко воспринимаемые части. Скройте несущественные на данный момент опции и обучайте пользователя по ходу дела.

-
+
@@ -151,15 +151,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Пользователь всегда должен знать, в каком месте приложения он находится

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

-
+
@@ -168,8 +168,8 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Ни в коем случае не теряйте данные пользователей

Сохраняйте данные, на создание которых пользователь потратил свое время, и обеспечьте ему доступ к этой информации из любого места. Запоминайте настройки @@ -177,7 +177,7 @@ page.title=Принципы проектирования Android обновление приложения.

-
+
@@ -186,15 +186,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Внешне похожие элементы должны вести себя одинаково

Помогите людям распознать функциональные различия, сделав их визуально отличимыми. Избегайте ситуаций, в которых разные места приложения выглядят похоже, но работают по-разному при одних тех же исходных данных.

-
+
@@ -203,15 +203,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Прерывайте пользователя только в действительно важных ситуациях

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

-
+
@@ -220,8 +220,8 @@ page.title=Принципы проектирования Android

Постарайтесь приятно удивить пользователя

-
-
+
+

Предоставьте пользователю привычные приемы

У людей повышается самооценка, когда они могут разобраться в чем-либо самостоятельно. Сделайте ваше приложение простым в освоении @@ -229,7 +229,7 @@ page.title=Принципы проектирования Android является хорошим навигационным приемом.

-
+
@@ -238,16 +238,16 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Пользователь ни в чем не виноват

Будьте корректны, подсказывая пользователю, как выйти из сложного положения. Никто не хочет чувствовать себя бестолковым, имея дело с вашим приложением. При возникновении проблем дайте четкие указания по восстановлению, но избавьте пользователя от технических деталей. -В идеальном варианте приложение способно самостоятельно исправить ситуацию.

+Если приложение само исправит ситуацию, будет еще лучше.

-
+
@@ -256,15 +256,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Не скупитесь на поддержку пользователя

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

-
+
@@ -273,16 +273,16 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Выполняйте за пользователя черную работу

-

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

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

-
+
@@ -291,15 +291,15 @@ page.title=Принципы проектирования Android
 
-
-
+
+

Важные действия должны происходить быстро

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

-
+
diff --git a/docs/html-intl/intl/ru/design/material/index.jd b/docs/html-intl/intl/ru/design/material/index.jd index 2cfd91e88c546a47fda161b8226dc4fc9d3c9d58..da0352a10ef62af495ee1887cd181b76f1f2929e 100644 --- a/docs/html-intl/intl/ru/design/material/index.jd +++ b/docs/html-intl/intl/ru/design/material/index.jd @@ -1,7 +1,7 @@ -page.title=Material Design -page.tags=Material Design -page.type=design -page.image=design/material/images/MaterialLight.png +page.title=Material Design для Android +page.tags=Material,design +page.type=разработка +page.image=images/cards/design-material-for-android_2x.jpg @jd:body @@ -92,7 +92,7 @@ Material Design.

-

Виджет RecyclerView Представляет собой более гибкую версию ListView, +

Виджет RecyclerView представляет собой более гибкую версию ListView, которая поддерживает различные типы макетов и способствует повышению производительности.

@@ -137,9 +137,9 @@ Z. Это новое свойство показывает, насколько

Анимация

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

+изменения состояния представления и переходов между действиями.

-

Эти API-интерфейсы позволяют

+

Эти API-интерфейсы позволяют:

  • @@ -149,7 +149,7 @@ Z. Это новое свойство показывает, насколько скрывать и отображать представление с помощью анимации для кругового появления;
  • -переключаться меду действиями с помощью настраиваемой анимации для переходов между действиями; +переключаться между действиями с помощью настраиваемой анимации для переходов между действиями;
  • создавать более естественное движение с помощью анимации для перемещения по кривой; @@ -158,14 +158,14 @@ Z. Это новое свойство показывает, насколько анимировать изменение одного или нескольких свойств представления с помощью анимации для изменения состояния представления;
  • -отображать анимацию в графических элементах списков состояний в помежутке между изменением состояний представления. +отображать анимацию в графических элементах списков состояний в промежутке между изменением состояний представления.
-

Анимация для реакции на касание встроена а некоторые стандартные представления, например, кнопки. Новые API-интерфейсы +

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

-

Дополнительные сведения, см. в разделе Определение настраиваемой +

Дополнительные сведения см. в разделе Определение настраиваемой анимации.

diff --git a/docs/html-intl/intl/ru/design/patterns/compatibility.jd b/docs/html-intl/intl/ru/design/patterns/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..fd1f5a8c557faa7c6a20138b0d62d4213ddec34c --- /dev/null +++ b/docs/html-intl/intl/ru/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=Обратная совместимость +page.tags="support" +page.metaDescription=Примечания о том, как Android 4.x адаптирует пользовательский интерфейс, разработанный для ОС и оборудования более старых версий. +@jd:body + + +
+

Документация для разработчиков

+

Поддержка различных устройств

+
+
+ +

Существенные изменения в Android 3.0, включенные в новую версию:

+
    +
  • Прекращено использование аппаратных клавиш навигации ("Назад", "Меню", "Поиск", "Главная"). Вместо них за навигацию теперь отвечают + виртуальные элементы управления ("Назад", "Главная", "Последние").
  • +
  • Функциональный шаблон для использования меню на панелях действий.
  • +
+

Эти изменения в Android 4.0 для планшетов также реализованы в платформе для телефонов.

+ +

Адаптация Android 4.0 для оборудования и приложений более старых версий

+ +
+
+ +

Телефоны с виртуальными элементами навигации

+

В приложениях Android, созданных для Android 3.0 и более поздних версий, действия отображаются на панели действий. Действия, которые не помещаются на этой панели или не являются существенными, чтобы размещать их в элементе верхнего уровня, отображаются в панели дополнительных действий. + +

+

Чтобы открыть панель дополнительных действий, необходимо коснуться ее значка в панели действий.

+ +
+
+ + + +
+
+ +
+
+ +

Телефоны с физическими клавишами навигации

+

На телефонах Android с традиционными аппаратными клавишами отсутствует виртуальная панель навигации (в нижней части экрана). + Вместо этого для отображения дополнительных действий используется аппаратная клавиша меню. Открывающееся в результате нажатия этой клавиши +окно оформлено в том же стиле, что и в примере выше, однако отображается оно в нижней части экрана.

+ +
+
+ + + +
+
+ +
+
+ +

Устаревшие приложения на телефонах с виртуальными элементами навигации

+

Если запустить приложение, созданное для Android 2.3 или более ранних версий, на телефоне с виртуальными элементами +навигации, то панель дополнительных действий отобразится справа в виртуальной панели навигации. Можно +коснуться элемента управления, чтобы отобразить действия приложения в стиле традиционного меню Android.

+ +
+
+ + + +
+
diff --git a/docs/html-intl/intl/ru/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/ru/design/patterns/confirming-acknowledging.jd index 99a0d17ba464cc31640b9edfd704cad06f0bdbb1..d3f8bddf233a8d1271743045d2faa8b515782549 100644 --- a/docs/html-intl/intl/ru/design/patterns/confirming-acknowledging.jd +++ b/docs/html-intl/intl/ru/design/patterns/confirming-acknowledging.jd @@ -4,12 +4,12 @@ page.tags=dialog,toast,notification

В некоторых ситуациях, когда пользователь выполняет действие в вашем приложении, полезно в текстовом виде запросить подтверждение этого действия или отобразить уведомление о его выполнении.

-
-
+
+

Подтверждение — это просьба к пользователю подтвердить, что он действительно хочет, чтобы запущенное действие было выполнено. Иногда подтверждение отображается вместе с предупреждением или важной информацией относительно действия, которую пользователь должен принять во внимание.

-
+

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

@@ -22,14 +22,14 @@ page.tags=dialog,toast,notification

Подтверждение

-
-
+
+

Пример: Google Play Books

-

В этом примере пользователь пытается удалить книгу из своей библиотеки Play Google. Для подтверждения этого действия отображается оповещение, поскольку пользователь должен понимать, что книга больше не будет доступна ни с одного устройства.

+

В этом примере пользователь пытается удалить книгу из своей библиотеки Google Play. Для подтверждения этого действия отображается оповещение, поскольку пользователь должен понимать, что книга больше не будет доступна ни с одного устройства.

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

-
+

Пример: Android Beam

Подтверждения не обязательно должны быть представлены в виде оповещения с двумя кнопками. После старта Android Beam пользователю предлагается прикоснуться к содержимому (в данном примере, к фотографии) для обмена данными. Если пользователь решит не продолжать, он просто ничего не предпримет.

@@ -37,31 +37,31 @@ page.tags=dialog,toast,notification

Уведомление

-
-
-

Пример: Сохранение оставленного черновика Gmail

+
+
+

Пример: сохранение оставленного черновика Gmail

В этом примере, если пользователь переходит назад или вверх от экрана составления писем Gmail (возможно, вследствие неожиданного события), текущий черновик автоматически сохраняется. Всплывающее уведомление обращает внимание пользователя на этот факт. Через несколько секунд оно исчезает.

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

-
-

Пример: Удаление переписки Gmail

+
+

Пример: удаление переписки Gmail

После того, как пользователь удалит переписку из Gmail, появляется уведомление с возможностью отмены действия. Оно остается, пока пользователь не предпримет постороннее действие, например, прокрутит список писем.

Отсутствие подтверждения или уведомления

-
-
+
+

Пример: +1

Подтверждение не требуется. Если пользователь нажал +1 случайно, это не имеет большого значения. Он может просто нажать кнопку еще раз, чтобы отменить действие.

Уведомление не требуется. Пользователь увидит, что кнопка +1 поменяла состояние и стала красной. Это достаточно ясный сигнал.

-
-

Пример: Удаление приложения с главного экрана

+
+

Пример: удаление приложения с главного экрана

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

Уведомление не требуется. Пользователь будет знать, что приложение удалено с главного экрана, потому что сам заставил его исчезнуть в результате перетаскивания.

diff --git a/docs/html-intl/intl/ru/design/patterns/navigation.jd b/docs/html-intl/intl/ru/design/patterns/navigation.jd index a65a8f6f23bd71fad3be4aef19f82a9876a0a5bc..3a0fc6eb31e1c398162b565409a864af1f38f52e 100644 --- a/docs/html-intl/intl/ru/design/patterns/navigation.jd +++ b/docs/html-intl/intl/ru/design/patterns/navigation.jd @@ -16,14 +16,14 @@ page.image=/design/media/navigation_between_siblings_gmail.png инструкциям по применению кнопок "Назад" и "Вверх" сделает навигацию в вашем приложении предсказуемой и надежной с точки зрения пользователей.

В Android 2.3 и в более ранних версиях для навигации внутри приложения использовалась системная кнопка Назад. С появлением панели действий в Android 3.0 стал доступен второй механизм -навигации, — кнопка Вверх, содержащая значок приложения и левую угловую скобку.

+навигации – кнопка Вверх, содержащая значок приложения и левую угловую скобку.

Кнопки "Вверх" и "Назад"

Кнопка "Вверх" используется для навигации внутри приложения по иерархической структуре его -экранов. Например, если на экране A отображается некоторый список, и при выборе какого-либо элемента открывается +экранов. Например, если на экране A отображается некоторый список и при выборе какого-либо элемента открывается экран B (с подробной информацией об этом элементе), то на экране B должна присутствовать кнопка "Вверх" для возврата к экрану A.

Если экран является самым верхним в приложении (то есть главным), он не должен содержать кнопку @@ -34,7 +34,7 @@ page.image=/design/media/navigation_between_siblings_gmail.png экранов, а не на иерархии приложения.

Если предыдущий экран одновременно является иерархическим родителем текущего, -кнопка "Назад" имеет то же действие, что и кнопка "Вверх"—, и это случается довольно +кнопка "Назад" имеет то же действие, что и кнопка "Вверх", — и это случается довольно часто. Однако, в отличие от кнопки "Вверх", гарантирующей, что пользователь остается в приложении, кнопка "Назад" может перевести его на главный экран или даже в другое приложение.

@@ -57,7 +57,7 @@ page.image=/design/media/navigation_between_siblings_gmail.png

Изменение представления содержимого на экране

Изменение параметров представления содержимого на экране не сказывается на поведении кнопок "Вверх" и "Назад", — экран остается на прежнем месте в иерархии приложения, а история навигации не меняется.

-

Примерами изменения представления являются

+

Примерами изменения представления являются:

  • переключение представлений с помощью вкладок или жестов прокрутки вправо/влево;
  • переключение представлений с помощью раскрывающихся (или свернутых) вкладок;
  • @@ -75,7 +75,7 @@ page.image=/design/media/navigation_between_siblings_gmail.png

    Однако существует важное исключение из этого правила во время просмотра подробных представлений элементов, не связанных -вместе ссылающимся списком, например—, приложений +вместе ссылающимся списком, например приложений одного разработчика или альбомов одного исполнителя в Play Store. В этих случаях переход по каждой ссылке создает историю навигации, что заставляет кнопку "Назад" перебирать все ранее просмотренные экраны. Кнопка "Вверх" должна по-прежнему игнорировать эти связанные по смыслу экраны и осуществлять переход на последний просмотренный контейнерный экран.

    @@ -104,7 +104,7 @@ page.image=/design/media/navigation_between_siblings_gmail.png

Что касается кнопки "Назад", необходимо обеспечить более предсказуемую навигацию, вставив в -в стек переходов назад полный путь навигации вверх к самому верхнему экрану приложения. Это позволит пользователям, +стек переходов назад полный путь навигации вверх к самому верхнему экрану приложения. Это позволит пользователям, забывшим, как они вошли в приложение, перейти к его главному экрану перед выходом из приложения.

@@ -122,8 +122,8 @@ page.image=/design/media/navigation_between_siblings_gmail.png называются косвенными.

Кнопка "Назад" действует не так, как в случае со стандартными (прямыми) уведомлениями. Нажав ее на промежуточном экране косвенного уведомления, -пользователь вернется в точку, где было сгенерировано уведомление, —поскольку -в стеке переходов назад не появились никакие дополнительные экраны. Если пользователь перейдет в приложение с +пользователь вернется в точку, где было создано уведомление, поскольку +в стеке обратных переходов не появилось дополнительных экранов. Если пользователь перейдет в приложение с промежуточного экрана, поведение кнопок "Вверх" и "Назад" будет таким же, как и при стандартных уведомлениях: навигация внутри приложения, а не возврат к промежуточному экрану.

@@ -152,15 +152,15 @@ page.image=/design/media/navigation_between_siblings_gmail.png

Навигация между приложениями

-

Одним из фундаментальных достоинств системы Android является способность приложений активировать друг -друга, что дает пользователю возможность переходить непосредственно из одного приложения в другое. Например, +

Одним из фундаментальных достоинств системы Android является способность взаимного запуска приложений, +что дает пользователю возможность переходить непосредственно из одного приложения в другое. Например, приложение, которому нужно сделать снимок, может активировать приложение Camera, которое передаст фотографию -вызвавшему приложению. Это огромное преимущество, как для разработчика, имеющего возможность без проблем воспользоваться +вызвавшему его приложению. Это огромное преимущество как для разработчика, имеющего возможность без проблем воспользоваться кодом других приложений, так и для пользователя, получающего согласованный интерфейс для часто выполняемых действий.

-

Чтобы разобраться в навигации между приложениями, необходимо разобраться в поведении платформы Android, -которое обсуждается ниже.

+

Чтобы разобраться в навигации между приложениями, необходимо понять поведение платформы Android, + обсуждаемое ниже.

Действия, задачи и намерения

@@ -168,28 +168,28 @@ page.image=/design/media/navigation_between_siblings_gmail.png информацией и все действия, которые при этом может выполнить пользователь. Приложение представляет собой набор действий, состоящий как из действий, созданных разработчиком, так и из тех, которые выполняются с помощью других приложений.

-

Задача — последовательность действий, выполняемых пользователем для достижения цели. Отдельная +

Задача — это последовательность действий, выполняемых пользователем для достижения цели. Отдельная задача может использовать действия, заимствованные у одного или у нескольких различных приложений.

-

Намерение — механизм, позволяющий приложению сигнализировать, что ему требуется помощь +

Намерение — это механизм, позволяющий приложению сигнализировать, что ему требуется помощь другого приложения в выполнении некоторого действия. Действия, выполняемые приложением, могут указывать, на какие намерения -они готовы отвечать. Для осуществления достаточно распространенных намерений, например, "Поделиться", у пользователя может быть установлено несколько приложений, +они готовы отвечать. Для осуществления достаточно распространенных намерений, например "Поделиться", у пользователя может быть установлено несколько приложений, способных выполнить соответствующий запрос.

Пример: навигация между приложениями для поддержки совместного использования ресурсов

Чтобы понять, как действия, задачи и намерения взаимодействуют друг с другом, разберемся, как одно приложение позволяет пользователям -поделиться содержимым с помощью другого приложения. Например, запуск приложения Play Store из главного экрана создает -новую задачу, Task A (см. рисунок ниже). Когда пользователь выполнит навигацию по Play Store и коснется интересующей его книги, -чтобы просмотреть информацию о ней, он останется в том же приложении, расширив его возможности с помощью добавленных действий. Запуск +поделиться содержимым с помощью другого приложения. Например, при запуске приложения Play Store с главного экрана создается +новая задача, Task A (см. рисунок ниже). Когда пользователь выполнит навигацию по Play Store и коснется интересующей его книги, +чтобы просмотреть информацию о ней, он остается в той же задаче, расширяя ее с помощью добавленных действий. Запуск действия "Поделиться" выводит перед пользователем диалоговое окно со списком действий (из разных приложений), зарегистрированных для выполнения намерения "Поделиться".

Если пользователь предпочтет поделиться информацией через Gmail, действие "Написать" приложения Gmail добавляется как продолжение задачи -Task A—, и никакая новая задача не создается. Если в фоновом режиме работает собственная задача Gmail, на нее это никак +Task A, и никакая новая задача не создается. Если в фоновом режиме работает собственная задача Gmail, на нее это никак не повлияет.

Если во время действия "Составление сообщения" пользователь отправит сообщение или коснется кнопки "Назад", он вернется к @@ -199,7 +199,7 @@ Store, пока, наконец, не вернется на главный эк

Однако, коснувшись кнопки "Вверх" во время действия "Составление сообщения", пользователь выскажет пожелание остаться в приложении -Gmail. Откроется экран действия "Переписка" приложения Gmail, и для него будет создано новая задача Task B. Новые задачи +Gmail. Откроется экран действия "Переписка" приложения Gmail, и для него будет создана новая задача Task B. Новые задачи всегда имеют корень на главном экране, поэтому касание кнопки "Назад" на экране переписки возвращает пользователя именно туда.

diff --git a/docs/html-intl/intl/ru/guide/components/activities.jd b/docs/html-intl/intl/ru/guide/components/activities.jd new file mode 100644 index 0000000000000000000000000000000000000000..5f55a35213503fd533294699a9f6f92e5f080e2a --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Операции +page.tags=операция,намерение +@jd:body + + + + + +

{@link android.app.Activity} — это компонент приложения, который выдает экран, + и с которым пользователи могут взаимодействовать для выполнения каких-либо действий, например набрать номер телефона, сделать фото, отправить письмо или +просмотреть карту. Каждой операции присваивается окно для прорисовки соответствующего пользовательского интерфейса. Обычно окно +отображается во весь экран, однако его размер может быть меньше, и оно может размещаться поверх других +окон.

+ +

Как правило, приложение состоит из нескольких операций, которые слабо +связаны друг с другом. Обычно одна из операций в приложении обозначается как «основная», +предлагаемая пользователю при первом запуске приложения. В свою очередь, каждая +операция может запустить другую операцию для выполнения различных действий. Каждый раз, когда +запускается новая операция, предыдущая операция останавливается, однако система сохраняет ее +в стеке («стек переходов назад»). При запуске новой операции она помещается в стек переходов назад и +отображается для пользователя. Стек переходов назад работает по принципу «последним вошёл — первым вышел», +поэтому после того как пользователь завершил текущую операцию и нажал кнопку Назад, текущая операция +удаляется из стека (и уничтожается), и возобновляется предыдущая операция. (Подробные сведения о стеке +переходов назад представлены в статье Задачи и стек +переходов назад.)

+ +

Когда операция останавливается по причине запуска новой операции, для уведомления об изменении ее состояния +используются методы обратного вызова жизненного цикла операции. +Существует несколько таких методов, которые может принимать операция вследствие изменения своего состояния +— создание операции, ее остановка, возобновление или уничтожение системой; также +каждый обратный вызов представляет возможность выполнить определенное действие, +подходящее для соответствующего изменения состояния. Например, в случае остановки операция должна освободить любые +крупные объекты, например, подключение к сети или базе данных. При возобновлении операции вы можете +повторно получить необходимые ресурсы и возобновить выполнение прерванных действий. Такие изменения состояния +являются частью жизненного цикла операции.

+ +

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

+ + + +

Создание операции

+ +

Чтобы создать операцию, сначала необходимо создать подкласс класса {@link android.app.Activity} (или +воспользоваться существующим его подклассом). В таком подклассе необходимо реализовать методы обратного вызова, которые +вызывает система при переходе операции из одного состояния своего жизненного цикла в другое, например при +создании, остановке, возобновлении или уничтожении операции. Вот два наиболее важных метода +обратного вызова:

+ +
+
{@link android.app.Activity#onCreate onCreate()}
+
Этот метод необходимо обязательно реализовать, поскольку система вызывает его при создании вашей +операции. В своей реализации вам необходимо инициализировать ключевые компоненты +операции. + Наиболее важно именно здесь вызвать {@link android.app.Activity#setContentView +setContentView()} для определения макета пользовательского интерфейса операции.
+
{@link android.app.Activity#onPause onPause()}
+
Система вызывает этот метод в качестве первого признака выхода пользователя из +операции (однако это не всегда означает, что операция будет уничтожена). Обычно именно здесь +необходимо применять любые изменения, которые должны быть сохранены помимо текущего сеанса работы пользователя (поскольку +пользователь может не вернуться назад).
+
+ +

Существуют также и некоторые другие методы обратного вызова жизненного цикла, которые необходимо использовать для того, чтобы обеспечить +плавный переход между операциями, а также для обработки непредвиденных нарушений в работе операции, в результате которых она может быть +остановлена или даже уничтожена. Более подробное описание всех методов обратного вызова жизненного цикла представлены в разделе, +посвященном управлению жизненным циклом операций.

+ + + +

Реализация пользовательского интерфейса

+ +

Для реализации пользовательского интерфейса операции используется иерархия представлений —объектов, полученных из +класса {@link android.view.View}. Каждое представление отвечает за определенную прямоугольную область +окна операции и может реагировать на действия пользователей. Например, представлением может быть +кнопка, нажатие на которую приводит к выполнению определенного действия.

+ +

В Android предусмотрен набор уже готовых представлений, которые можно использовать для создания дизайна макета и его +организации. Виджеты — это представления с визуальными (и интерактивными) элементами, например, +кнопками, текстовыми полями, чекбоксами или просто изображениями. Макеты — это представления, полученные из класса {@link +android.view.ViewGroup}, обеспечивающие уникальную модель компоновки для своих дочерних представлений, таких как линейный +макет, сетка или относительный макет. Также можно создать подкласс для классов {@link android.view.View} и +{@link android.view.ViewGroup} (или воспользоваться существующими подклассами), чтобы создать собственные виджеты и +макеты, и затем применить их к макету своей операции.

+ +

Чаще всего для задания макета с помощью представлений используется XML-файл макета, сохраненный в +ресурсах приложения. Таким образом вы можете хранить дизайн пользовательского интерфейса отдельно от +исходного кода, который служит для задания поведения операции. Чтобы задать макет в качестве пользовательского интерфейса +операции, можно использовать метод {@link android.app.Activity#setContentView(int) setContentView()}, передав в него +идентификатор ресурса для макета. Однако вы также можете создать новые {@link android.view.View} в коде вашей +операции и создать иерархию представлений. Для этого вставьте {@link +android.view.View} в {@link android.view.ViewGroup}, а затем используйте этот макет, передав корневой объект +{@link android.view.ViewGroup} в метод {@link android.app.Activity#setContentView(View) +setContentView()}.

+ +

Подробные сведения о создании пользовательского интерфейса см. в статье Пользовательский интерфейс.

+ + + +

Объявление операции в манифесте

+ +

Чтобы операция стала доступна системе, ее необходимо объявить в файле +манифеста. Для этого откройте файл манифеста и добавьте элемент {@code <activity>} в +качестве дочернего для элемента +{@code <application>}. Например:

+ +
+<manifest ... >
+  <application ... >
+      <activity android:name=".ExampleActivity" />
+      ...
+  </application ... >
+  ...
+</manifest >
+
+ +

Существует несколько других атрибутов, которые можно включить в этот элемент, чтобы определить такие +свойства, как метка операции, значок операции или тема оформления для пользовательского интерфейса операции. +Единственным обязательным атрибутом является {@code android:name} +— он определяет имя класса операции. После +публикации вашего приложения вам не следует переименовывать его, поскольку это может нарушить некоторые +функциональные возможности приложения, например, ярлыки приложения (ознакомьтесь с публикацией Вещи +, которые нельзя менять в блоге разработчиков).

+ +

Дополнительные сведения об объявлении операции +в манифесте см. в справке по элементу {@code <activity>}.

+ + +

Использование фильтров намерений

+ +

Элемент {@code +<activity>} также может задавать различные фильтры намерений — с помощью элемента {@code +<intent-filter>} — для объявления того, как другие компоненты приложения +могут активировать операцию.

+ +

При создании нового приложения с помощью инструментов Android SDK в заготовке операции, +создаваемой автоматически, имеется фильтр намерений, который объявляет операцию. +Эта операция реагирует на выполнение «основного» действия, и ее следует поместить в категорию переходсредства запуска. Фильтр намерений +выглядит следующим образом.

+ +
+<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
+    <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+</activity>
+
+ +

Элемент {@code +<action>} указывает, что это «основная» точка входа в приложение. Элемент {@code +<category>}указывает, что эту операцию следует указать в +средстве запуска приложений системы (чтобы пользователи могли запускать эту операцию).

+ +

Если приложение планируется создать самодостаточным и запретить другим приложениям активировать +его операции, то других фильтров намерений создавать не нужно. В этом случае только в одной операции +должно иметься «основное» действие, и ее следует поместить в категорию средства запуска, как в примере выше. В операциях, +которые не должны быть доступны для других приложений, не следует включать фильтры намерений. +Вы можете самостоятельно запустить такие операции с помощью явных намерений (как описывается в следующем разделе).

+ +

Однако, если вам необходимо, чтобы операция реагировала на неявные намерения, +получаемые от других приложений (а также из вашего приложения), для операции необходимо определить дополнительные фильтры +намерений. Для каждого типа намерения, на который необходимо реагировать, необходимо указать объект {@code +<intent-filter>}, включающий элемент +{@code +<action>} и необязательный элемент {@code +<category>} или {@code +<data>} (или оба этих элемента). Эти элементы определяют тип намерения, на который может реагировать +ваша операция.

+ +

Дополнительные сведения о том, как операции могут реагировать на намерения, приведены в статье Намерения и фильтры намерений. +

+ + + +

Запуск операции

+ +

Для запуска другой операции достаточно вызвать метод {@link android.app.Activity#startActivity +startActivity()}, передав в него объект {@link android.content.Intent}, который описывает запускаемую +операцию. В намерении указывается либо точная операция для запуска, либо описывается +тип операции, которую вы хотите выполнить (после чего система выбирает для вас подходящую +операцию, которая +может даже находиться в другом приложении). Намерение также может содержать небольшой объем данных, +которые будут использоваться запущенной операцией.

+ +

При работе с собственным приложением зачастую требуется лишь запустить нужную операцию. + Для этого необходимо создать намерение, которое явно определяет требуемую операцию +с помощью имени класса. Ниже представлен пример запуска одной операцией другой операции с именем {@code +SignInActivity}.

+ +
+Intent intent = new Intent(this, SignInActivity.class);
+startActivity(intent);
+
+ +

Однако в вашем приложении также может потребоваться выполнить некоторое действие, например, отправить письмо, текстовое +сообщение или обновить статус, используя данные из вашей операции. В этом случае в вашем приложении могут +отсутствовать такие действия, поэтому вы можете воспользоваться операциями +из других приложений, имеющихся на устройстве, которые могут выполнять требуемые действия. Как раз в этом случае +намерения особенно полезны — можно создать намерение, которое описывает необходимое действие, +после чего система +запускает его из другого приложения. При наличии +нескольких операций, которые могут обработать намерение, пользователь может выбирать, какое из них следует использовать. Например, +если пользователю требуется предоставить возможность отправить электронное письмо, можно создать +следующее намерение:

+ +
+Intent intent = new Intent(Intent.ACTION_SEND);
+intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
+startActivity(intent);
+
+ +

Дополнительный компонент {@link android.content.Intent#EXTRA_EMAIL}, добавленный в намерение, представляет собой строковый массив +адресов электронной почты для отправки письма. Когда почтовая программа реагирует на это +намерение, она считывает дополнительно добавленный строковый массив и помещает имеющиеся в нем адреса в поле получателя +в окне создания письма. При этом запускается операция почтовой программы, а после того, как +пользователь завершит требуемые действия, возобновляется ваша операция.

+ + + + +

Запуск операции для получения результата

+ +

В некоторых случаях после запуска операции может потребоваться получить результат. Для этого +вызовите метод {@link android.app.Activity#startActivityForResult +startActivityForResult()} (вместо {@link android.app.Activity#startActivity +startActivity()}). Чтобы получить результат после выполнения последующей +операции, реализуйте метод обратного +вызова {@link android.app.Activity#onActivityResult onActivityResult()}. По завершении последующей операции она возвращает результат в объекте {@link +android.content.Intent} +в вызванный метод {@link android.app.Activity#onActivityResult onActivityResult()}.

+ +

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

+ +
+private void pickContact() {
+    // Create an intent to "pick" a contact, as defined by the content provider URI
+    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+    startActivityForResult(intent, PICK_CONTACT_REQUEST);
+}
+
+@Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
+    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
+        // Perform a query to the contact's content provider for the contact's name
+        Cursor cursor = getContentResolver().query(data.getData(),
+        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
+        if (cursor.moveToFirst()) { // True if the cursor is not empty
+            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
+            String name = cursor.getString(columnIndex);
+            // Do something with the selected contact's name...
+        }
+    }
+}
+
+ +

В этом примере демонстрируется базовая логика, которой следует руководствоваться при использовании метода {@link +android.app.Activity#onActivityResult onActivityResult()} для обработки результата +выполнения операции. Первое условие проверяет, успешен ли запрос, и если он успешен, то результат для +{@code resultCode} будет {@link android.app.Activity#RESULT_OK}; также проверяется, известен ли запрос, +для которого получен этот результат, и в этом случае {@code requestCode} соответствует +второму параметру, отправленному в метод {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Здесь код обрабатывает результат выполнения операции путем запроса данных, +возвращенных в {@link android.content.Intent} (параметр {@code data}).

+ +

При этом {@link +android.content.ContentResolver} выполняет запрос к поставщику контента, который возвращает объект +{@link android.database.Cursor}, обеспечивающий считывание запрошенных данных. Дополнительные сведения представлены в статье +Поставщики контента.

+ +

Дополнительные сведения об использовании намерений см. в статье Намерения и фильтры +намерений.

+ + +

Завершение операции

+ +

Для завершения операции достаточно вызвать ее метод {@link android.app.Activity#finish +finish()}. Также для завершения отдельной операции, запущенной ранее, можно вызвать метод +{@link android.app.Activity#finishActivity finishActivity()}.

+ +

Примечание. В большинстве случаев вам не следует явно завершать операцию +с помощью этих методов. Как указано в следующем разделе, посвященном управлению жизненным циклом операции, система +Android выполняет такое управление за вас, поэтому вам не нужно завершать ваши +собственные операции. Вызов этих методов может отрицательно сказаться на ожидаемом +поведении приложения. Их следует использовать исключительно тогда, когда вы абсолютно не хотите, чтобы пользователь возвращался к этому +экземпляру операции.

+ + +

Управление жизненным циклом операций

+ +

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

+ +

Существует всего три состояния операции:

+ +
+
Возобновлена
+
Операция выполняется на переднем плане экрана и отображается для пользователя. (Это состояние +также иногда называется «Выполняется».)
+ +
Приостановлена
+
На переднем фоне выполняется другая операция, которая отображается для пользователя, однако эта операция по-прежнему не скрыта. То есть +поверх текущей операции отображается другая операция, частично прозрачная или не занимающая +полностью весь экран. Приостановленная операция полностью активна (объект {@link android.app.Activity} +по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, и он также остается связанным с +диспетчером окон), однако в случае острой нехватки памяти система может завершить ее.
+ +
Остановлена
+
Операция полностью перекрывается другой операцией (теперь она выполняется в +фоновом режиме). Остановленная операция по-прежнему активна (объект {@link android.app.Activity} +по-прежнему находится в памяти, в нем сохраняются все сведения о состоянии и информация об элементах, но объект больше не +связан с диспетчером окон). Однако операция больше не видна пользователю, и в случае нехватки памяти система +может завершить ее.
+
+ +

Если операция приостановлена или полностью остановлена, система может очистить ее из памяти путем +завершения самой операции (с помощью метода {@link android.app.Activity#finish finish()}) или просто завершить ее +процесс. В случае повторного открытия операции (после ее завершения) ее потребуется создать +полностью.

+ + + +

Реализация обратных вызовов жизненного цикла

+ +

При переходе операции из одного вышеописанного состояния в другое, уведомления об этом +реализуются через различные методы обратного вызова. Все методы обратного вызова представляют собой привязки, которые +можно переопределить для выполнения подходящего действия при изменении состояния операции. Указанная ниже базовая +операция включает каждый из основных методов жизненного цикла.

+ + +
+public class ExampleActivity extends Activity {
+    @Override
+    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // The activity is being created.
+    }
+    @Override
+    protected void {@link android.app.Activity#onStart onStart()} {
+        super.onStart();
+        // The activity is about to become visible.
+    }
+    @Override
+    protected void {@link android.app.Activity#onResume onResume()} {
+        super.onResume();
+        // The activity has become visible (it is now "resumed").
+    }
+    @Override
+    protected void {@link android.app.Activity#onPause onPause()} {
+        super.onPause();
+        // Another activity is taking focus (this activity is about to be "paused").
+    }
+    @Override
+    protected void {@link android.app.Activity#onStop onStop()} {
+        super.onStop();
+        // The activity is no longer visible (it is now "stopped")
+    }
+    @Override
+    protected void {@link android.app.Activity#onDestroy onDestroy()} {
+        super.onDestroy();
+        // The activity is about to be destroyed.
+    }
+}
+
+ +

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

+ +

Вместе все эти методы определяют весь жизненный цикл операции. С помощью реализации этих +методов можно отслеживать три вложенных цикла в жизненном цикле операции:

+ +
    +
  • Весь жизненный цикл операции происходит между вызовом метода {@link +android.app.Activity#onCreate onCreate()} и вызовом метода {@link +android.app.Activity#onDestroy}. Ваша операция должна выполнить настройку +«глобального» состояния (например, определение макета) в методе {@link android.app.Activity#onCreate onCreate()}, а затем +освободить все оставшиеся в {@link android.app.Activity#onDestroy} ресурсы. Например, если в вашей операции +имеется поток, выполняющийся в фоновом режиме, для загрузки данных по сети, операция может создать +такой поток в методе {@link android.app.Activity#onCreate onCreate()}, а затем остановить его в методе {@link +android.app.Activity#onDestroy}.
  • + +
  • Видимый жизненный цикл операции происходит между вызовами методов {@link +android.app.Activity#onStart onStart()} и {@link +android.app.Activity#onStop onStop()}. В течение этого времени операция +отображается на экране, где пользователь может взаимодействовать с ней. Например, метод {@link android.app.Activity#onStop onStop()} вызывается в +случае, когда запускается новая операция, а текущая больше не отображается. В промежутке между вызовами этих двух методов можно +сохранить ресурсы, необходимые для отображения операции для пользователя. Например, можно зарегистрировать объект +{@link android.content.BroadcastReceiver} в методе {@link +android.app.Activity#onStart onStart()} для отслеживания изменений, влияющих на пользовательский интерфейс, а затем отменить его регистрацию +в методе {@link android.app.Activity#onStop onStop()}, когда пользователь больше не видит +отображаемого. В течение всего жизненного цикла операции система может несколько раз вызывать методы {@link android.app.Activity#onStart onStart()} и {@link +android.app.Activity#onStop onStop()}, поскольку +операция то отображается для пользователя, то скрывается от него.

  • + +
  • Жизненный цикл операции, выполняемый на переднем плане, происходит между вызовами методов {@link +android.app.Activity#onResume onResume()} и {@link android.app.Activity#onPause +onPause()}. В течение этого времени операция выполняется на фоне всех прочих операций и +отображается для пользователя. Операция может часто уходить в фоновый режим и выходить из него — например, +метод {@link android.app.Activity#onPause onPause()} вызывается при переходе устройства в спящий режим или +при появлении диалогового окна. Поскольку переход в это состояние может выполняться довольно часто, код в этих двух методах +должен быть легким, чтобы не допустить медленных переходов и не заставлять пользователя ждать.

  • +
+ +

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

+ + +

Рисунок 1. Жизненный цикл операции.

+ +

Эти же методы жизненного цикла перечислены в таблице 1, в которой подробно описан каждый метод +обратного вызова и указано его место в +жизненном цикле операции в целом, включая сведения о том, может ли система завершить операцию по завершении +метода обратного вызова.

+ +

Таблица 1. Сводные сведения о методах обратного вызова +жизненного цикла операции.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Метод Описание Завершаемый? Следующий
{@link android.app.Activity#onCreate onCreate()}Вызывается при первом создании операции. + Здесь необходимо настроить все обычные статические элементы — +создать представления, привязать данные и т. д. Этот метод передает +объект Bundle, содержащий предыдущее состояние операции (если +такое состояние было зафиксировано ранее; см. раздел Сохранение состояния +операции). +

За ним всегда следует метод {@code onStart()}.

Нет{@code onStart()}
    {@link android.app.Activity#onRestart +onRestart()}Вызывается после остановки операции непосредственно перед ее +повторным запуском. +

За ним всегда следует метод {@code onStart()}.

Нет{@code onStart()}
{@link android.app.Activity#onStart onStart()}Вызывается непосредственно перед тем, как операция становится видимой для пользователя. +

За ним следует метод {@code onResume()}, если операция +переходит на передний план, или метод {@code onStop()}, если она становится скрытой.

Нет{@code onResume()}
или
{@code onStop()}
    {@link android.app.Activity#onResume onResume()}Вызывается непосредственно перед тем, как операция +начинает взаимодействие с пользователем. На этом этапе операция находится в самом +верху стека операций, и в нее поступают данные, вводимые пользователем. +

За ним всегда следует метод {@code onPause()}.

Нет{@code onPause()}
{@link android.app.Activity#onPause onPause()}Вызывается, когда система собирается возобновить +другую операцию. Этот метод обычно используется для записи несохраненных изменений +в постоянное место хранения данных, остановки анимаций и других элементов, которые могут +использовать ресурсы ЦП и т. д. Здесь крайне важна оперативность, поскольку +следующая операция не будет возобновлена до тех пор, пока она не будет возвращена на передний план. +

За ним следует либо метод {@code onResume()}, если операция +возвращается на передний план, либо метод {@code onStop()}, если операция +становится скрытой для пользователя.

Да{@code onResume()}
или
{@code onStop()}
{@link android.app.Activity#onStop onStop()}Вызывается в случае, когда операция больше не отображается для пользователя. Это может +произойти по причине того, что операция уничтожена, или ввиду возобновления поверх нее другой операции +(существующей или новой). +

За ним следует либо метод {@code onRestart()}, если +операция возобновляет взаимодействие с пользователем, либо метод +{@code onDestroy()}, если операция переходит в фоновый режим.

Да{@code onRestart()}
или
{@code onDestroy()}
{@link android.app.Activity#onDestroy +onDestroy()}Вызывается перед тем, как операция будет уничтожена. Это финальный вызов, +который получает операция. Его можно вызвать либо по причине +завершения операции (вызов метода {@link android.app.Activity#finish + finish()}), либо ввиду временного уничтожения системой этого +экземпляра операции с целью освободить место. Чтобы различить эти два +сценария, используется метод {@link + android.app.Activity#isFinishing isFinishing()}.ДаНичего
+ +

В столбце «Завершаемый?» указывается, может ли система +в любое время завершить процесс, содержащий операцию, после возвращения метода без +выполнения другой строки кода операции. Для трех методов в этом столбце указано «Да»: ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()} и {@link android.app.Activity#onDestroy +onDestroy()}). Поскольку метод {@link android.app.Activity#onPause onPause()} является первым из +этих трех после создания операции, метод {@link android.app.Activity#onPause onPause()} является +последним, который гарантированно будет вызван перед тем, как процесс можно будет завершить; если +системе потребуется срочно восстановить память в случае аварийной ситуации, то методы {@link +android.app.Activity#onStop onStop()} и {@link android.app.Activity#onDestroy onDestroy()} вызвать +не удастся. Поэтому следует воспользоваться {@link android.app.Activity#onPause onPause()}, чтобы записать +критически важные данные (такие как правки пользователя) в хранилище постоянных данных. Однако следует внимательно подходить к выбору информации, +которую необходимо сохранить во время выполнения метода {@link android.app.Activity#onPause onPause()}, поскольку любая блокировка +процедур в этом методе может вызвать блокирование перехода к следующей операции и тормозить работу +пользователя.

+ +

Методы, для которых в столбце Завершаемый? указано «Нет», защищают процесс, содержащий операцию +, от завершения сразу с момента их вызова. Поэтому завершить операцию +можно в период между возвратом {@link android.app.Activity#onPause onPause()} и вызовом +{@link android.app.Activity#onResume onResume()}. Его снова не удастся завершить, пока снова не будет вызван и возвращен +{@link android.app.Activity#onPause onPause()}.

+ +

Примечание. Операцию, которую технически невозможно завершить в соответствии с определением +в таблице 1, по-прежнему может завершить система, однако это может произойти только в +чрезвычайных ситуациях, когда нет другой возможности. Случаи, когда возможно завершение операции, +более подробно рассматриваются в статьеПроцессы и +потоки.

+ + +

Сохранение состояния операции

+ +

В обзорных сведениях об управлении жизненным циклом операции кратко упоминается, +что +в случае приостановки или полной остановки операции ее состояние сохраняется. Это действительно так, поскольку +объект {@link android.app.Activity} при этом по-прежнему находится в памяти +, и вся информация о ее элементах и текущем состоянии по-прежнему активна. Поэтому любые +вносимые пользователем в операции изменения сохраняются, и когда операция возвращается на передний план +(когда она «возобновляется»), эти изменения остаются в этом объекте.

+ +

Однако когда система уничтожает операцию в целях восстановления памяти, объект {@link +android.app.Activity} уничтожается, в результате чего системе не удается просто восстановить состояние операции для взаимодействия +с ней. Вместо этого системе необходимо повторно создать объект {@link android.app.Activity}, если пользователь +возвращается к нему. Но пользователю неизвестно, +что система уже уничтожила операцию и создала ее повторно, поэтому, возможно, +он ожидает, что операция осталась прежней. В этой ситуации можно обеспечить +сохранение важной информации о состоянии операции путем реализации дополнительного +метода обратного вызова, который позволяет сохранить информацию о вашей операции: {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}.

+ +

Прежде чем сделать операцию доступной для уничтожения, система вызывает метод +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Система передает в этот метод объект +{@link android.os.Bundle}, в котором можно сохранить +информацию о состоянии операции в виде пар «имя-значение», используя для этого такие методы, как {@link +android.os.Bundle#putString putString()} и {@link +android.os.Bundle#putInt putInt()}. Затем, если система завершает процесс +вашего приложения и пользователь возвращается к вашей операции, система повторно создает операцию и передает объект +{@link android.os.Bundle} в оба метода: {@link android.app.Activity#onCreate onCreate()} и {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}. С помощью любого из этих +методов можно извлечь из объекта {@link android.os.Bundle} сохраненную информацию о состоянии операции и восстановить +ее. Если такая информация отсутствует, то объект {@link +android.os.Bundle} передается с нулевым значением (это происходит в случае, когда операция +создается в первый раз).

+ + +

Рисунок 2. Два способа возврата операции к отображению для пользователя +в неизмененном состоянии: уничтожение операции с последующим ее повторным созданием, когда операция должна восстановить свое +ранее сохраненное состояние, или остановка операции и ее последующее восстановление в неизмененном +состоянии.

+ +

Примечание. Нет никаких гарантий, что метод {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} будет вызван до того, как ваша +операция будет уничтожена, поскольку существуют случаи, когда нет необходимости сохранять состояние +(например, когда пользователь покидает вашу операцию нажатием кнопки Назад, +явным образом +закрывая ее). Если система вызывает метод {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, она делает это до вызова метода {@link +android.app.Activity#onStop onStop()} и, возможно, перед вызовом метода {@link android.app.Activity#onPause +onPause()}.

+ +

Однако, даже если вы ничего не предпринимаете и не реализуете метод {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, часть состояния операции восстанавливается +реализацией по умолчанию метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} класса {@link android.app.Activity}. В частности, реализация +по умолчанию вызывает соответствующий метод {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} для каждого объекта {@link +android.view.View} в макете, благодаря чему каждое представление может предоставлять ту информацию о себе, +которую следует сохранить. Почти каждый виджет в платформе Android реализует этот метод необходимым +для себя способом так, что любые видимые изменения в пользовательском интерфейсе автоматически сохраняются и восстанавливаются при повторном +создании операции. Например, виджет {@link android.widget.EditText} сохраняет любой текст, +введенный пользователем, а виджет {@link android.widget.CheckBox} сохраняет информацию о том, был +ли установлен флажок. От вас требуется лишь указать уникальный идентификатор (с атрибутом {@code android:id}) +для каждого виджета, состояние которого необходимо сохранить. Если виджету не присвоен идентификатор, то системе +не удастся сохранить его состояние.

+ + + +

Несмотря на то что реализация метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} по умолчанию позволяет сохранить полезную информацию о +пользовательском интерфейсе вашей операции, вам по-прежнему может потребоваться переопределить ее для сохранения дополнительной информации. +Например, может потребоваться сохранить значения элементов, которые изменялись в течение жизненного цикла операции (которые могут +коррелировать со значениями, восстановленными в пользовательском интерфейсе, однако элементы, содержащие эти значения пользовательского интерфейса, по умолчанию не +были восстановлены).

+ +

Поскольку реализация метода {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} по умолчанию позволяет сохранить состояние пользовательского интерфейса, в случае +, если вы переопределите метод с целью сохранить дополнительную информацию о состоянии, перед выполнением каких-либо действий вы всегда можете вызвать реализацию +суперкласса для метода +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Точно так же реализацию суперкласса {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} следует вызывать в случае ее переопределения, чтобы реализация по умолчанию могла сохранить +состояния представлений.

+ +

Примечание. Поскольку вызов метода {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} не гарантируется, +вам следует использовать его только для записи переходного состояния операции (состояние +пользовательского интерфейса) — никогда не используйте его для хранения постоянных данных. Вместо этого используйте метод {@link +android.app.Activity#onPause onPause()} для сохранения постоянных данных (например, тех, которые следует сохранить в +базу данных), когда пользователь покидает операцию.

+ +

Отличный способ проверить возможность вашего приложения восстанавливать свое состояние — это просто повернуть +устройство для изменения ориентации экрана. При изменении ориентации экрана система +уничтожает и повторно создает операцию, чтобы применить альтернативные ресурсы, которые могут быть +доступны для новой конфигурации экрана. Только по одной этой причине крайне важно, чтобы ваша операция +могла полностью восстанавливать свое состояние при ее повторном создании, поскольку пользователи постоянно работают с +приложениями в разных ориентациях экрана.

+ + +

Обработка изменений в конфигурации

+ +

Некоторые конфигурации устройств могут изменяться в режиме выполнения +(например, ориентация экрана, доступность клавиатуры и язык). В таких случаях Android повторно создает выполняющуюся операцию +(система сначала вызывает метод {@link android.app.Activity#onDestroy}, а затем сразу же вызывает метод {@link +android.app.Activity#onCreate onCreate()}). Такое поведение позволяет +приложению учитывать новые конфигурации путем автоматической перезагрузки в приложение +альтернативных ресурсов, которые вы предоставили (например, различные макеты для +разных ориентаций и экранов разных размеров).

+ +

Если операция разработана должным образом и должным образом поддерживает перезапуск после изменения ориентации экрана и +восстановление своего состояния, как описано выше, ваше приложение можно считать более устойчивым к другим +непредвиденным событиям в жизненном цикле операции.

+ +

Лучший способ обработки такого перезапуска +— сохранить и восстановить состояние операции с помощью методов {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} и {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (или {@link +android.app.Activity#onCreate onCreate()}), как описано в предыдущем разделе.

+ +

Дополнительные сведения об изменениях конфигурации, происходящих в режиме выполнения, и о способах их обработки +представлены в руководствеОбработка изменений в режиме +выполнения.

+ + + +

Согласование операций

+ +

Когда одна операция запускает другую, в жизненных циклах обеих из них происходит переход из одного состояния в другое. Первая операция +приостанавливается и заврешается (однако она не будет остановлена, если она по-прежнему видима на фоне), а вторая +операция создается. В случае, если эти операции обмениваются данным, сохраненными на диске или в другом месте, важно понимать, +что первая операция не останавливается полностью до тех пор, пока не будет создана вторая операция. +Наоборот, процесс запуска второй операции накладывается на процесс остановки первой +операции.

+ +

Порядок обратных вызовов жизненного цикла четко определен, в частности, когда в одном и том же процессе находятся две операции +, и одна из них запускает другую. Ниже представлен порядок выполнения действий в случае, когда операция +А запускает операцию Б.

+ +
    +
  1. Выполняется метод {@link android.app.Activity#onPause onPause()} операции А.
  2. + +
  3. Последовательно выполняются методы {@link android.app.Activity#onCreate onCreate()}, {@link +android.app.Activity#onStart onStart()} и {@link android.app.Activity#onResume onResume()} +операции Б. (Теперь для пользователя отображается операция Б.)
  4. + +
  5. Затем, если операция A больше не отображается на экране, выполняется ее метод {@link +android.app.Activity#onStop onStop()}.
  6. +
+ +

Такая предсказуемая последовательность выполнения обратных вызовов жизненного цикла позволяет управлять переходом +информации из одной операции в другую. Например, если после остановки первой операции требуется выполнить запись в базу данных, +чтобы следующая операция могла считать их, то запись в базу данных следует выполнить +во время выполнения метода {@link android.app.Activity#onPause onPause()}, а не во время выполнения метода {@link +android.app.Activity#onStop onStop()}.

+ + diff --git a/docs/html-intl/intl/ru/guide/components/bound-services.jd b/docs/html-intl/intl/ru/guide/components/bound-services.jd new file mode 100644 index 0000000000000000000000000000000000000000..ad690b776ef07c419618ba636e1072421f882831 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=Привязанные службы +parent.title=Службы +parent.link=services.html +@jd:body + + +
+
    +

    Содержание документа

    +
      +
    1. Основы
    2. +
    3. Создание привязанной службы +
        +
      1. Расширение класса Binder
      2. +
      3. Использование объекта Messenger
      4. +
      +
    4. +
    5. Привязка к службе
    6. +
    7. Управление жизненным циклом привязанной службы
    8. +
    + +

    Ключевые классы

    +
      +
    1. {@link android.app.Service}
    2. +
    3. {@link android.content.ServiceConnection}
    4. +
    5. {@link android.os.IBinder}
    6. +
    + +

    Примеры

    +
      +
    1. {@code + RemoteService}
    2. +
    3. {@code + LocalService}
    4. +
    + +

    См. также:

    +
      +
    1. Службы
    2. +
    +
+ + +

Привязанная служба предоставляет интерфейс типа клиент-сервер. Привязанная служба позволяет компонентам приложения +(например, операциям) взаимодействовать со службой, отправлять запросы, получать результаты и даже делать то же самое с другими процессами через +IPC. Привязанная служба обычно работает, пока другой компонент приложения +привязан к ней. Она не работает постоянно в фоновом режиме.

+ +

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

+ + +

Основы

+ +

Привязанная служба представляет собой реализацию класса {@link android.app.Service}, которая позволяет +другим приложениям привязываться к нему и взаимодействовать с ним. Чтобы обеспечить привязку службы +, сначала необходимо реализовать метод обратного вызова {@link android.app.Service#onBind onBind()}. Этот +метод возвращает объект {@link android.os.IBinder}. Он определяет программный интерфейс, +с помощью которого клиенты могут взаимодействовать со службой.

+ + + +

Для привязки к службе клиент может вызвать метод {@link android.content.Context#bindService +bindService()}. После привязки он должен предоставить реализацию метода {@link +android.content.ServiceConnection}, который служит для отслеживания подключения к службе. Метод {@link +android.content.Context#bindService bindService()} возвращается незамедлительно без значения, однако +, когда система Android устанавливает подключение +клиент-служба, она вызывает метод {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} для {@link +android.content.ServiceConnection}, чтобы выдать объект {@link android.os.IBinder}, который +клиент может использовать для взаимодействия со службой.

+ +

Одновременно к службе могут подключиться сразу несколько клиентов. Однако система вызывает метод +{@link android.app.Service#onBind onBind()} вашей службы для получения объекта {@link android.os.IBinder} +только при первой привязке клиента. После чего система выдает такой же объект {@link android.os.IBinder} для любых +дополнительных клиентов, которые выполняют привязку, без повторного вызова метода {@link android.app.Service#onBind onBind()}.

+ +

Когда отменяется привязка последнего клиента от службы, система уничтожает службу (если только +служба не была так же запущена методом {@link android.content.Context#startService startService()}).

+ +

Самую важную роль в реализации привязанной службы играет определение интерфейса, +который возвращает ваш метод обратного вызова {@link android.app.Service#onBind onBind()}. Существует несколько +различных способов определения интерфейса {@link android.os.IBinder} службы. Каждый из них рассматривается в следующем +разделе.

+ + + +

Создание привязанной службы

+ +

При создании службы, обеспечивающей привязку, требуется объект {@link android.os.IBinder}, +который обеспечивает программный интерфейс, с помощью которого клиенты могут взаимодействовать со службой. Существует +три способа определения такого интерфейса:

+ +
+
Расширение класса Binder
+
Если служба является частной и предоставляется в рамках вашего собственного приложения, а также выполняется в том же процессе, что и клиент +(общий процесс), создавать интерфейс следует путем расширения класса {@link android.os.Binder} +и возврата его экземпляра из метода +{@link android.app.Service#onBind onBind()}. Клиент получает объект {@link android.os.Binder}, +после чего он может использовать его для получения прямого доступа к общедоступным методам, имеющимся либо в реализации {@link android.os.Binder}, +либо даже в {@link android.app.Service}. +

Этот способ является предпочтительным, когда служба просто выполняется в фоновом режиме для +вашего приложения. Этот способ не подходит для создания интерфейса только тогда, +когда ваша служба используется другими приложениями или в отдельных процессах.

+ +
Использование объекта Messenger
+
Если необходимо, чтобы интерфейс службы был доступен для разных процессов, его можно создать +с помощью объекта {@link android.os.Messenger}. Таким образом, служба +определяет объект {@link android.os.Handler}, соответствующий различным типам объектов {@link +android.os.Message}. Этот объект {@link android.os.Handler} +является основой для объекта {@link android.os.Messenger}, который, в свою очередь, предоставляет клиенту объект {@link android.os.IBinder}, +благодаря чему последний может отправлять команды в службу с помощью объектов {@link +android.os.Message}. Кроме того, клиент может определить объект {@link android.os.Messenger} для самого +себя, что позволяет службе возвращать сообщения клиенту. +

Это самый простой способ организовать взаимодействие процессов, поскольку {@link +android.os.Messenger} организует очередь всех запросов в рамках одного потока, поэтому вам не нужно делать +свою службу потокобезопасной.

+
+ +
Использование языка AIDL
+
AIDL (Android Interface Definition Language) выполняет всю работу по разделению объектов на +примитивы, которые операционная система может распознать и распределить по процессам для организации +взаимодействия между ними (IPC). Предыдущий способ с использованием объекта {@link android.os.Messenger} фактически основан на AIDL, поскольку это его +базовая структура. Как уже упоминалось выше, объект {@link android.os.Messenger} создает очередь из всех +запросов клиентов в рамках одного потока, поэтому служба одновременно получает только один запрос. Однако, +если необходимо, чтобы служба обрабатывала одновременно сразу несколько запросов, можно использовать AIDL +напрямую. В таком случае ваша служба должна поддерживать многопоточность и должна быть потокобезопасной. +

Чтобы использовать AIDL напрямую, необходимо +создать файл {@code .aidl}, который определяет программный интерфейс. Этот файл используется инструментами SDK Android для +создания абстрактного класса, который реализует интерфейс и обеспечивает взаимодействие процессов, и который +в дальнейшем можно расширить в службе.

+
+
+ +

Примечание. В большинстве приложений не следует использовать AIDL для +создания и привязки службы, поскольку для этого может потребоваться поддержка многопоточности, что, в свою очередь, может привести +к более сложной реализации. Поэтому AIDL не подходит для большинства приложений, +и в этой статье мы не будем рассматривать использование этого способа для вашей службы. Если же вы точно уверены, что +вам необходимо использовать AIDL напрямую, обратитесь к статье +AIDL.

+ + + + +

Расширение класса Binder

+ +

Если ваша служба используется только локальным приложением и не взаимодействует с разными процессами, +можно реализовать собственный класс {@link android.os.Binder}, с помощью которого клиент получает прямой +доступ к общедоступным методам в службе.

+ +

Примечание. Этот вариант подходит только в том случае, если клиент и служба выполняются внутри +одного приложения и процесса, что является наиболее распространенной ситуацией. Например, расширение класса отлично подойдет для +музыкального приложения, в котором необходимо привязать операцию к собственной службе приложения, которая +воспроизводит музыку в фоновом режиме.

+ +

Вот как это сделать:

+
    +
  1. Создайте в вашей службе экземпляр класса {@link android.os.Binder} со следующими характеристиками: +
      +
    • экземпляр содержит общедоступные методы, которые может вызывать клиент; либо
    • +
    • экземпляр возвращает текущий экземпляр класса {@link android.app.Service}, содержащий +общедоступные методы, которые может вызывать клиент; или
    • +
    • экземпляр возвращает экземпляр другого класса, размещенного в службе, содержащий общедоступные методы, +которые может вызывать клиент.
    • +
    +
  2. Верните этот экземпляр класса {@link android.os.Binder} из метода обратного вызова {@link +android.app.Service#onBind onBind()}.
  3. +
  4. В клиенте получите класс {@link android.os.Binder} от метода обратного вызова {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} и +выполните вызовы к привязанной службе с помощью предоставленных методов.
  5. +
+ +

Примечание. Служба и клиент должны выполняться в одном и том же приложении +, поскольку в этом случае клиент может транслировать возвращенный объект и надлежащим образом вызывать его API-интерфейсы. Кроме того, служба +и клиент должны выполняться в рамках одного и того же процесса, поскольку этот способ не подразумевает какого-либо +распределения по процессам.

+ +

Ниже представлен пример службы, которая предоставляет клиентам доступ к методам посредством реализации класса +{@link android.os.Binder}:

+ +
+public class LocalService extends Service {
+    // Binder given to clients
+    private final IBinder mBinder = new LocalBinder();
+    // Random number generator
+    private final Random mGenerator = new Random();
+
+    /**
+     * Class used for the client Binder.  Because we know this service always
+     * runs in the same process as its clients, we don't need to deal with IPC.
+     */
+    public class LocalBinder extends Binder {
+        LocalService getService() {
+            // Return this instance of LocalService so clients can call public methods
+            return LocalService.this;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    /** method for clients */
+    public int getRandomNumber() {
+      return mGenerator.nextInt(100);
+    }
+}
+
+ +

Объект {@code LocalBinder} предоставляет для клиентов метод {@code getService()}, чтобы они могли получить +текущий экземпляр класса {@code LocalService}. Благодаря этому клиенты могут вызывать общедоступные методы в +службе. Например, клиенты могут вызвать метод {@code getRandomNumber()} из службы.

+ +

Ниже представлен пример операции, которая выполняет привязку к классу {@code LocalService} и вызывает метод {@code getRandomNumber()} +при нажатии кнопки:

+ +
+public class BindingActivity extends Activity {
+    LocalService mService;
+    boolean mBound = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        // Bind to LocalService
+        Intent intent = new Intent(this, LocalService.class);
+        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // Unbind from the service
+        if (mBound) {
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    /** Called when a button is clicked (the button in the layout file attaches to
+      * this method with the android:onClick attribute) */
+    public void onButtonClick(View v) {
+        if (mBound) {
+            // Call a method from the LocalService.
+            // However, if this call were something that might hang, then this request should
+            // occur in a separate thread to avoid slowing down the activity performance.
+            int num = mService.getRandomNumber();
+            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /** Defines callbacks for service binding, passed to bindService() */
+    private ServiceConnection mConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName className,
+                IBinder service) {
+            // We've bound to LocalService, cast the IBinder and get LocalService instance
+            LocalBinder binder = (LocalBinder) service;
+            mService = binder.getService();
+            mBound = true;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName arg0) {
+            mBound = false;
+        }
+    };
+}
+
+ +

В примере выше показано, как клиент привязывается к службе с помощью реализации +{@link android.content.ServiceConnection} и обратного вызова {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}. В +следующем разделе представлена более подробная информация об этом процессе привязки к службе.

+ +

Примечание. В примере выше не выполняется явная отмена привязки к службе, +однако всем клиентам следует отменять привязку в соответствующие сроки (например, когда операция приостанавливается).

+ +

Примеры кода представлены в статьях, посвященных классам {@code +LocalService.java} и {@code +LocalServiceActivities.java}, в ApiDemos.

+ + + + + +

Использование объекта Messenger

+ + + +

Если необходимо, чтобы служба взаимодействовала с удаленными процессами, для предоставления интерфейса службы можно воспользоваться объектом +{@link android.os.Messenger}. Такой подход +позволяет организовать взаимодействие между процессами (IPC) без необходимости использовать AIDL.

+ +

Вот краткий обзор того, как следует использовать объект {@link android.os.Messenger}:

+ +
    +
  • Служба реализует объект {@link android.os.Handler}, который получает обратный вызов для каждого +вызова от клиента.
  • +
  • Объект {@link android.os.Handler} используется для создания объекта {@link android.os.Messenger} +(который является ссылкой на объект {@link android.os.Handler}).
  • +
  • Объект {@link android.os.Messenger} создает объект {@link android.os.IBinder}, который служба +возвращает клиентам из метода {@link android.app.Service#onBind onBind()}.
  • +
  • Клиенты используют полученный объект {@link android.os.IBinder} для создания экземпляра объекта {@link android.os.Messenger} +(который ссылается на объект {@link android.os.Handler} службы), используемого клиентом для отправки объектов +{@link android.os.Message} в службу.
  • +
  • Служба получает каждый объект {@link android.os.Message} в своем объекте {@link +android.os.Handler} — в частности, в методе {@link android.os.Handler#handleMessage +handleMessage()}.
  • +
+ + +

Таким образом, для клиента отсутствуют «методы» для отправки вызова службе. Вместо этого +клиент отправляет «сообщения» (объекты {@link android.os.Message}), которые служба получает в +своем объекте {@link android.os.Handler}.

+ +

Ниже представлен пример службы, которая использует интерфейс {@link android.os.Messenger}:

+ +
+public class MessengerService extends Service {
+    /** Command to the service to display a message */
+    static final int MSG_SAY_HELLO = 1;
+
+    /**
+     * Handler of incoming messages from clients.
+     */
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SAY_HELLO:
+                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    /**
+     * Target we publish for clients to send messages to IncomingHandler.
+     */
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * When binding to the service, we return an interface to our messenger
+     * for sending messages to the service.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
+        return mMessenger.getBinder();
+    }
+}
+
+ +

Обратите внимание, что метод {@link android.os.Handler#handleMessage handleMessage()} в объекте +{@link android.os.Handler} — это место, где служба получает входящие объекты {@link android.os.Message} +и решает, что делать дальше, руководствуясь элементом {@link android.os.Message#what}.

+ +

Клиенту требуется лишь создать объект {@link android.os.Messenger} на основе объекта {@link +android.os.IBinder}, возвращенного службой, и отправить сообщение с помощью метода {@link +android.os.Messenger#send send()}. Ниже представлен пример того, как простая операция выполняет привязку к +службе и отправляет ей сообщение {@code MSG_SAY_HELLO}:

+ +
+public class ActivityMessenger extends Activity {
+    /** Messenger for communicating with the service. */
+    Messenger mService = null;
+
+    /** Flag indicating whether we have called bind on the service. */
+    boolean mBound;
+
+    /**
+     * Class for interacting with the main interface of the service.
+     */
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            // This is called when the connection with the service has been
+            // established, giving us the object we can use to
+            // interact with the service.  We are communicating with the
+            // service using a Messenger, so here we get a client-side
+            // representation of that from the raw IBinder object.
+            mService = new Messenger(service);
+            mBound = true;
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            // This is called when the connection with the service has been
+            // unexpectedly disconnected -- that is, its process crashed.
+            mService = null;
+            mBound = false;
+        }
+    };
+
+    public void sayHello(View v) {
+        if (!mBound) return;
+        // Create and send a message to the service, using a supported 'what' value
+        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
+        try {
+            mService.send(msg);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        // Bind to the service
+        bindService(new Intent(this, MessengerService.class), mConnection,
+            Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // Unbind from the service
+        if (mBound) {
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
+}
+
+ +

Обратите внимание, что в этом примере не показано, как служба отвечает клиенту. Если требуется, +чтобы служба реагировала, необходимо создать объект {@link android.os.Messenger} и в клиенте. Затем, +когда клиент получает обратный вызов {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, она отправляет в службу объект {@link android.os.Message}, который включает объект +{@link android.os.Messenger} клиента в качестве значения параметра {@link android.os.Message#replyTo} +метода {@link android.os.Messenger#send send()}.

+ +

Пример организации двустороннего обмена сообщениями приведен в примерах кода {@code +MessengerService.java} (служба) и {@code +MessengerServiceActivities.java} (клиент).

+ + + + + +

Привязка к службе

+ +

Для привязки к службе компоненты приложения (клиенты) могут использовать метод +{@link android.content.Context#bindService bindService()}. После этого система Android +вызывает метод {@link android.app.Service#onBind +onBind()} службы, который возвращает объект {@link android.os.IBinder} для взаимодействия со службой.

+ +

Привязка выполняется асинхронно. {@link android.content.Context#bindService +bindService()} возвращается сразу же и не возвращает клиенту объект +{@link android.os.IBinder}. Для получения объекта {@link android.os.IBinder} клиенту необходимо создать экземпляр {@link +android.content.ServiceConnection} и передать его в метод {@link android.content.Context#bindService +bindService()}. Интерфейс {@link android.content.ServiceConnection} включает метод обратного вызова, +который система использует для того, чтобы выдать объект {@link android.os.IBinder}.

+ +

Примечание. Выполнить привязку к службе могут только операции, другие службы и поставщики контента +— вы не можете самостоятельно выполнить привязку к службе из ресивера.

+ +

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

+
    +
  1. Реализуйте интерфейс {@link android.content.ServiceConnection}. +

    Ваша реализация должна переопределять два метода обратного вызова:

    +
    +
    {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
    +
    Система вызывает этот метод, чтобы выдать объект {@link android.os.IBinder}, возвращенный методом +{@link android.app.Service#onBind onBind()}службы.
    +
    {@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}
    +
    Система Android вызывает этот метод в случае непредвиденной потери +подключения к службе, например при сбое в работе службы или в случае ее завершения. Этот метод не вызывается, когда клиент +отменяет привязку.
    +
    +
  2. +
  3. Вызовите метод {@link +android.content.Context#bindService bindService()}, передав в него реализацию интерфейса {@link +android.content.ServiceConnection}.
  4. +
  5. Когда система вызывает ваш метод обратного вызова {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, вы можете приступить к выполнению вызовов к службе с помощью +методов, определенных интерфейсом.
  6. +
  7. Чтобы отключиться от службы, вызовите метод {@link +android.content.Context#unbindService unbindService()}. +

    В случае уничтожения клиента выполняется отмена его привязки к службе, однако вам всегда следует отменять +привязку по завершении взаимодействия со службой или в случае приостановки операции, чтобы служба +могла завершить свою работу, когда она не используется. (Более подробно подходящее время для привязки и ее отмены +рассматриваются далее в этом документе).

    +
  8. +
+ +

Ниже представлен пример фрагмента кода для подключения клиента к созданной выше службе путем +расширения класса Binder — клиенту нужно лишь передать возвращенный объект +{@link android.os.IBinder} в класс {@code LocalService} и запросить экземпляр {@code +LocalService}:

+ +
+LocalService mService;
+private ServiceConnection mConnection = new ServiceConnection() {
+    // Called when the connection with the service is established
+    public void onServiceConnected(ComponentName className, IBinder service) {
+        // Because we have bound to an explicit
+        // service that is running in our own process, we can
+        // cast its IBinder to a concrete class and directly access it.
+        LocalBinder binder = (LocalBinder) service;
+        mService = binder.getService();
+        mBound = true;
+    }
+
+    // Called when the connection with the service disconnects unexpectedly
+    public void onServiceDisconnected(ComponentName className) {
+        Log.e(TAG, "onServiceDisconnected");
+        mBound = false;
+    }
+};
+
+ +

С помощью этого интерфейса {@link android.content.ServiceConnection} клиент может выполнить привязку к службе, передав ее в +метод {@link android.content.Context#bindService bindService()}. Например:

+ +
+Intent intent = new Intent(this, LocalService.class);
+bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+ +
    +
  • Первый параметр в методе {@link android.content.Context#bindService bindService()} представляет собой объект +{@link android.content.Intent}, который явным образом именует службу для привязки (хотя переход может быть +и неявным).
  • +
  • Второй параметр — это объект {@link android.content.ServiceConnection}.
  • +
  • Третий параметр представляет собой флаг, указывающий параметры привязки. Обычно им является {@link +android.content.Context#BIND_AUTO_CREATE}, создающий службу, если она уже не выполняется. +Другие возможные значения: {@link android.content.Context#BIND_DEBUG_UNBIND} +и {@link android.content.Context#BIND_NOT_FOREGROUND} или {@code 0}, если значение отсутствует.
  • +
+ + +

Дополнительные примечания

+ +

Ниже представлены некоторые важные замечания о привязке к службе.

+
    +
  • Всегда отлавливайте исключения {@link android.os.DeadObjectException}, которые выдаются +при возникновении сбоя подключения. Это единственное исключение, которое выдается удаленными методами.
  • +
  • Для объектов всегда учитываются ссылки на них в процессах.
  • +
  • Обычно необходимо связать привязку и ее отмену во время +сопоставления моментов подключения и отключения в жизненном цикле клиента. Например: +
      +
    • Если взаимодействие со службой требуется лишь в то время, когда операция отображается, привязку +необходимо выполнить во время метода {@link android.app.Activity#onStart onStart()}, а отмену привязки — во время выполнения метода {@link +android.app.Activity#onStop onStop()}.
    • +
    • Если необходимо, чтобы операция получала ответы даже в случае ее остановки во время работы в фоновом режиме, +то привязку можно выполнить во время {@link android.app.Activity#onCreate onCreate()}, а отмену привязки + — во время выполнения {@link android.app.Activity#onDestroy onDestroy()}. Однако следует помнить, что такой способ подразумевает, +что вашей операции необходимо использовать службу все время, пока она выполняется (даже если она выполняется в фоновом режиме). Поэтому, +если служба находится в другом процессе, вы тем самым утяжеляете процесс, а это +повышает вероятность того, что система завершит его.
    • +
    +

    Примечание. Обычно не следует выполнять привязку или отменять ее +во время выполнения методов {@link android.app.Activity#onResume onResume()} и {@link +android.app.Activity#onPause onPause()} вашей операции, поскольку такие обратные вызовы происходят при каждом переходе из одного состояния в другое, + а обработка данных, выполняемая при таких переходах, должна быть минимальной. Кроме того, если к одной и той же +службе привязано несколько операций в вашем приложении, и имеется переход между +двумя этими операциями, служба может быть уничтожена и создана повторно, поскольку текущая операция выполняет отмену привязки +(во время приостановки) до того, как следующая служба выполнит привязку (во время возобновления). (Подробные сведения о согласовании жизненных циклов операций при таких переходах +представлены в статье +Операции.)

    +
+ +

Пример кода, в котором показан порядок привязки к службе, см. в статье, посвященной классу {@code +RemoteService.java}, в ApiDemos.

+ + + + + +

Управление жизненным циклом привязанной службы

+ +

Когда выполняется отмена привязки службы ко всем клиентам, система Android уничтожает такую службу (если она не была запущена +вместе с {@link android.app.Service#onStartCommand onStartCommand()}). В таком случае вам не нужно +управлять жизненным циклом своей службы, если она исключительно привязанная служба +— система Android управляет ей за вас на основании привязки службы к любым другим клиентам.

+ +

Однако, если вы решите реализовать метод обратного вызова {@link android.app.Service#onStartCommand +onStartCommand()}, вам необходимо явным образом остановить службу, поскольку в +этом случае она считается запущенной. В таком случае служба выполняется до тех пор, пока +сама не остановит свою работу с помощью метода {@link android.app.Service#stopSelf()} или до тех пор, пока другой компонент не вызовет метод {@link +android.content.Context#stopService stopService()}, независимо от привязки службы к каким-либо +клиентам.

+ +

Кроме того, если ваша служба запущена и принимает привязку, то при вызове системой +вашего метода {@link android.app.Service#onUnbind onUnbind()} вы также можете вернуть +{@code true}, если желаете получить вызов к {@link android.app.Service#onRebind +onRebind()} при следующей привязке к службе (вместо получения вызова к методу {@link +android.app.Service#onBind onBind()}). Метод {@link android.app.Service#onRebind +onRebind()} возвращает значение void, однако клиент по-прежнему получает объект {@link android.os.IBinder} в своем методе обратного вызова +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}. +На рисунке 1 ниже иллюстрируется логика жизненного цикла такого рода.

+ + + +

Рисунок 1. Жизненный цикл запущенной службы, +для которой выполняется привязка.

+ + +

Дополнительные сведения о жизненном цикле уже запущенной службы представлены в статье Службы.

+ + + + diff --git a/docs/html-intl/intl/ru/guide/components/fragments.jd b/docs/html-intl/intl/ru/guide/components/fragments.jd new file mode 100644 index 0000000000000000000000000000000000000000..b13fcc3da7ef5871c1b636f819d49588f955b8d0 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=Фрагменты +parent.title=Операции +parent.link=activities.html +@jd:body + + + +

Фрагмент (класс {@link android.app.Fragment}) представляет поведение или часть пользовательского интерфейса в операции +(класс {@link android.app.Activity}). Разработчик может объединить несколько фрагментов в одну операцию для построения +многопанельного пользовательского интерфейса и повторного использования фрагмента в нескольких операциях. Фрагмент можно рассматривать как + модульную часть операции. Такая часть имеет свой жизненный цикл и самостоятельно обрабатывает события ввода. Кроме того, +ее можно добавить или удалить непосредственно во время выполнения операции. Это нечто вроде вложенной операции, которую +можно многократно использовать в различных операциях.

+ +

Фрагмент всегда должен быть встроен в операцию, и на его жизненный цикл напрямую +влияет жизненный цикл операции. Например, когда операция приостановлена, в том же состоянии находятся и все +фрагменты внутри нее, а когда операция уничтожается, уничтожаются и все фрагменты. Однако пока +операция выполняется (это соответствует состоянию возобновлена жизненного цикла), можно +манипулировать каждым фрагментом независимо, например добавлять или удалять их. Когда разработчик выполняет такие +транзакции с фрагментами, он может также добавить их в стек переходов назад, которым управляет +операция. Каждый элемент стека переходов назад в операции является записью + выполненной транзакции с фрагментом. Стек переходов назад позволяет пользователю обратить транзакцию с фрагментом (выполнить навигацию в обратном направлении), +нажимая кнопку Назад.

+ +

Когда фрагмент добавлен как часть макета операции, он находится в объекте {@link +android.view.ViewGroup} внутри иерархии представлений операции и определяет собственный +макет представлений. +Разработчик может вставить фрагмент в макет операции двумя способами. Для этого следует объявить фрагмент в +файле макета операции как элемент {@code <fragment>} или добавить его в +существующий объект{@link android.view.ViewGroup} в коде приложения. Впрочем, фрагмент не обязан быть частью +макета операции. Можно использовать фрагмент без интерфейса в качестве невидимого рабочего потока +операции.

+ +

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

+ + +

Философия проектирования

+ +

Фрагменты впервые появились в Android версии 3.0 (API уровня 11), главным образом, для обеспечения +большей динамичности и гибкости пользовательских интерфейсов на больших экранах, например, у планшетов. Поскольку экраны +планшетов гораздо больше, чем у смартфонов, они предоставляют больше возможностей для объединения и +перестановки компонентов пользовательского интерфейса. Фрагменты позволяют делать это, избавляя разработчика от необходимости управлять +сложными изменениями в иерархии представлений. Разбивая макет операции на фрагменты, разработчик получает возможность +модифицировать внешний вид операции в ходе выполнения и сохранять эти изменения в стеке переходов назад, +которым управляет операция.

+ +

Например, новостное приложение может использовать один фрагмент для показа списка статей слева, +а другой—для отображения статьи справа. Оба фрагмента отображаются за +одну операцию рядом друг с другом, и каждый имеет собственный набор методов обратного вызова жизненного цикла и управляет +собственными событиями пользовательского ввода. Таким образом, вместо применения одной операции для выбора статьи, а другой + — для чтения статей, пользователь может выбрать статью и читать ее в рамках одной +операции, как на планшете, изображенном на рисунке 1.

+ +

Следует разрабатывать каждый фрагмент как модульный и повторно используемый компонент операции. Поскольку + каждый фрагмент определяет собственный макет и собственное поведение со своими обратными вызовами жизненного цикла, разработчик может +включить один фрагмент в несколько операций. Поэтому он должен предусмотреть повторное использование фрагмента и не допускать, +чтобы один фрагмент непосредственно манипулировал другим. Это особенно важно, потому что модульность фрагментов +позволяет изменять их сочетания в соответствии с различными размерами экранов. Если +приложение должно работать и на планшетах, и на смартфонах, можно повторно использовать фрагменты в различных +конфигурациях макета, чтобы оптимизировать взаимодействие с пользователем в зависимости от доступного размера экрана. Например, +на смартфоне может возникнуть необходимость в разделении фрагментов для предоставления однопанельного пользовательского интерфейса, если + разработчику не удается поместить более одного фрагмента в одну операцию.

+ + +

Рисунок 1. Пример того, как два модуля пользовательского интерфейса, определенные +фрагментами, могут быть объединены внутри одной операции для работы на планшетах, но разделены на +смартфонах.

+ +

Вернемся к примеру с новостным приложением. Оно может иметь +два фрагмента, встроенных в Операцию А, когда выполняется на устройстве планшетного формата. В то же время на +экране смартфона недостаточно места для обоих фрагментов, и поэтому Операция А включает в себя +только фрагмент со списком статей. Когда пользователь выбирает статью, запускается +Операция В, содержащая второй фрагмент для чтения статьи. Таким образом, приложение +поддерживает как планшеты, так и смартфоны благодаря повторному использованию фрагментов в различных сочетаниях, как показано на +рисунке 1.

+ +

Подробные сведения относительно разработки приложения с различными сочетаниями фрагментов для +различных конфигураций экрана приводятся в руководстве Поддержка планшетов и смартфонов

+ + + +

Создание фрагмента

+ +
+ +

Рисунок 2. Жизненный цикл фрагмента (во время +выполнения операции)

+
+ +

Для создания фрагмента необходимо создать подкласс класса {@link android.app.Fragment} (или его существующего +подкласса). Класс {@link android.app.Fragment} имеет код, во многом схожий с +кодом {@link android.app.Activity}. Он содержит методы обратного вызова, аналогичные методам операции, такие +как {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()} и {@link android.app.Fragment#onStop onStop()}. На практике, +если требуется преобразовать существующее приложение Android так, чтобы в нем использовались фрагменты, достаточно просто переместить +код из методов обратного вызова операции в соответствующие методы обратного вызова +фрагмента.

+ +

Как правило, необходимо реализовать следующие методы жизненного цикла:

+ +
+
{@link android.app.Fragment#onCreate onCreate()}
+
Система вызывает этот метод, когда создает фрагмент. В своей реализации разработчик должен +инициализировать ключевые компоненты фрагмента, которые требуется сохранить, когда фрагмент находится в состоянии +паузы или возобновлен после остановки.
+
{@link android.app.Fragment#onCreateView onCreateView()}
+
Система вызывает этот метод при первом отображении пользовательского интерфейса фрагмента +на дисплее. Для прорисовки пользовательского интерфейса фрагмента следует возвратить из этого метода объект {@link android.view.View}, +который является корневым в макете фрагмента. Если фрагмент не имеет пользовательского интерфейса, можно +возвратить null.
+
{@link android.app.Activity#onPause onPause()}
+
Система вызывает этот метод как первое указание того, что пользователь покидает +фрагмент (это не всегда означает уничтожение фрагмента). Обычно именно в этот момент +необходимо фиксировать все изменения, которые должны быть сохранены за рамками текущего сеанса работы пользователя (поскольку +пользователь может не вернуться назад).
+
+ +

В большинстве приложений для каждого фрагмента должны быть реализованы, как минимум, эти три метода. Однако существуют и +другие методы обратного вызова, которые следует использовать для управления различными этапами жизненного цикла +фрагмента. Все методы обратного вызова жизненного цикла подробно обсуждаются в разделе + Управление жизненным циклом фрагмента.

+ + +

Существует также ряд подклассов, которые, возможно, потребуется расширить вместо использования базового класса {@link +android.app.Fragment}:

+ +
+
{@link android.app.DialogFragment}
+
Отображение перемещаемого диалогового окна. Использование этого класса для создания диалогового окна является хорошей альтернативой + вспомогательным методам диалогового окна в классе{@link android.app.Activity}. Дело в том, что он дает возможность +вставить диалоговое окно фрагмента в управляемый операцией стек переходов назад для фрагментов, +что позволяет пользователю вернуться к закрытому фрагменту.
+ +
{@link android.app.ListFragment}
+
Отображение списка элементов, управляемых адаптером (например, {@link +android.widget.SimpleCursorAdapter}), аналогично классу {@link android.app.ListActivity}. Этот класс предоставляет +несколько методов для управления списком представлений, например, метод обратного вызова {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} для +обработки нажатий.
+ +
{@link android.preference.PreferenceFragment}
+
Отображение иерархии объектов {@link android.preference.Preference} в виде списка, аналогично классу +{@link android.preference.PreferenceActivity}. Этот класс полезен, когда в приложении создается +операция «Настройки».
+
+ + +

Добавление пользовательского интерфейса

+ +

Фрагмент обычно используется как часть пользовательского интерфейса операции, при этом он добавляет в операцию +свой макет.

+ +

Чтобы создать макет для фрагмента, разработчик должен реализовать метод обратного вызова {@link +android.app.Fragment#onCreateView onCreateView()}, который система Android вызывает, +когда для фрагмента наступает время отобразить свой макет. Реализация этого метода должна возвращать объект +{@link android.view.View}, который является корневым в макете фрагмента.

+ +

Примечание. Если фрагмент является подклассом класса {@link +android.app.ListFragment}, реализация по умолчанию возвращает класс {@link android.widget.ListView} из метода + {@link android.app.Fragment#onCreateView onCreateView()}, так что реализовывать его нет необходиомости.

+ +

Чтобы возвратить макет из метода {@link +android.app.Fragment#onCreateView onCreateView()}, можно выполнить его раздувание из ресурса макета, определенного в XML-файле. Для +этой цели метод {@link android.app.Fragment#onCreateView onCreateView()} предоставляет объект +{@link android.view.LayoutInflater}.

+ +

Например, код подкласса класса {@link android.app.Fragment}, загружающий макет из файла +{@code example_fragment.xml}, может выглядеть так:

+ +
+public static class ExampleFragment extends Fragment {
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.example_fragment, container, false);
+    }
+}
+
+ + + +

Параметр {@code container}, передаваемый методу {@link android.app.Fragment#onCreateView +onCreateView()}, является родительским классом {@link android.view.ViewGroup} (из макета операции), в который +будет вставлен макет +фрагмента. Параметр {@code savedInstanceState} является классом {@link android.os.Bundle}, который +предоставляет данные о предыдущем экземпляре фрагмента во время возобновления фрагмента +(восстановление состояния подробно обсуждается в разделе Управление +жизненным циклом фрагмента).

+ +

Метод {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} принимает +три аргумента:

+
    +
  • Идентификатор ресурса макета, раздувание которого следует выполнить.
  • +
  • Объект класса {@link android.view.ViewGroup}, который должен стать родительским для макета после раздувания. Передача параметра {@code +container} необходима для того, чтобы система смогла применить параметры макета к корневому представлению +раздутого макета, определяемому родительским представлением, в которое направляется макет.
  • +
  • Логическое значение, показывающее, следует ли прикрепить макет к объекту {@link +android.view.ViewGroup} (второй параметр) во время раздувания. (В данном случае это +false, потому что система уже вставляет раздутый макет в объект {@code +container}, ипередача значения true создала бы лишнюю группу представления в окончательном макете).
  • +
+ +

Мы увидели, как создавать фрагмент, предоставляющий макет. Теперь необходимо добавить +фрагмент в операцию.

+ + + +

Добавление фрагмента в операцию

+ +

Как правило, фрагмент добавляет часть пользовательского интерфейса в операцию, и этот интерфейс встраивается +в общую иерархию представлений операции. Разработчик может добавить фрагмент в макет операции двумя +способами:

+ +
    +
  • объявив фрагмент в файле макета операции. +

    В этом случае можно +указать свойства макета для фрагмента, как будто он является представлением. Например, файл макета операции +с двумя фрагментами может выглядеть следующим образом:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:orientation="horizontal"
    +    android:layout_width="match_parent"
    +    android:layout_height="match_parent">
    +    <fragment android:name="com.example.news.ArticleListFragment"
    +            android:id="@+id/list"
    +            android:layout_weight="1"
    +            android:layout_width="0dp"
    +            android:layout_height="match_parent" />
    +    <fragment android:name="com.example.news.ArticleReaderFragment"
    +            android:id="@+id/viewer"
    +            android:layout_weight="2"
    +            android:layout_width="0dp"
    +            android:layout_height="match_parent" />
    +</LinearLayout>
    +
    +

    Атрибут {@code android:name} в элементе {@code <fragment>} определяет класс {@link +android.app.Fragment}, экземпляр которого создается в макете.

    + +

    Когда система создает этот макет операции, она создает экземпляр каждого фрагмента, определенного в макете, +и для каждого вызывает метод {@link android.app.Fragment#onCreateView onCreateView()}, +чтобы получить макет каждого фрагмента. Система вставляет объект {@link android.view.View}, возвращенный + фрагментом, непосредственно вместо элемента {@code <fragment>}.

    + +
    +

    Примечание. Каждый фрагмент должен иметь уникальный идентификатор, который +система сможет использовать для восстановления фрагмента в случае перезапуска операции. (Что касается разработчика, он может использовать этот идентификатор для +захвата фрагмента с целью выполнения транзакций с ним, например, чтобы удалить его). Предоставить +идентификатор фрагменту можно тремя способами:

    +
      +
    • указать атрибут {@code android:id} с уникальным идентификатором;
    • +
    • указать атрибут {@code android:tag} с уникальной строкой;
    • +
    • ничего не предпринимать, чтобы система использовала идентификатор контейнерного +представления.
    • +
    +
    +
  • + +
  • или программным образом, добавив фрагмент в существующий объект {@link android.view.ViewGroup}. +

    В любой момент выполнения операции разработчик может добавить фрагменты в ее макет. Для +этого достаточно указать объект {@link +android.view.ViewGroup}, в котором следует разместить фрагмент.

    +

    Для выполнения транзакций с фрагментами внутри операции (таких как добавление, удаление или замена +фрагмента) необходимо использовать API-интерфейсы из {@link android.app.FragmentTransaction}. Экземпляр + класса {@link android.app.FragmentTransaction} можно получить от объекта {@link android.app.Activity} следующим образом:

    + +
    +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}
    +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
    +
    + +

    После этого можно добавить фрагмент методом {@link +android.app.FragmentTransaction#add(int,Fragment) add()}, указав добавляемый фрагмент и +представление, в которое он должен быть добавлен. Например:

    + +
    +ExampleFragment fragment = new ExampleFragment();
    +fragmentTransaction.add(R.id.fragment_container, fragment);
    +fragmentTransaction.commit();
    +
    + +

    Первый аргумент, передаваемый методу {@link android.app.FragmentTransaction#add(int,Fragment) add()}, +представляет собой контейнерный объект {@link android.view.ViewGroup} для фрагмента, указанный при помощи +идентификатора ресурса. Второй параметр — это фрагмент, который нужно добавить.

    +

    Выполнив изменения с помощью +{@link android.app.FragmentTransaction}, необходимо +вызвать метод {@link android.app.FragmentTransaction#commit}, чтобы они вступили в силу.

    +
  • +
+ + +

Добавление фрагмента, не имеющего пользовательского интерфейса

+ +

Пример, приведенный выше, демонстрирует, как добавлять в операцию фрагмент с предоставлением пользовательского интерфейса. Однако +можно использовать фрагмент и для реализации фонового поведения операции без какого-либо дополнительного +пользовательского интерфейса.

+ +

Чтобы добавить фрагмент без пользовательского интерфейса, добавьте фрагмент из операции, используя метод {@link +android.app.FragmentTransaction#add(Fragment,String)} (передав ему уникальный строковый «тег» для +фрагмента вместо идентификатора представления). Фрагмент будет добавлен, но, поскольку он не связан +с представлением в макете операции, он не будет принимать вызов метода {@link +android.app.Fragment#onCreateView onCreateView()}. Поэтому в реализации этого метода нет необходимости.

+ +

Передача строкового тега свойственна не только фрагментам без пользовательского интерфейса, поэтому можно +передавать строковые теги и фрагментам, имеющим пользовательский интерфейс. Однако, если у фрагмента нет + пользовательского интерфейса, то строковый тег является единственным способом его идентификации. Если впоследствии потребуется получить фрагмент от +операции, нужно будет вызвать метод {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

+ +

Пример операции, использующей фрагмент в качестве фонового потока, без пользовательского интерфейса, приведен в образце кода {@code +FragmentRetainInstance.java}, входящем в число образцов в SDK (и доступном при помощи +Android SDK Manager). Путь к нему в системе — +<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

+ + + +

Управление фрагментами

+ +

Для управления фрагментами в операции нужен класс {@link android.app.FragmentManager}. Чтобы получить его, + следует вызвать метод {@link android.app.Activity#getFragmentManager()} из кода операции.

+ +

Ниже указаны действия, которые позволяет выполнить{@link android.app.FragmentManager}:

+ +
    +
  • получать фрагменты, имеющиеся в операции, с помощью метода {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (для фрагментов, предоставляющих пользовательский интерфейс в + макете операции) или {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (как для фрагментов, имеющих пользовательский интерфейс, так и для фрагментов без него);
  • +
  • снимать фрагменты со стека переходов назад методом {@link +android.app.FragmentManager#popBackStack()} (имитируя нажатие кнопки Назад пользователем);
  • +
  • регистрировать процесс-слушатель изменений в стеке переходов назад при помощи метода {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.
  • +
+ +

Дополнительные сведения об этих и других методах приводятся в документации по классу {@link +android.app.FragmentManager}.

+ +

Как было показано в предыдущем разделе, можно использовать класс {@link android.app.FragmentManager} +для открытия {@link android.app.FragmentTransaction}, что позволяет выполнять транзакции с фрагментами, например, +добавление и удаление.

+ + +

Выполнение транзакций с фрагментами

+ +

Большим достоинством использования фрагментов в операции является возможность добавлять, удалять, заменять их и +выполнять другие действия с ними в ответ на действия пользователя. Любой набор изменений, +вносимых в операцию, называется транзакцией. Ее можно выполнить при помощи API-интерфейсов в {@link +android.app.FragmentTransaction}. Каждую транзакцию можно сохранить в стеке переходов назад, +которым управляет операция. Это позволит пользователю перемещаться назад по изменениям во фрагментах (аналогично перемещению +назад по операциям).

+ +

Экземпляр класса {@link android.app.FragmentTransaction} можно получить от {@link +android.app.FragmentManager}, например, так:

+ +
+FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()};
+FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
+
+ +

Каждая транзакция является набором изменений, выполняемых одновременно. Разработчик может указать +все изменения, которые ему нужно выполнить в данной транзакции, вызывая методы {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()} +и {@link android.app.FragmentTransaction#replace replace()}. Затем, чтобы применить транзакцию +к операции, следует вызвать метод {@link android.app.FragmentTransaction#commit()}.

+ + +

Впрочем, до вызова метода{@link +android.app.FragmentTransaction#commit()} у разработчика может возникнуть необходимость вызвать метод {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, чтобы добавить транзакцию +в стек переходов назад по транзакциям фрагмента. Этим стеком переходов назад управляет операция, что позволяет +пользователю вернуться к предыдущему состоянию фрагмента, нажав кнопку Назад.

+ +

Например, следующий код демонстрирует, как можно заменить один фрагмент другим, сохранив при этом предыдущее +состояние в стеке переходов назад:

+ +
+// Create new fragment and transaction
+Fragment newFragment = new ExampleFragment();
+FragmentTransaction transaction = getFragmentManager().beginTransaction();
+
+// Replace whatever is in the fragment_container view with this fragment,
+// and add the transaction to the back stack
+transaction.replace(R.id.fragment_container, newFragment);
+transaction.addToBackStack(null);
+
+// Commit the transaction
+transaction.commit();
+
+ +

В этом коде объект {@code newFragment} замещает фрагмент (если таковой имеется), находящийся в +контейнере макета, на который указывает идентификатор {@code R.id.fragment_container}. В результате вызова метода {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} транзакция замены +сохраняется в стеке переходов назад, чтобы пользователь мог обратить транзакцию и вернуть +предыдущий фрагмент, нажав кнопку Назад.

+ +

Если в транзакцию добавить несколько изменений (например, еще раз вызвать {@link +android.app.FragmentTransaction#add add()} или {@link android.app.FragmentTransaction#remove +remove()}), а затем вызвать {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, все изменения, примененные до вызова +метода {@link android.app.FragmentTransaction#commit commit()}, будут добавлены в стек + переходов назад как одна транзакция, и кнопкаНазад обратит их все вместе.

+ +

Порядок добавления изменений к объекту {@link android.app.FragmentTransaction} не играет роли + за следующими исключениями:

+
    +
  • метод {@link android.app.FragmentTransaction#commit()} должен быть вызван в последнюю очередь;
  • +
  • если в один контейнер добавляется несколько фрагментов, то порядок их +добавления определяет порядок, в котором они появляются в иерархии видов.
  • +
+ +

Если при выполнении транзакции, удаляющей фрагмент, не вызвать метод {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()}, при фиксации транзакции фрагмент +уничтожается, и пользователь теряет возможность вернуться к нему. В то же время, если +вызвать {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} при +удалении фрагмента, фрагмент перейдет в состояние остановки и будет возобновлен, если пользователь вернется +к нему.

+ +

Совет. К каждой транзакции с фрагментом можно применить анимацию +перехода, вызвав {@link android.app.FragmentTransaction#setTransition setTransition()} до +фиксации.

+ +

Вызов метода {@link android.app.FragmentTransaction#commit()} не приводит к немедленному выполнению + транзакции. Метод запланирует ее выполнение в потоке пользовательского интерфейса операции (в «главном» потоке), как только +у потока появится возможность для этого. Впрочем, при необходимости можно вызвать {@link +android.app.FragmentManager#executePendingTransactions()} из потока пользовательского интерфейса, чтобы +транзакции, запланированные методом {@link android.app.FragmentTransaction#commit()} были выполнены немедленно. Как правило, +в этом нет необходимости, за исключением случаев, когда транзакция является зависимостью для заданий в других потоках.

+ +

Внимание! Фиксировать транзакцию методом {@link +android.app.FragmentTransaction#commit commit()} можно только до того, как операциясохранит свое +состояние (после того, как пользователь покинет ее). Попытка зафиксировать транзакцию после этого момента + вызовет исключение. Дело в том, что состояние после фиксации может быть потеряно, если понадобится +восстановить операцию. В ситуациях, в которых потеря фиксации не критична, следует вызывать {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}.

+ + + + +

Взаимодействие с операцией

+ +

Хотя {@link android.app.Fragment} реализован как объект, независимый от +класса {@link android.app.Activity}, и может быть использован внутри нескольких операций, конкретный экземпляр +фрагмента напрямую связан с содержащей его операцией.

+ +

В частности, фрагмент может обратиться к экземпляру {@link android.app.Activity} с помощью метода {@link +android.app.Fragment#getActivity()} и без труда выполнить такие задачи, как поиск представления в макете +операции:

+ +
+View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
+
+ +

Аналогичным образом операция может вызывать методы фрагмента, получив ссылку на объект +{@link android.app.Fragment} от {@link android.app.FragmentManager} с помощью метода {@link +android.app.FragmentManager#findFragmentById findFragmentById()} или {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Например:

+ +
+ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
+
+ + +

Создание обратного вызова события для операции

+ +

В некоторых случаях необходимо, чтобы фрагмент использовал события совместно с операцией. Хороший способ реализации этого +состоит в том, чтобы определить интерфейс обратного вызова внутри фрагмента и потребовать от контейнерной операции +его реализации. Когда операция примет обратный вызов через этот интерфейс, она сможет обмениваться информацией с +другими фрагментами в макете по мере необходимости.

+ +

Пусть, например, у новостного приложения имеются два фрагмента в одной операции: один для отображения списка +статей (фрагмент A), а другой—для отображения статьи (фрагмент B). Тогда фрагмент A должен сообщать +операции о том, что выбран пункт списка, чтобы она могла сообщить фрагменту B о необходимости отобразить статью. В +этом случае интерфейс {@code OnArticleSelectedListener} объявляется во фрагменте A:

+ +
+public static class FragmentA extends ListFragment {
+    ...
+    // Container Activity must implement this interface
+    public interface OnArticleSelectedListener {
+        public void onArticleSelected(Uri articleUri);
+    }
+    ...
+}
+
+ +

Тогда операция, содержащая этот фрагмент, реализует интерфейс {@code OnArticleSelectedListener} +и переопределит +метод {@code onArticleSelected()}, чтобы извещать фрагмент B о событии, исходящем от фрагмента A. Чтобы +контейнерная операция наверняка реализовала этот интерфейс, метод обратного вызова {@link +android.app.Fragment#onAttach onAttach()} во фрагменте A (который система вызывает при добавлении +фрагмента в операцию) создает экземпляр класса {@code OnArticleSelectedListener}, выполнив +приведение типа объекта {@link android.app.Activity}, который передается методу {@link android.app.Fragment#onAttach +onAttach()}:

+ +
+public static class FragmentA extends ListFragment {
+    OnArticleSelectedListener mListener;
+    ...
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        try {
+            mListener = (OnArticleSelectedListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
+        }
+    }
+    ...
+}
+
+ +

Если операция не реализовала интерфейс, фрагмент генерирует исключение +{@link java.lang.ClassCastException}. +В случае успеха элемент {@code mListener} будет содержать ссылку на реализацию интерфейса +{@code OnArticleSelectedListener} в операции, чтобы фрагмент A мог использовать +события совместно с операцией, вызывая методы, определенные интерфейсом {@code OnArticleSelectedListener}. Например, если фрагмент A является расширением +класса {@link android.app.ListFragment}, то всякий раз, +когда пользователь нажимает элемент списка, система вызывает {@link android.app.ListFragment#onListItemClick +onListItemClick()} во фрагменте. Этот метод, в свою очередь, вызывает метод {@code onArticleSelected()}, чтобы использовать +событие совместно с операцией:

+ +
+public static class FragmentA extends ListFragment {
+    OnArticleSelectedListener mListener;
+    ...
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        // Append the clicked item's row ID with the content provider Uri
+        Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
+        // Send the event and Uri to the host activity
+        mListener.onArticleSelected(noteUri);
+    }
+    ...
+}
+
+ +

Параметр {@code id}, передаваемый методу {@link +android.app.ListFragment#onListItemClick onListItemClick()}, — это идентификатор строки с выбранным элементом списка, +который операция (или другой фрагмент) использует для получения статьи от объекта {@link +android.content.ContentProvider} приложения.

+ +

Дополнительные сведения о +работе с поставщиком контента приводятся в документе Поставщики контента.

+ + + +

Добавление элементов в строку действий

+ +

Фрагменты могут добавлять пункты меню в Меню вариантов операции (и, следовательно, в Строку действий), реализовав +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Однако, чтобы этот метод мог +принимать вызовы, необходимо вызывать {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} во время выполнения метода {@link +android.app.Fragment#onCreate(Bundle) onCreate()}, чтобы сообщить, что фрагмент +намеревается добавить пункты в Меню вариантов (в противном случае фрагмент не примет вызов метода +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).

+ +

Любые пункты, добавляемые фрагментом в Меню вариантов, присоединяются к +уже существующим. Кроме того, фрагмент принимает обратные вызовы метода {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, когда пользователь выбирает пункт +меню.

+ +

Разработчик может также зарегистрировать представление в макете своего фрагмента, чтобы предоставить контекстное меню. Для этого следует вызвать метод {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Когда пользователь открывает +контекстное меню, фрагмент принимает вызов метода {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}. Когда пользователь выбирает пункт меню, фрагмент принимает вызов метода {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.

+ +

Примечание. Хотя фрагмент принимает обратный вызов по событию «выбран пункт меню» +для каждого добавленного им пункта, операция первой принимает соответствующий обратный вызов, когда пользователь +выбирает пункт меню. Если имеющаяся в операции реализация обратного вызова по событию «выбран пункт меню» не +обрабатывает выбранный пункт, событие передается методу обратного вызова во фрагменте. Это справедливо для +Меню вариантов и контекстных меню.

+ +

Подробные сведения относительно меню см. в руководствах для разработчиков Меню и Строка действий

+ + + + +

Управление жизненным циклом фрагмента

+ +
+ +

Рисунок 3. Влияние жизненного цикла операции на жизненный цикл +фрагмента

+
+ +

Управление жизненным циклом фрагмента во многом аналогично управлению жизненным циклом операции. Как и +операция, фрагмент может существовать в одном из трех состояний:

+ +
+
Возобновлен
+
Фрагмент виден во время выполнения операции.
+ +
Приостановлен
+
На переднем плане выполняется и находится в фокусе другая операция, но операция, содержащая данный +фрагмент, по-прежнему видна (операция переднего плана частично прозрачна или не +занимает весь экран).
+ +
Остановлен
+
Фрагмент не виден. Либо контейнерная операция остановлена, либо +фрагмент удален из нее, но добавлен в стек переходов назад. Остановленный фрагмент +по-прежнему активен (вся информация о состоянии и элементах сохранена в системе). Однако он больше +не виден пользователю и будет уничтожен в случае уничтожения операции.
+
+ +

Здесь снова просматривается аналогия с операцией: разработчик может сохранить состояние фрагмента с помощью {@link +android.os.Bundle} на случай, если процесс операции будет уничтожен, а разработчику понадобится восстановить +состояние фрагмента при повторном создании операции. Состояние можно сохранить во время выполнения метода обратного вызова {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} во фрагменте и восстановить его во время выполнения +{@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()} или {@link +android.app.Fragment#onActivityCreated onActivityCreated()}. Дополнительные сведения о сохранении +состояния приводятся в документе Операции. +

+ +

Самое значительное различие в ходе жизненного цикла между операцией и фрагментом состоит в принципах +их сохранения в соответствующих стеках переходов назад. По умолчанию операция помещается в управляемый системой стек переходов назад для операций, +когда она останавливается (чтобы пользователь мог вернуться +к ней с помощью кнопки Назад, как описано в статье Задачи и стек переходов назад). +В то же время, фрагмент помещается в стек переходов назад, управляемый операцией, только когда разработчик +явно запросит сохранение конкретного экземпляра, вызвав метод {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} во время транзакции, +удаляющей фрагмент.

+ +

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

+ +

Внимание! Если возникнет необходимость в объекте {@link android.content.Context} +внутри объекта класса {@link android.app.Fragment}, можно вызвать метод{@link android.app.Fragment#getActivity()}. +Однако разработчик должен быть внимательным и вызывать метод {@link android.app.Fragment#getActivity()} только когда фрагмент +прикреплен к операции. Если фрагмент еще не прикреплен или был откреплен в конце +его жизненного цикла, метод {@link android.app.Fragment#getActivity()} возвратит null.

+ + +

Согласование с жизненным циклом операции

+ +

Жизненый цикл операции, содержащей фрагмент, непосредственным образом влияет на жизненый цикл +фрагмента, так что каждый обратный вызов жизненного цикла операции приводит к аналогичному обратного вызову для каждого +фрагмента. Например, когда операция принимает вызов {@link android.app.Activity#onPause}, каждый +ее фрагмент принимает {@link android.app.Fragment#onPause}.

+ +

Однако у фрагментов есть несколько дополнительных методов обратного вызова жизненого цикла, которые обеспечивают уникальное взаимодействие с операцией +для выполнения таких действий, как создание и уничтожение пользовательского интерфейса фрагмента. Вот эти +методы:

+ +
+
{@link android.app.Fragment#onAttach onAttach()}
+
Вызывается, когда фрагмент связывается с операцией (ему передается объект {@link +android.app.Activity}).
+
{@link android.app.Fragment#onCreateView onCreateView()}
+
Вызывается для создания иерархии представлений, связанной с фрагментом.
+
{@link android.app.Fragment#onActivityCreated onActivityCreated()}
+
Вызывается, когда метод {@link android.app.Activity#onCreate +onCreate()}, принадлежащий операции, возвращает управление.
+
{@link android.app.Fragment#onDestroyView onDestroyView()}
+
Вызывается при удалении иерархии представлений, связанной с фрагментом.
+
{@link android.app.Fragment#onDetach onDetach()}
+
Вызывается при разрыве связи фрагмента с операцией.
+
+ +

Зависимость жизненого цикла фрагмента от содержащей его операции иллюстрируется +рисунком 3. На этом рисунке можно видеть, что очередное состояние операции определяет, какие +методы обратного вызова может принимать фрагмент. Например, когда операция принимает свой метод обратного вызова {@link +android.app.Activity#onCreate onCreate()}, фрагмент внутри этой операции принимает всего лишь метод обратного вызова +{@link android.app.Fragment#onActivityCreated onActivityCreated()}.

+ +

Когда операция переходит в состояние «возобновлена», можно свободно добавлять в нее фрагменты и удалять +их. Таким образом, жизненный цикл фрагмента может быть независимо изменен, только пока операция остается +в состоянии «возобновлена».

+ +

Однако, когда операция выходит из этого состояния, продвижение фрагмента по его +жизненному циклу снова осуществляется операцией.

+ + + + +

Пример:

+ +

Чтобы суммировать все сказанное в этом документе, рассмотрим пример операции, +использующей два фрагмента для создания макета с двумя панелями. Операция, код которой приведен ниже, включает в себя один фрагмент для +отображения списка пьес Шекспира, а другой — для отображения краткого содержания пьесы, выбранной +из списка. В примере показано, как следует организовывать различные конфигурации фрагментов +в зависимости от конфигурации экрана.

+ +

Примечание. Полный исходный код этой операции находится в разделе +{@code +FragmentLayout.java}.

+ +

Главная операция применяет макет обычным способом, в методе {@link +android.app.Activity#onCreate onCreate()}:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +

Здесь применяется макет {@code fragment_layout.xml}:

+ +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +

Пользуясь этим макетом, система создает экземпляр класса {@code TitlesFragment} (список +пьес), как только операция загрузит макет. При этом объект {@link android.widget.FrameLayout} +(в котором будет находиться фрагмент с кратким содержанием) занимает место в правой части +экрана, но поначалу остается пустым. Как будет показано ниже, фрагмент не помещается в {@link android.widget.FrameLayout}, пока пользователь не выберет элемент +в списке.

+ +

Однако не все экраны достаточно широки, чтобы отображать +краткое содержание рядом со списком пьес. Поэтому описанный выше макет используется только при альбомной ориентации +экрана и хранится в файле {@code res/layout-land/fragment_layout.xml}.

+ +

Когда же устройство находится в книжной ориентации, система применяет макет, приведенный ниже, который +хранится в файле{@code res/layout/fragment_layout.xml}:

+ +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +

В этом макете присутствует только объект {@code TitlesFragment}. Это означает, что при +книжной ориентации устройства виден только список пьес. Когда пользователь нажимает на элемент +списка в этой конфигурации, приложение запускает новую операцию для отображения краткого содержания, +а не загружает второй фрагмент.

+ +

Далее можно видеть, как это реализовано в классах фрагмента. Вначале идет код класса {@code +TitlesFragment}, отображающий список пьес Шекспира. Этот фрагмент является расширением класса {@link +android.app.ListFragment} и использует его функции для выполнения основной работы со списком.

+ +

Изучая код, обратите внимание на то, что в качестве реакции на нажатие пользователем +элемента списка возможны две модели поведения. В зависимости от того, какой из двух макетов активен, либо в рамках одной операции создается и отображается новый фрагмент +с кратким содержанием (за счет добавления фрагмента в объект {@link +android.widget.FrameLayout}), либо запускается новая операция (отображающая фрагмент).

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +

Второй фрагмент, {@code DetailsFragment}, отображает краткое содержание пьесы, выбранной в +списке {@code TitlesFragment}:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +

Вспомним код класса {@code TitlesFragment}: если пользователь нажимает на пункт списка, а +текущий макет не включает в себя представление {@code R.id.details} (которому принадлежит фрагмент +{@code DetailsFragment}), то приложение запускает операцию {@code DetailsActivity} +для отображения содержимого элемента.

+ +

Далее идет код класса {@code DetailsActivity}, который всего лишь содержит объект {@code DetailsFragment} для отображения +краткого содержания выбранной пьесы на экране в книжной ориентации:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +

Обратите внимание, что в альбомной конфигурации эта операция самостоятельно завершается, чтобы главная +операция могла принять управление и отобразить фрагмент {@code DetailsFragment} рядом с фрагментом{@code TitlesFragment}. +Это может произойти, если пользователь запустит операцию {@code DetailsActivity} в книжной ориентации экрана, а +затем перевернет устройство в альбомную ориентацию (в результате чего текущая операция будет перезапущена).

+ + +

Дополнительные образцы кода, использующего фрагменты (и файлы с полным исходным кодом этого примера), +доступны в приложении-примере API Demos в разделе +ApiDemos (которое можно загрузить из компонента Samples SDK).

+ + diff --git a/docs/html-intl/intl/ru/guide/components/fundamentals.jd b/docs/html-intl/intl/ru/guide/components/fundamentals.jd new file mode 100644 index 0000000000000000000000000000000000000000..181cbbdfb10771335b329e1db2fcbc7825cacb22 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=Основы создания приложений +@jd:body + + + +

Приложения для Android пишутся на языке программирования Java. Инструменты Android SDK (Software Development Kit – комплект разработки программного обеспечения) компилируют +написанный вами код — и все требуемые файлы данных и ресурсов — в файл APK – программный пакет Android, +который представляет собой файл архива с расширением {@code .apk}. В файле APK находится все, что требуется для работы +Android-приложения, и он позволяет установить приложение на любом устройстве под управлением системы Android.

+ +

Каждое приложение Android, установленное на устройстве, работает в собственной "песочнице" (изолированной программной среде):

+ +
    +
  • операционная система Android представляет собой многопользовательскую систему Linux, в которой каждое приложение является +отдельным пользователем;
  • + +
  • по умолчанию система назначает каждому приложению уникальный идентификатор пользователя Linux (этот идентификатор используется только +системой и неизвестен приложению); система устанавливает полномочия для всех файлов + в приложении, с тем чтобы доступ к ним был разрешен только пользователю с идентификатором, назначенным этому приложению;
  • + +
  • у каждого процесса имеется собственная виртуальная машина (ВМ), так что код приложения выполняется изолированно от +других приложений;
  • + +
  • по умолчанию каждое приложение выполняется в собственном процессе Linux. Android запускает процесс, когда требуется +выполнить какой-либо компонент приложения, а затем завершает процесс, когда он больше не +нужен либо когда системе требуется освободить память для других приложений.
  • +
+ +

Таким образом система Android реализует принцип предоставления минимальных прав. То есть +каждое приложение по умолчанию имеет доступ только к тем компонентам, которые ему необходимы для работы, и +ни к каким другим. Благодаря этому формируется исключительно безопасная среда, в которой приложение не имеет доступа к недозволенным областям +системы.

+ +

Однако у приложения есть варианты предоставления своих данных другим приложениям и +доступа к системным службам:

+ +
    +
  • двум приложениям можно назначить один идентификатор пользователя Linux. В этом случае +каждый из них сможет обращаться к файлам другого приложения. Для экономии ресурсов системы также можно +сделать так, чтобы приложения с одинаковым идентификатором пользователя выполнялись в одном процессе Linux и использовали одну ВМ ( +приложения также должны быть подписаны одним сертификатом);
  • +
  • приложение может запросить разрешение на доступ к данным устройства, например к контактам +пользователя, SMS-сообщениям, подключаемой карте памяти (SD-карте), камере, Bluetooth и др. Все +разрешения должны предоставляться приложению при его установке.
  • +
+ +

Это основные сведения о том, каким образом приложение Android существует в системе. В остальной части +этого документа раскрываются следующие темы:

+
    +
  • базовые компоненты, которые определяют приложение;
  • +
  • файл манифеста, в котором объявляются компоненты и функции устройства, необходимые для +приложения;
  • +
  • ресурсы, которые существуют отдельно от кода приложения и позволяют приложению +адаптировать свою работу к устройствам с различными конфигурациями.
  • +
+ + + +

Компоненты приложения

+ +

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

+ +

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

+ +

Четыре типа компонентов:

+ +
+ +
Операции
+ +
Операция (Activity) представляет собой один экран с пользовательским интерфейсом. Например, +в приложении для работы с электронной почтой одна операция может служить для отображения списка новых +сообщений, другая – для составления сообщения и третья операция – для чтения сообщений. Несмотря на то что +операции совместно формируют связное взаимодействие пользователя с приложением по работе с электронной почтой, каждая из них +не зависит от других операций. Любые из этих операций могут быть запущены +другим приложением (если это позволяет приложение по работе с электронной почтой). Например, приложение для камеры может запустить +операцию в приложении по работе с электронной почтой, которая составляет новое сообщение, чтобы пользователь мог отослать фотографию. + +

Операция относится к подклассу класса {@link android.app.Activity}. Подробные сведения об этом можно +найти в руководстве для разработчиков в статье Операции +.

+
+ + +
Службы
+ +
Служба (Service) представляет собой компонент, который работает в фоновом режиме и выполняет длительные +операции, связанные с работой удаленных процессов. Служба +не имеет пользовательского интерфейса. Например, она может воспроизводить музыку в фоновом режиме, пока +пользователь работает в другом приложении, или же она может получать данные по сети, не +блокируя взаимодействие пользователя с операцией. Служба может быть запущена другим компонентом, который затем будут взаимодействовать с ней, – например +операцией. + +

Служба относится к подклассу класса {@link android.app.Service}. Подробные сведения об этом можно +найти в руководстве для разработчиков в статье Службы +.

+
+ + +
Поставщики контента
+ +
Поставщик контента (Content provider) управляет общим набором данных приложения. Данные можно хранить в +файловой системе, базе данных SQLite, в Интернете или любом другом постоянном месте хранения, к которому у вашего +приложения имеется доступ. Посредством поставщика контента другие приложения могут запрашивать или даже изменять +данные (если поставщик контента позволяет делать это). Например, в системе Android есть поставщик +контента, который управляет информацией контактов пользователя. Любое приложение, получившее соответствующие +разрешения, может запросить часть этого поставщика контента (например {@link +android.provider.ContactsContract.Data}), для чтения и записи сведений об определенном человеке. + +

Поставщики контента также используются для чтения и записи данных, доступ к которым внешним компонентам +приложение не предоставляет. Например, в образце приложения Note Pad с помощью +поставщика контента выполняется сохранение заметок.

+ +

Поставщик контента относится к подклассу класса {@link android.content.ContentProvider}. +Он должен реализовывать стандартный набор API-интерфейсов, с помощью которых другие приложения будут выполнять +транзакции. Подробные сведения можно найти в руководстве для разработчиков в статье Поставщики контента +.

+
+ + +
Приемники широковещательных сообщений
+ +
Приемник широковещательных сообщений (Broadcast receiver) представляет собой компонент, который реагирует на объявления +распространяемые по всей системе. Многие из этих объявлений рассылает система — например объявление о том, +что экран выключился, аккумулятор разряжен или был сделан фотоснимок. +Объявления также могут рассылаться приложениями, — например, чтобы сообщить другим приложениям о том, что +какие-то данные были загружены на устройство и теперь готовы для использования. Несмотря на то что приемники широковещательных сообщений +не имеют пользовательского интерфейса, они могутсоздавать уведомления в строке состояния, +чтобы предупредить пользователя о событии "рассылка объявления". Однако чаще всего они являются +просто "шлюзом" для других компонентов и предназначены для выполнения минимального объема работы. Например +, они могут инициировать выполнение службой определенных действий при возникновении события. + +

Приемник широковещательных сообщений относится к подклассу класса {@link android.content.BroadcastReceiver} +, а каждое такое сообщение предоставляется как объект {@link android.content.Intent}. Подробные сведения изложены +в руководстве, посвященном классу {@link android.content.BroadcastReceiver}.

+
+ +
+ + + +

Уникальной особенностью системы Android является то, что любое приложение может запустить компонент +другого приложения. Например, если вы хотите дать пользователю возможность фотографировать, используя +камеру устройства, то, поскольку наверняка имеется другое приложение, которое может выполнить это действие, вместо того чтобы разработать операцию фотографирования в своем приложении, вы можете вызвать +такое приложение. Вам не +нужно внедрять код из приложения для камеры или даже устанавливать на него ссылку. +Вместо этого вы можете просто запустить операцию фотографирования + из приложения для камеры. По завершении этой операции фотография будет возвращена в ваше приложение, и ее можно будет использовать. Для пользователя + это будет выглядеть как одно приложение.

+ +

Когда система запускает компонент, она запускает процесс для этого приложения (если +он еще не был запущен) и создает экземпляры классов, которые требуются этому компоненту. Например, если ваше приложение +запустит операцию фотографирования в приложении для камеры, эта операция +будет выполняться в процессе, который относится к этому стороннему приложению, а не в процессе вашего приложения. +Поэтому, в отличие от приложений для большинства других систем, в приложениях для Android отсутствует единая +точка входа (например, в них нет функции {@code main()}).

+ +

Поскольку система выполняет каждое приложение в отдельном процессе с такими правами доступа к файлам, которые +ограничивают доступ в другие приложения, ваше приложение не может напрямую вызвать компонент из +другого приложения. Это может сделать сама система Android. Поэтому, чтобы вызвать компонент в +другом приложении, необходимо сообщить системе о своем намерении (Intent) +запустить определенный компонент. После этого система активирует для вас этот компонент.

+ + +

Активация компонентов

+ +

Компоненты трех из четырех возможных типов — операции, службы и +приемники широковещательных сообщений — активируются асинхронным сообщением, которое называется Intent (намерение). +Объекты Intent связывают друг с другом отдельные компоненты во время выполнения, будь то это компоненты + вашего или стороннего приложения (эти объекты Intent можно представить себе +в виде мессенджеров, которые посылают другим компонентам запрос на выполнение действий).

+ +

Объект Intent создается с помощью объекта {@link android.content.Intent}, который описывает запрос на +активацию либо конкретного компонента, либо компонента конкретного типа — соответственно, намерение Intent +может быть явным или неявным.

+ +

Для операций и служб Объект Intent определяет действие, которое требуется выполнить (например, просмотреть (view) или +отправить (send) что-то), а также может указывать URI (Uniform Resource Identifier – унифицированный идентификатор ресурса) данных, с которыми это действие нужно выполнить (помимо прочих сведений, которые +нужно знать запускаемому компоненту). Например, объект Intent может передавать запрос +на выполнение операции "показать изображение" или "открыть веб-страницу". В некоторых ситуациях операцию можно +запустить, чтобы получить результат. В этом случае операция возвращает +результат также в виде объекта {@link android.content.Intent} (например, можно отправить сообщение Intent, чтобы дать +пользователю возможность выбрать контакт и вернуть его вам — в ответном сообщении Intent будет содержаться +URI, указывающий на выбранный контакт).

+ +

Для приемников широковещательных сообщений Intent просто определяет +передаваемое объявление (например, широковещательное сообщение о низком уровне заряда аккумулятора +содержит только строку "аккумулятор разряжен").

+ +

Компоненты четвертого типа – поставщики контента – сообщениями Intent не активируются. Они +активируются по запросу от {@link android.content.ContentResolver}. Процедура определения + контента (content resolver) обрабатывает все прямые транзакции с поставщиком контента, с тем чтобы этого не пришлось делать компоненту, который +выполняет транзакции с поставщиком. Вместо этого он вызывает методы для объекта {@link +android.content.ContentResolver}. Это формирует слой, абстрагирующий (в целях безопасности) поставщика +контента от компонента, запрашивающего информацию.

+ +

Для активации компонентов каждого типа имеются отдельные методы:

+
    +
  • Можно запустить операцию (или определить для нее какое-то новое действие), +передав объект {@link android.content.Intent} методу {@link android.content.Context#startActivity +startActivity()} или {@link android.app.Activity#startActivityForResult startActivityForResult()} +(если требуется, чтобы операция вернула результат).
  • +
  • Можно запустить службу (либо выдать работающей службе новые инструкции), +передав объект {@link android.content.Intent} методу {@link android.content.Context#startService +startService()}. Либо можно установить привязку к службе, передав объект{@link android.content.Intent} методу +{@link android.content.Context#bindService bindService()}.
  • +
  • Можно инициировать рассылку сообщений, передав объект {@link android.content.Intent} таким методам, как +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} и {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.
  • +
  • Можно выполнить запрос к поставщику контента, вызвав метод {@link +android.content.ContentProvider#query query()} для объекта {@link android.content.ContentResolver}.
  • +
+ +

Подробные сведения об использовании объектов Intent приведены в документе Объекты Intent и +фильтры объектов Intent. Более подробная информация об активации определенных компонентов также приведена +в следующих документах: Операции, Службы, {@link +android.content.BroadcastReceiver} и Поставщики контента.

+ + +

Файл манифеста

+ +

Для запуска компонента приложения системе Android необходимо знать, что +компонент существует. Для этого она читает файл {@code AndroidManifest.xml} приложения (файл +манифеста). В этом файле, который должен находиться в корневой папке +приложения, должны быть объявлены все компоненты приложения.

+ +

Помимо объявления компонентов приложения, манифест служит и для других целей, +среди которых:

+
    +
  • указание всех полномочий пользователя, которые требуются приложению, например разрешения на доступ в Интернет или +на чтение контактов пользователя;
  • +
  • объявление минимальногоуровня API, +требуемого приложению, с учетом того, какие API-интерфейсы оно использует;
  • +
  • объявление аппаратных и программных функций, которые нужны приложению или используются им, например камеры, +службы Bluetooth или сенсорного экрана;
  • +
  • указание библиотек API, с которыми необходимо связать приложение (отличные от API-интерфейсов платформы +Android), например библиотеки Google Maps +;
  • +
  • и многое другое.
  • +
+ + +

Объявление компонентов

+ +

Основная задача манифеста – это информировать систему о компонентах приложения. Например, + файл манифеста может объявлять операцию следующим образом:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<manifest ... >
+    <application android:icon="@drawable/app_icon.png" ... >
+        <activity android:name="com.example.project.ExampleActivity"
+                  android:label="@string/example_label" ... >
+        </activity>
+        ...
+    </application>
+</manifest>
+ +

Атрибут {@code android:icon} в элементе <application> +указывает на ресурсы для значка, который обозначает +приложение.

+ +

Атрибут {@code android:name} в элементе <activity> +указывает полное имя класса подкласса {@link +android.app.Activity}, а атрибут {@code android:label} указывает строку, +которую необходимо использовать в качестве метки операции, отображаемой для пользователя.

+ +

Все компоненты приложения необходимо объявлять следующим образом:

+
    +
  • элементы <activity> +для операций;
  • +
  • элементы <service> +для служб;
  • +
  • элементы <receiver> +для приемников широковещательных сообщений;
  • +
  • элементы <provider> +для поставщиков контента
  • +
+ +

Системе не видны операции, службы и поставщики контента, которые имеются в исходном коде, но не объявлены +в манифесте, поэтому они не могут быть запущены. А вот +приемники широковещательных сообщений +можно либо объявить в манифесте, либо создать динамически в коде (как объекты +{@link android.content.BroadcastReceiver}) и зарегистрировать в системе путем вызова +{@link android.content.Context#registerReceiver registerReceiver()}.

+ +

Подробные сведения о структуризации файла манифеста для приложения см. в документе Файл AndroidManifest.xml +.

+ + + +

Объявление возможностей компонентов

+ +

Как уже говорилось в разделе Активация компонентов, с помощью объекта +{@link android.content.Intent} можно запускать операции, службы и приемники широковещательных сообщений. Для этого в объекте Intent следует +явно указать имя целевого компонента (с помощью имени класса компонента). Однако +в полной мере возможности объектов Intent раскрываются при использовании концепции неявных Intent. В неявном сообщении Intent +просто описывается тип действия, которое требуется выполнить (а также, хотя это и не обязательно, дата, в которую вы бы хотели +выполнить это действие). Системе же предоставляется возможности найти на устройстве компонент, который может выполнить это +действие, и запустить его. При наличии нескольких компонентов, которые могут выполнить действие, описанное в сообщении +Intent, пользователь выбирает, какой из них будет использоваться.

+ +

Система определяет компоненты, которые могут ответить на сообщение Intent, путем сравнения +полученного сообщения Intent с фильтрами объектов Intent, указанными в файле манифеста других приложений, имеющихся + на устройстве.

+ +

При объявлении операции в манифесте своего приложения по желанию можно указать +фильтры объектов Intent, которые указывают возможности операции, с тем чтобы она могла реагировать на сообщения Intent +от других приложений. Чтобы объявить фильтр Intent для своего компонента, +необходимо добавить элемент {@code +<intent-filter>} в качестве дочернего для элемента объявления компонента.

+ +

Например, если вы создали приложение для работы с электронной почтой с операцией составления нового сообщения, вы можете +объявить фильтр для ответа на сообщения Intent типа "send" (для отправки нового сообщения электронной почты) следующим образом:

+
+<manifest ... >
+    ...
+    <application ... >
+        <activity android:name="com.example.project.ComposeEmailActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <data android:type="*/*" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
+ +

Затем, если другое приложение создаст объект Intent с действием {@link +android.content.Intent#ACTION_SEND} и передаст его в {@link android.app.Activity#startActivity +startActivity()}, система сможет запустить вашу операцию, дав пользователю возможность написать и отправить +сообщение электронной почты.

+ +

Подробные сведения о создании фильтров объектов Intent приведены в документе Объекты Intent и фильтры объектов Intent. +

+ + + +

Объявление требований приложения

+ +

Существует огромное количество устройств, работающих под управлением Android, и не все они имеют +одинаковые функциональные возможности. Чтобы ваше приложение не могло быть установлено на устройствах, +в которых отсутствуют функции, необходимые приложению, важно четко определить профиль для +типов устройств, поддерживаемых вашим приложением, указав требования к аппаратному и программному обеспечению в +файле манифеста. Эти объявления по большей части носят информационный характер, система их не +читает. Однако их читают внешние службы, например Google Play, с целью обеспечения +фильтрации для пользователей, которые ищут приложения для своих устройств.

+ +

Например, если вашему приложению требуется камера и оно использует API-интерфейсы из Android 2.1 (уровень API 7), +эти параметры следует объявить в файле манифеста в качестве требований следующим образом:

+ +
+<manifest ... >
+    <uses-feature android:name="android.hardware.camera.any"
+                  android:required="true" />
+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
+    ...
+</manifest>
+
+ +

Теперь ваше приложение нельзя будет установить из Google Play на устройствах, в которых нет камеры, а также на устройствах, работающих под управлением +Android версии ниже 2.1.

+ +

Однако можно также объявить, что приложение использует камеру, но для его работы она не является +непременно необходимой. В этом случае в приложении атрибуту {@code required} +необходимо задать значение {@code "false"}, а во время работы оно должно проверять, имеется ли +на устройстве камера, и при необходимости отключать свои функции, которые используют камеру.

+ +

Более подробные сведения об управлении совместимостью своего приложения с различными устройствами +приведены в документе Совместимость устройств +.

+ + + +

Ресурсы приложения

+ +

Приложение Android состоит не только из кода — ему необходимы такие существующие отдельно от исходного кода +ресурсы, как изображения, аудиофайлы и все, что связано с визуальным +представлением приложения. Например, необходимо определять анимацию, меню, стили, цвета +и макет пользовательских интерфейсов операций в файлах XML. Используя ресурсы приложения, можно без труда +изменять его различные характеристики, не меняя код, а, кроме того, —путем предоставления +наборов альтернативных ресурсов — можно оптимизировать свое приложение для работы с различными +конфигурациями устройств (например, для различных языков или размеров экрана).

+ +

Для каждого ресурса, включаемого в проект Android, инструменты SDK задают уникальный +целочисленный идентификатор, который может использоваться, чтобы сослаться на ресурс из кода приложения или из +других ресурсов, определенных в XML. Например, если в вашем приложении имеется файл изображения с именем {@code +logo.png} (сохраненный в папке {@code res/drawable/}), инструменты SDK сформируют идентификатор ресурса +под именем {@code R.drawable.logo}, с помощью которого на изображение можно будет ссылаться и вставлять его в +пользовательский интерфейс.

+ +

Один из наиболее важных аспектов предоставления ресурсов отдельно от исходного кода +заключается в возможности использовать альтернативные ресурсы для различных конфигураций +устройств. Например, определив строки пользовательского интерфейса в XML, вы сможете перевести их на другие +языки и сохранить эти переводы в отдельных файлах. Затем по квалификатору языка +, добавленному к имени каталога ресурса (скажем {@code res/values-fr/} для строк на французском +языке), и выбранному пользователем языку система Android применит к вашему пользовательскому интерфейсу строки на +соответствующем языке.

+ +

Android поддерживает разные квалификаторы для соответствующих ресурсов. Квалификатор + представляет собой короткую строку, которая включается в имена каталогов ресурсов с целью +определения конфигурации устройства, для которой эти ресурсы следует использовать. В качестве другого +примера можно сказать, что для своих операций следует создавать разные макеты, которые будут соответствовать +размеру и ориентации экрана устройства. Например, когда экран устройства имеет книжную +ориентацию (расположен вертикально), кнопки в макете можно также размещатьь по вертикали, а когда экран +развернут горизонтально (альбомная ориентация), кнопки следует размещать по горизонтали. Чтобы при изменении ориентации экрана изменялся макет, +можно определить два разных макета и применить соответствующий +квалификатор к имени каталога каждого макета. После этого система будет автоматически применять соответствующий +макет в зависимости от ориентации устройства.

+ +

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

+ + + +
+
+

Также читайте:

+
+
Объекты Intent и фильтры объектов Intent +
+
Сведения об использовании API-интерфейсов {@link android.content.Intent} для + активации таких компонентов приложений, как операции и службы, а также о предоставлении возможности другим приложениям + использовать компоненты своего приложения.
+
Операции
+
Сведения о создании экземпляра класса {@link android.app.Activity}, + который выдает определенный экран в вашем приложении с пользовательским интерфейсом.
+
Предоставление ресурсов
+
Описание структуры приложений Android, в которой ресурсы приложения существуют отдельно от + его кода, а также сведения о том, как предоставлять альтернативные ресурсы для определенных конфигураций + устройств. +
+
+
+
+

Возможно, вас также заинтересует:

+
+
Совместимость устройств
+
Сведения о том, каким образом система Android работает на устройствах разных типов, и общие сведения о том, + как оптимизировать свое приложение для каждого устройства или ограничить круг устройств, на которых может быть установлено + приложение.
+
Системные разрешения
+
Сведения о том, как система Android ограничивает доступ приложений к определенным API-интерфейсам с помощью системы + разрешений, которая требует согласия пользователя на использование этих API-интерфейсов вашим приложением.
+
+
+
+ diff --git a/docs/html-intl/intl/ru/guide/components/index.jd b/docs/html-intl/intl/ru/guide/components/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..41d5a3491bf2ecda71feb25dc8717cb9e94f75e7 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=Компоненты приложения +page.landing=true +page.landing.intro=Платформа приложений системы Android позволяет создавать функциональные и инновационные приложения с помощью набора компонентов, которые можно использовать многократно. В этом разделе рассказывается о том, как создавать компоненты, определяющие элементы структуры вашего приложения, и как связывать их воедино с помощью объектов Intent. +page.metaDescription=Платформа приложений системы Android позволяет создавать функциональные и инновационные приложения с помощью набора компонентов, которые можно использовать многократно. В этом разделе рассказывается о том, как создавать компоненты, определяющие элементы структуры вашего приложения, и как связывать их воедино с помощью объектов Intent. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +
+ +
+

Статьи блога

+ + +

Использование класса DialogFragment

+

В этой статье я расскажу, как с помощью DialogFragment с использованием вспомогательной библиотеки v4 (в целях обеспечения совместимости с устройствами, работающими под управлением системы с версией ниже, чем Honeycomb) можно отобразить простое диалоговое окно редактирования и вернуть результат в вызывающую операцию с помощью интерфейса.

+
+ + +

Фрагменты для всех

+

Сегодня мы выпустили библиотеку статических элементов, которая предоставляет доступ к тому же Fragments API (а также новому классу LoaderManager и нескольким другим классам), с тем чтобы приложения, совместимые с Android 1.6 и более поздними версиями, могли использовать фрагменты для создания пользовательских интерфейсов для планшетов.

+
+ + +

Многопоточность для повышения производительности

+

Для создания быстро реагирующих приложений рекомендуется, чтобы в основном потоке пользовательского интерфейса +выполнялся минимальный объем работы. Любая задача, которая в принципе может выполнять долго и привести к зависанию приложения, должна +обрабатываться в другом потоке.

+
+
+ +
+

Обучение

+ + +

Управление жизненным циклом операций

+

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

+
+ + +

Создание динамического интерфейса пользователя с использованием фрагментов

+

Данный курс обучения посвящен созданию динамического интерфейса пользователя с использованием +фрагментов и его оптимизации для устройств с экранами разных размеров, включая поддержку +устройств с версией Android 1.6.

+
+ + +

Общий доступ к контенту

+

В этом курсе обучения рассказывается о некоторых стандартных способах отправки и получения контента + приложениями с помощью API-интерфейсов Intent и объекта ActionProvider.

+
+
+ +
diff --git a/docs/html-intl/intl/ru/guide/components/intents-filters.jd b/docs/html-intl/intl/ru/guide/components/intents-filters.jd new file mode 100644 index 0000000000000000000000000000000000000000..d710081e4181141a9bfbcca0dadccd0ae26dfefd --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Объекты Intent и фильтры объектов Intent +page.tags="IntentFilter" +@jd:body + + + + + + +

{@link android.content.Intent} представляет собой объект обмена сообщениями, с помощью которого можно запросить выполнение действия +у компонента другого приложения. +Несмотря на то, что объекты Intent упрощают обмен данными между компонентами по нескольким аспектам, в основном они используются +в трех ситуациях:

+ +
    +
  • Для запуска операции: +

    Компонент {@link android.app.Activity} представляет собой один экран в приложении. Для запуска нового +экземпляра компонента {@link android.app.Activity} необходимо передать объект {@link android.content.Intent} +методу{@link android.content.Context#startActivity startActivity()}. Объект {@link android.content.Intent} +описывает операцию, которую требуется запустить, а также содержит все остальные необходимые данные.

    + +

    Если после завершения операции от нее требуется получить результат, +вызовите метод {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Ваша операция получит результат +в виде отдельного объекта {@link android.content.Intent} в обратном вызове метода {@link +android.app.Activity#onActivityResult onActivityResult()} операции. +Подробные сведения см. в руководстве Операции.

  • + +
  • Для запуска службы: +

    {@link android.app.Service} является компонентом, который выполняет действия в фоновом режиме +без пользовательского интерфейса. Службу можно запустить для выполнения однократного действия +(например, чтобы загрузить файл), передав объект{@link android.content.Intent} +методу {@link android.content.Context#startService startService()}. Объект {@link android.content.Intent} +описывает службу, которую требуется запустить, а также содержит все остальные необходимые данные.

    + +

    Если служба сконструирована с интерфейсом клиент-сервер, к ней +можно установить привязку из другого компонента, передав объект{@link android.content.Intent} методу {@link +android.content.Context#bindService bindService()}. Подробные сведения см. в руководстве Службы.

  • + +
  • Для рассылки широковещательных сообщений: +

    Широковещательное сообщение ― это сообщение, которое может принять любое приложение. Система выдает различные +широковещательные сообщения о системных событиях, например, когда система загружается или устройство начинает заряжаться. +Для выдачи широковещательных сообщений другим приложениям необходимо передать объект {@link android.content.Intent} +методу {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()} или {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.

    +
  • +
+ + + + +

Типы объектов Intent

+ +

Есть два типа объектов Intent:

+ +
    +
  • Явные объекты Intent указывают компонент, который требуется запустить, по имени ( +полное имя класса). Явные объекты Intent обычно используются для запуска компонента из +вашего собственного приложения, поскольку вам известно имя класса операции или службы, которую необходимо запустить. Например +, можно запустить новую операцию в ответ на действие пользователя или запустить службу, чтобы загрузить +файл в фоновом режиме.
  • + +
  • Неявные объекты Intent не содержат имени конкретного компонента. Вместо этого они в целом объявляют действие, +которое требуется выполнить, что дает возможность компоненту из другого приложения обработать этот запрос. Например, если требуется +показать пользователю место на карте, то с помощью неявного объекта Intent можно запросить, чтобы это сделало другое приложение, в котором +такая возможность предусмотрена.
  • +
+ +

Когда создан явный объект Intent для запуска операции или службы, система немедленно +запускает компонент приложения, указанный в объекте {@link android.content.Intent}.

+ +
+ +

Рисунок 1. Схематическое изображение процесса передачи неявного объекта Intent +по системе для запуска другой операции: [1] Операция А создает объект +{@link android.content.Intent} с описанием действия и передает его методу {@link +android.content.Context#startActivity startActivity()}. [2] Система Android ищет во всех +приложениях фильтры Intent, которые соответствуют данному объекту Intent. Когда приложение с подходящим фильтром найдено, [3] система +запускает соответствующую операцию (Операция B), вызвав ее метод {@link +android.app.Activity#onCreate onCreate()} и передав ему объект {@link android.content.Intent}. +

+
+ +

Когда создан неявный объект Intent, система Android находит подходящий компонент путем +сравнения содержимого объекта Intent с фильтрами Intent, объявленными в файлах манифеста других приложений, имеющихся на +устройстве. Если объект Intent совпадает с фильтром Intent, система запускает этот компонент и передает ему +объект {@link android.content.Intent}. Если подходящими оказываются несколько фильтров Intent, система +выводит диалоговое окно, где пользователь может выбрать приложение для выполнения данного действия.

+ +

Фильтр Intent представляет собой выражение в файле манифеста приложения, +указывающее типы объектов Intent, которые мог бы +принимать компонент. Например, объявив фильтр Intent для операции, +вы даете другим приложениям возможность напрямую запускать вашу операцию с помощью некоторого объекта Intent. +Точно так же, если вы не объявите какие-либо фильтры Intent для операции, то ее можно будет запустить +только с помощью явного объекта Intent.

+ +

Внимание! В целях обеспечения безопасности приложения всегда используйте явный объект +Intent при запуске {@link android.app.Service} и не +объявляйте фильтры Intent для своих служб. Запуск служб с помощью неявных объектов Intent является +рискованным с точки зрения безопасности, поскольку нельзя быть на абсолютно уверенным, какая служба отреагирует на такой объект Intent, +а пользователь не может видеть, какая служба запускается. Начиная с Android 5.0 (уровень API 21) система +вызывает исключение при вызове метода {@link android.content.Context#bindService bindService()} +с помощью неявного объекта Intent.

+ + + + + +

Создание объекта Intent

+ +

Объект {@link android.content.Intent} содержит информацию, на основании которой система Android +определяет, какой компонент требуется запустить (например, точное имя компонента или категорию +компонентов, которые должны получить этот объект Intent), а также сведения, которые необходимы компоненту-получателю, +чтобы надлежащим образом выполнить действие (а именно — выполняемое действие и данные, с которыми его требуется выполнить).

+ + +

Основные сведения, содержащиеся в объекте {@link android.content.Intent}:

+ +
+ +
Имя компонента
+
Имя компонента, который требуется запустить. + +

Эта информация является необязательной, но именно она и делает объект Intent +явным. Ее наличие означает, что объект Intent следует доставить только компоненту приложения, +определенному по имени. При отсутствии имени компонента объект Intent является неявным, а +система определяет, какой компонент получит этот объект Intent по другим сведениям, которые в нем содержатся +(например, по действию, данным и категории — см. описание далее). Поэтому, если вам требуется запустить определенный +компонент из своего приложения, следует указать его имя.

+ +

Примечание. При запуске {@link android.app.Service} следует +всегда указывать имя компонента. В противном случае вы не сможете быть на абсолютно уверенным в том, какая служба +отреагирует на объект Intent, а пользователь не может видеть, какая служба запускается.

+ +

Это поле объекта {@link android.content.Intent} представляет собой объект +{@link android.content.ComponentName}, который можно указать с помощью полного +имени класса целевого компонента, включая имя пакета приложения. Например, +{@code com.example.ExampleActivity}. Задать имя компонента можно с помощью метода {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()} или конструктора +{@link android.content.Intent}.

+ +
+ +

Действие
+
Строка, определяющая стандартное действие, которое требуется выполнить (например, view (просмотр) или pick (выбор)). + +

При выдаче объектов Intent с широковещательными сообщениями это действие, которое произошло и о котором сообщается. +Действие в значительной степени определяет, каким образом структурирована остальная часть объекта Intent,—в частности, +что именно содержится в разделе данных и дополнительных данных. + +

Для использования объектами Intent в пределах своего приложения (либо для использования другими +приложениями, чтобы вызывать компоненты из вашего приложения) можно указать собственные действия. Обычно же следует использовать константы действий, +определенные классом {@link android.content.Intent} или другими классами платформы. Вот несколько +стандартных действий для запуска операции:

+ +
+
{@link android.content.Intent#ACTION_VIEW}
+
Используйте это действие в объекте Intent с методом {@link + android.content.Context#startActivity startActivity()}, когда имеется определенная информация, которую + операция может показать пользователю, например, фотография в приложении галереи или адрес для + просмотра в картографическом приложении.
+ +
{@link android.content.Intent#ACTION_SEND}
+
Его еще называют объектом Intent "share" (намерение предоставить общий доступ). Это действие следует использовать в объекте Intent с методом {@link + android.content.Context#startActivity startActivity()}, при наличии определенных данных, доступ к которым пользователь может + предоставить через другое приложение, например приложение для работы с электронной почтой или социальными сетями.
+
+ +

Другие константы, определяющие стандартные действия, см. в справочнике по классу {@link android.content.Intent} +. Другие действия определяются +в других частях платформы Android. Например, в {@link android.provider.Settings} определяются действия, +открывающие ряд экранов приложения настройки системы.

+ +

Действие можно указать для объекта Intent с методом {@link android.content.Intent#setAction +setAction()} или конструктором {@link android.content.Intent}.

+ +

Если вы определяете собственные действия, обязательно используйте в качестве их префикса имя пакета +вашего приложения. Например:

+
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
+
+ +
Данные
+
URI (объект {@link android.net.Uri}), ссылающийся на данные, с которыми будет выполняться действие и/или +тип MIME этих данных. Тип передаваемых данных обычно определяется действием объекта Intent. Например, +если действием является {@link android.content.Intent#ACTION_EDIT}, в данных должен содержаться +URI документа, который требуется отредактировать. + +

При создании объекта Intent, +помимо URI, зачастую бывает важно указать тип данных (их тип MIME). +Например, операция, которая может выводить на экран изображения, скорее всего, не сможет +воспроизвести аудиофайл, даже если и у тех, и у других данных будут одинаковые форматы URI. +Поэтому указание типа MIME данных помогает системе Android +найти наиболее подходящий компонент для получения вашего объекта Intent. +Однако тип MIME иногда можно унаследовать от URI — в частности, когда данные представляют собой +{@code content:} URI, который указывает, что данные находятся на устройстве и ими управляет +{@link android.content.ContentProvider}, а это дает возможность системе видеть тип MIME данных.

+ +

Чтобы задать только URI данных, вызовите {@link android.content.Intent#setData setData()}. +Чтобы задать только тип MIME, вызовите {@link android.content.Intent#setType setType()}. При необходимости +оба этих параметра можно в явном виде задать с помощью {@link +android.content.Intent#setDataAndType setDataAndType()}.

+ +

Внимание! Если требуется задать и URI, и тип MIME, +не вызывайте {@link android.content.Intent#setData setData()} и +{@link android.content.Intent#setType setType()}, поскольку каждый из этих методов аннулирует результат выполнения другого. +Чтобы задать URI и тип MIME всегда используйте + метод {@link android.content.Intent#setDataAndType setDataAndType()}.

+
+ +

Категория
+
Строка, содержащая прочие сведения о том, каким компонентом +должна выполняться обработка этого объекта Intent. В объект Intent можно поместить любое количество +описаний категорий, однако большинству объектов Intent категория не требуется. +Вот некоторые стандартные категории: + +
+
{@link android.content.Intent#CATEGORY_BROWSABLE}
+
Целевая операция позволяет запускать себя веб-браузером для отображения данных, + указанных по ссылке — например, изображения или сообщения электронной почты. +
+
{@link android.content.Intent#CATEGORY_LAUNCHER}
+
Эта операция является начальной операцией задачи, она указана в + средстве запуска приложений системы. +
+
+ +

Полный список категорий см. в описании класса {@link android.content.Intent} +.

+ +

Указать категорию можно с помощью{@link android.content.Intent#addCategory addCategory()}.

+
+
+ + +

Приведенные выше свойства (имя компонента, действие, данные и категория) представляют собой +характеристики, определяющие объект Intent. На основании этих свойств система Android +может решить, какой компонент следует запустить.

+ +

Однако в объекте Intent может быть приведена и другая информация, которая не влияет на то, +каким образом определяется требуемый компонент приложения. Объект Intent также может содержать:

+ +
+
Дополнительные данные
+
Пары "ключ-значение", содержащие прочую информацию, которая необходима для выполнения запрошенного действия. +Точно так же, как некоторые действия используют определенные виды URI данных, некоторые действия используют определенные дополнительные данные. + +

Добавлять дополнительные данные можно с помощью различных методов {@link android.content.Intent#putExtra putExtra()}, +каждый из которых принимает два параметра: имя и значение ключа. +Также можно создать объект {@link android.os.Bundle} со всеми дополнительными данными, затем вставить +объект {@link android.os.Bundle} в объект {@link android.content.Intent} с помощью метода {@link +android.content.Intent#putExtras putExtras()}.

+ +

Например, при создании объекта Intent для отправки сообщения электронной почты с методом +{@link android.content.Intent#ACTION_SEND} можно указать получателя с помощью ключа +{@link android.content.Intent#EXTRA_EMAIL}, а тему сообщения ― с помощью ключа +{@link android.content.Intent#EXTRA_SUBJECT}.

+ +

Класс {@link android.content.Intent} указывает много констант {@code EXTRA_*} +для стандартных типов данных. Если вам требуется объявить собственные дополнительные ключи (для объектов Intent, которые +принимает ваше приложение), обязательно указывайте в качестве префикса +имя пакета своего приложения. Например:

+
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
+
+ +
Флаги
+
Флаги, определенные в классе {@link android.content.Intent}, которые действуют как метаданные для объекта +Intent. Флаги должны указывать системе Android, каким образом следует запускать операцию (например, к какой +задаче должна принадлежать операция +) и как с ней обращаться после запуска (например, будет ли она указана в списке последних +операций). + +

Подробные сведения см. в документе, посвященном методу {@link android.content.Intent#setFlags setFlags()}.

+
+ +
+ + + + +

Пример явного объекта Intent

+ +

Явные объекты Intent используются для запуска конкретных компонентов приложения, например +определенной операции или службы. Чтобы создать явный объект Intent, задайте +имя компонента для объекта {@link android.content.Intent} — все +остальные свойства объекта Intent можно не задавать.

+ +

Например, если в своем приложении вы создали службу с именем {@code DownloadService}, +предназначенную для загрузки файлов из Интернета, то для ее запуска можно использовать следующий код:

+ +
+// Executed in an Activity, so 'this' is the {@link android.content.Context}
+// The fileUrl is a string URL, such as "http://www.example.com/image.png"
+Intent downloadIntent = new Intent(this, DownloadService.class);
+downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl));
+startService(downloadIntent);
+
+ +

Конструктор {@link android.content.Intent#Intent(Context,Class)} +предоставляет {@link android.content.Context} приложению, а +компоненту ― объект {@link java.lang.Class}. Фактически +этот объект Intent явно запускает класс{@code DownloadService} в приложении.

+ +

Подробные сведения о создании и запуске службы см. в руководстве +Службы.

+ + + + +

Пример неявного объекта Intent

+ +

Неявный объект Intent указывает действие, которым может быть вызвано любое имеющееся на устройстве приложение, способное +выполнить это действие. Неявные объекты Intent используются, когда ваше приложение не может выполнить +то или иное действие, а другие приложения, скорее всего, могут и вы хотите, чтобы пользователь имел возможность выбрать, какое приложение для этого использовать.

+ +

Например, если у вас есть контент и вы хотите, чтобы пользователь поделился им с другими людьми, создайте объект Intent +с действием {@link android.content.Intent#ACTION_SEND} +и добавьте дополнительные данные, указывающие на контент, общий доступ к которому следует предоставить. Когда с помощью этого объекта Intent вы вызываете +{@link android.content.Context#startActivity startActivity()}, пользователь сможет +выбрать приложение, посредством которого к контенту будет предоставлен общий доступ.

+ +

Внимание! Возможна ситуация, когда на устройстве пользователя не будетникакого +приложения, которое может откликнуться на неявный объект Intent, отправленный вами методу {@link android.content.Context#startActivity +startActivity()}. В этом случае вызов закончится неудачей, а работа приложения аварийно завершится. Чтобы проверить, +будет получен ли операцией объект Intent, вызовите метод {@link android.content.Intent#resolveActivity +resolveActivity()} для своего объекта {@link android.content.Intent}. Если результатом будет значение, отличное от null, +значит, имеется хотя бы одно приложение, которое способно откликнуться на объект Intent и можно вызывать +{@link android.content.Context#startActivity startActivity()}. Если же результатом будет значение null, +объект Intent не следует использовать и по возможности следует отключить функцию, которая выдает +этот объект Intent.

+ + +
+// Create the text message with a string
+Intent sendIntent = new Intent();
+sendIntent.setAction(Intent.ACTION_SEND);
+sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
+sendIntent.setType("text/plain");
+
+// Verify that the intent will resolve to an activity
+if (sendIntent.resolveActivity(getPackageManager()) != null) {
+    startActivity(sendIntent);
+}
+
+ +

Примечание. В этом случае URI не используется, а вместо этого следует объявить +тип данных объекта Intent, чтобы указать контент, содержащийся в дополнительных данных.

+ + +

При вызове метода {@link android.content.Context#startActivity startActivity()} система +анализирует все установленные приложения, чтобы определить, какие из них могут откликнуться на объект Intent этого вида (объект +Intent с действием {@link android.content.Intent#ACTION_SEND} и данными "text/plain" +). Если имеется только одно подходящее приложение, оно будет сразу же открыто и получит данный объект +Intent. Если объект Intent принимают несколько операций, система +отображает диалоговое окно, в котором пользователь может выбрать приложение для выполнения данного действия.

+ + +
+ +

Рисунок 2. Диалоговое окно выбора.

+
+ +

Принудительное использование блока выбора приложения

+ +

При наличии нескольких приложений, откликающихся на ваш неявный объект Intent, +пользователь может выбрать требуемое приложение и указать, что оно будет по умолчанию выполнять это +действие. Это удобно в случае действия, для выполнения которого +пользователь обычно хочет всегда использовать одно и то же приложение, например, при открытии веб-страницы (пользователи +обычно используют один и тот же браузер).

+ +

Однако если на объект Intent могут откликнуться несколько приложений, возможно, пользователь предпочтет каждый раз использовать другое +приложение, поэтому следует явно выводить диалоговое окно выбора. В диалоговом окне +выбора приложения пользователю предлагается при каждом запуске выбирать, какое приложение использовать для действия (пользователь не может выбрать приложение, +используемое по умолчанию). Например, когда ваше приложение выполняет операцию "share" (поделиться) с помощью действия {@link +android.content.Intent#ACTION_SEND}, пользователи могут, в зависимости от ситуации, предпочесть каждый раз делать это с помощью разных приложений +, поэтому следует всегда использовать диалоговое окно выбора, как показано на рисунке 2.

+ + + + +

Чтобы вывести на экран блок выбора приложения, создайте {@link android.content.Intent} с помощью {@link +android.content.Intent#createChooser createChooser()} и передайте его {@link +android.app.Activity#startActivity startActivity()}. Например:

+ +
+Intent sendIntent = new Intent(Intent.ACTION_SEND);
+...
+
+// Always use string resources for UI text.
+// This says something like "Share this photo with"
+String title = getResources().getString(R.string.chooser_title);
+// Create intent to show the chooser dialog
+Intent chooser = Intent.createChooser(sendIntent, title);
+
+// Verify the original intent will resolve to at least one activity
+if (sendIntent.resolveActivity(getPackageManager()) != null) {
+    startActivity(chooser);
+}
+
+ +

В результате на экран будет выведено диалоговое окно со списком приложений, которые могут отреагировать на объект Intent, переданный методу {@link +android.content.Intent#createChooser createChooser()} и используют указанный текст в качестве +заголовка диалога.

+ + + + + + + + + +

Получение неявного объекта Intent

+ +

Чтобы указать, какие неявные объекты Intent может принимать ваше приложение, объявите один или несколько фильтров Intent для +каждого компонента приложения с помощью элемента {@code <intent-filter>}файле манифеста. +Каждый фильтр Intent указывает тип объектов Intent, которые принимает компонент на основании действия, +данных и категории, заданных в объекте Intent. Система передаст неявный объект Intent вашему приложению, только если +он может пройти через один из ваших фильтров Intent.

+ +

Примечание. Явный объект Intent всегда доставляется его целевому компоненту, +без учета любых фильтров Intent, объявленных компонентом.

+ +

Компонент приложения должен объявлять отдельные фильтры для каждой уникальной работы, которую он может выполнить. +Например, у операции из приложения для работы с галереей изображений может быть два фильтра: один фильтр +для просмотра изображения, и второй для его редактирования. Когда операция запускается, +она анализирует объект {@link android.content.Intent} и выбирает режим своей работы на основании информации, +приведенной в {@link android.content.Intent} (например, показывать элементы управления редактора или нет).

+ +

Каждый фильтр Intent определяется элементом {@code <intent-filter>} +в файле манифеста приложения, указанном в объявлении соответствующего компонента приложения (например, +в элементе {@code <activity>}). + Внутри элемента {@code <intent-filter>}, +можно указать тип объектов Intent, которые будут приниматься, с помощью одного или нескольких +из следующих трех элементов:

+ +
+
{@code <action>}
+
Объявляет принимаемое действие, заданное в объекте Intent, в атрибуте {@code name}. Значение + должно быть текстовой строкой действия, а не константой класса.
+
{@code <data>}
+
Объявляет тип принимаемых данных, для чего используется один или несколько атрибутов, указывающих различные + составные части URI данных (scheme, host, port, + path и т. д.) и тип MIME.
+
{@code <category>}
+
Объявляет принимаемую категорию, заданную в объекте Intent, в атрибуте {@code name}. Значение + должно быть текстовой строкой действия, а не константой класса. + +

Примечание. Для получения неявных объектов Intent + необходимо включить категорию +{@link android.content.Intent#CATEGORY_DEFAULT} в фильтр Intent. Методы + {@link android.app.Activity#startActivity startActivity()} и + {@link android.app.Activity#startActivityForResult startActivityForResult()} обрабатывают все объекты Intent, как если бы они объявляли + категорию{@link android.content.Intent#CATEGORY_DEFAULT}. + Если вы не объявляете эту категорию в своем фильтре Intent, никакие неявные объекты Intent не будут переданы в +вашу операцию.

+
+
+ +

В следующем примере объявлена операция с фильтром Intent, определяющим получение объекта Intent +{@link android.content.Intent#ACTION_SEND}, когда данные относятся к типу text:

+ +
+<activity android:name="ShareActivity">
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+    </intent-filter>
+</activity>
+
+ +

Можно создавать фильтры, в которых будет несколько экземпляров +{@code <action>}, +{@code <data>} или +{@code <category>}. +В этом случае просто нужно убедиться в том, что компонент может справиться с любыми сочетаниями +этих элементов фильтра.

+ +

Когда требуется обрабатывать объекты Intent нескольких видов, но только в определенных сочетаниях +действия, типа данных и категории, необходимо создать несколько фильтров Intent.

+ + + + +

Неявный объект Intent проверяется фильтром путем сравнения объекта Intent с каждым из +этих трех элементов. Чтобы объект Intent был доставлен компоненту, он должен пройти все три теста. +Если он не будет соответствовать хотя бы одному из них, система Android не доставит этот объект Intent +компоненту. Однако, поскольку у компонента может быть несколько фильтров Intent, объект Intent, который +не проходит через один из фильтров компонента, может пройти через другой фильтр. +Подробные сведения о том, каким образом система принимает решения по поводу объектов Intent, см. в приведенном далее разделе + Разрешение объектов Intent.

+ +

Внимание! Чтобы случайно не запустить +{@link android.app.Service} другого приложения, всегда используйте явные объекты Intent для запуска собственных служб и не +объявляйте для них фильтры Intent.

+ +

Примечание. +Фильтры Intent необходимо объявлять в файле манифеста для всех операций. +Фильтры для приемников широковещательных сообщений можно регистрировать динамически путем вызова +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}. После этого регистрацию приемника широковещательных сообщений можно отменить с помощью {@link +android.content.Context#unregisterReceiver unregisterReceiver()}. В результате ваше приложение +сможет воспринимать определенные объявления только в течение указанного периода времени в процессе работы +приложения.

+ + + + + + + +

Примеры фильтров

+ +

Чтобы лучше понять различные режимы работы фильтров Intent, рассмотрите следующий фрагмент +из файла манифеста приложения для работы с социальными сетями.

+ +
+<activity android:name="MainActivity">
+    <!-- This activity is the main entry, should appear in app launcher -->
+    <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+</activity>
+
+<activity android:name="ShareActivity">
+    <!-- This activity handles "SEND" actions with text data -->
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+    </intent-filter>
+    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <action android:name="android.intent.action.SEND_MULTIPLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
+        <data android:mimeType="image/*"/>
+        <data android:mimeType="video/*"/>
+    </intent-filter>
+</activity>
+
+ +

Первая операция ({@code MainActivity}) является основной точкой входа приложения — это операция, которая +открывается, когда пользователь запускает приложение нажатием на его значок:

+
    +
  • Действие {@link android.content.Intent#ACTION_MAIN} + указывает на то, что это основная точка входа, и не ожидает никаких данных объекта Intent.
  • +
  • Категория {@link android.content.Intent#CATEGORY_LAUNCHER} указывает, что значок этой операции + следует поместить в средство запуска приложений системы. Если элемент {@code <activity>} + не содержит указаний на конкретный значок с помощью {@code icon}, то система воспользуется значком из элемента {@code <application>} +.
  • +
+

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

+ +

Вторая операция ({@code ShareActivity}) предназначена для упрощения обмена текстовым и мультимедийным +контентом. Несмотря на то, что пользователи могут входить в эту операцию, выбрав ее из {@code MainActivity}, +они также могут входить в {@code ShareActivity} напрямую из другого приложения, которое выдает неявный объект +Intent, соответствующий одному из двух фильтров Intent.

+ +

Примечание. Тип MIME +({@code +application/vnd.google.panorama360+jpg}) является особым типом данных, указывающим на +панорамные фотографии, с которыми можно работать с помощью API-интерфейсов Google +panorama.

+ + + + + + + + + + + + + +

Использование ожидающего объекта Intent

+ +

Объект {@link android.app.PendingIntent} является оболочкой, в которую заключается объект {@link +android.content.Intent}. Объект {@link android.app.PendingIntent} +в основном предназначен для того, чтобы предоставлять разрешение внешним приложениям +на использование содержащегося в нем объекта {@link android.content.Intent}, как если бы он исполнялся из +процесса вашего собственного приложения.

+ +

Основные варианты использования ожидающего объекта Intent:

+
    +
  • Объявление объекта Intent, который должен будет исполняться, когда пользователь выполняет действие с вашим уведомлением + ({@link android.app.NotificationManager} системы Android + исполняет {@link android.content.Intent}). +
  • Объявление объекта Intent, который должен будет исполняться, когда пользователь выполняет действие с вашим +виджетом приложения + (приложение главного экрана исполняет {@link android.content.Intent}). +
  • Объявление объекта Intent, который должен будет исполняться в указанное время в будущем ({@link android.app.AlarmManager} системы +Android исполняет {@link android.content.Intent}). +
+ +

Поскольку каждый объект {@link android.content.Intent} предназначен для обработки компонентом приложения, +который относится к определенному типу ({@link android.app.Activity}, {@link android.app.Service} или + {@link android.content.BroadcastReceiver}), объект {@link android.app.PendingIntent} также следует создавать +с учетом этого обстоятельства. При использовании ожидающего объекта Intent ваше приложение не будет +исполнять объект Intent вызовом, например, {@link android.content.Context#startActivity +startActivity()}. Вместо этого вам необходимо будет объявить требуемый тип компонента при создании +{@link android.app.PendingIntent} путем вызова соответствующего метода-создателя:

+ +
    +
  • Метод {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} для + {@link android.content.Intent}, который запускает {@link android.app.Activity}.
  • +
  • Метод {@link android.app.PendingIntent#getService PendingIntent.getService()} для + {@link android.content.Intent}, который запускает {@link android.app.Service}.
  • +
  • Метод {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} для + {@link android.content.Intent}, который запускает {@link android.content.BroadcastReceiver}.
  • +
+ +

Если только ваше приложение не принимает ожидающие объекты Intent от других приложений, +указанные выше методы создания {@link android.app.PendingIntent} являются единственными методами +{@link android.app.PendingIntent}, которые вам когда-либо понадобятся.

+ +

Каждый метод принимает текущий {@link android.content.Context} приложения, объект +{@link android.content.Intent}, который требуется поместить в оболочку, и один или несколько флагов, указывающих, +каким образом следует использовать объект Intent (например, можно ли использовать объект Intent неоднократно).

+ +

Подробные сведения об использовании ожидающих объектов Intent приведены в документации по каждому +из соответствующих вариантов использования, например, в руководствах, посвященным API-интерфейсам: УведомленияВиджеты приложений.

+ + + + + + + +

Разрешение объектов Intent

+ + +

Когда система получает неявный объект Intent для запуска операции, она выполняет поиск +наиболее подходящей операции путем сравнения объекта Intent с фильтрами Intent по трем критериям:

+ +
    +
  • действие объекта Intent; +
  • данные объекта Intent (тип URI и данных); +
  • категория объекта Intent. +
+ +

В следующих разделах описывается, каким образом объекты Intent сопоставляются с соответствующими компонентами, +а именно, как должен быть фильтр Intent объявлен в файле манифеста приложения.

+ + +

Тестирование действия

+ +

Для указания принимаемых действий объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +{@code +action>}. Например:

+ +
+<intent-filter>
+    <action android:name="android.intent.action.EDIT" />
+    <action android:name="android.intent.action.VIEW" />
+    ...
+</intent-filter>
+
+ +

Чтобы пройти через этот фильтр, действие, указанное в объекте {@link android.content.Intent}, + должно соответствовать одному или нескольким действиям, перечисленным в фильтре.

+ +

Если в фильтре не перечислены какие-либо действия, объекту +Intent будет нечему соответствовать, поэтому все объекты Intent не пройдут этот тест. Однако, если в объекте {@link android.content.Intent} +не указано действие, он пройдет тест (если в фильтре +содержится хотя бы одно действие).

+ + + +

Тестирование категории

+ +

Для указания принимаемых категорий объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +{@code +<category>}. Например:

+ +
+<intent-filter>
+    <category android:name="android.intent.category.DEFAULT" />
+    <category android:name="android.intent.category.BROWSABLE" />
+    ...
+</intent-filter>
+
+ +

Чтобы объект Intent прошел тестирование категории, все категории, приведенные в объекте {@link android.content.Intent}, +должны соответствовать категории из фильтра. Обратное не требуется — фильтр Intent может +объявлять и другие категории, которых нет в объекте {@link android.content.Intent}, объект +{@link android.content.Intent} при этом все равно пройдет тест. Поэтому объект Intent без категорий +всегда пройдет этот тест, независимо от того, какие категории объявлены в фильтре.

+ +

Примечание. +Система Android автоматически применяет категорию {@link android.content.Intent#CATEGORY_DEFAULT} +ко всем неявным объектам Intent, которые передаются в {@link +android.content.Context#startActivity startActivity()} и {@link +android.app.Activity#startActivityForResult startActivityForResult()}. +Поэтому, если вы хотите, чтобы ваша операция принимала неявные объекты Intent, в ее фильтрах Intent +должна быть указана категория для {@code "android.intent.category.DEFAULT"} (как +показано в предыдущем примере {@code <intent-filter>}).

+ + + +

Тестирование данных

+ +

Для указания принимаемых данных объекта Intent фильтр Intent может объявлять любое (в том числе нулевое) число элементов +{@code +<data>}. Например:

+ +
+<intent-filter>
+    <data android:mimeType="video/mpeg" android:scheme="http" ... />
+    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
+    ...
+</intent-filter>
+
+ +

Каждый элемент <data> +может конкретизировать структуру URI и тип данных (тип мультимедиа MIME). Имеются отдельные +атрибуты — {@code scheme}, {@code host}, {@code port} +и {@code path} — для каждой составной части URI: +

+ +

{@code <scheme>://<host>:<port>/<path>}

+ +

+Например: +

+ +

{@code content://com.example.project:200/folder/subfolder/etc}

+ +

В этом URI схема имеет вид {@code content}, узел ― {@code com.example.project}, +порт ― {@code 200}, а путь ― {@code folder/subfolder/etc}. +

+ +

Каждый из этих атрибутов является необязательным в элементе {@code <data>}, +однако имеются линейные зависимости:

+
    +
  • Если схема не указана, узел игнорируется.
  • +
  • Если узел не указан, порт игнорируется.
  • +
  • Если не указана ни схема, ни узел, путь игнорируется.
  • +
+ +

Когда URI, указанный в объекте Intent, сравнивается с URI из фильтра, +сравнение выполняется только с теми составными частями URI, которые приведены в фильтре. Например:

+
    +
  • Если в фильтре указана только схема, то все URI с этой схемой будет соответствовать +фильтру.
  • +
  • Если в фильтре указаны схема и полномочия, но отсутствует путь, все URI +с такими же схемой и полномочиями пройдут фильтр, а их пути учитываться не будут.
  • +
  • Если в фильтре указаны схема, полномочия и путь, то только URI с такими же схемой, +полномочиями и путем пройдут фильтр.
  • +
+ +

Примечание. Путь может быть +указан с подстановочным символом (*), чтобы потребовалось только частичное соответствие имени пути.

+ +

При выполнении тестирования данных сравнивается и URI, и тип MIME, указанные в объекте Intent, с URI +и типом MIME из фильтра. Действуют следующие правила: +

+ +
    +
  1. Объект Intent, который не содержит ни URI, ни тип MIME, пройдет этот +тест, только если в фильтре не указано никаких URI или типов MIME.
  2. + +
  3. Объект Intent, в котором имеется URI, но отсутствует тип MIME (ни явный, ни тот, который можно вывести из +URI), пройдет этот тест, только если URI соответствует формату URI из фильтра +, а в фильтре также не указан тип MIME.
  4. + +
  5. Объект Intent, в котором имеется тип MIME, но отсутствует URI, пройдет этот тест, +только если в фильтре указан тот же тип MIME и не указан формат URI.
  6. + +
  7. Объект Intent, в котором имеется и URI, и тип MIME (явный или тот, который можно вывести из +URI), пройдет только часть этого теста, проверяющую тип MIME, +в том случае, если этот тип совпадает с типом, приведенным в фильтре. Он пройдет часть этого теста, которая проверяет URI, +либо если его URI совпадает с URI из фильтра, либо если этот объект содержит URI {@code content:} +или {@code file:}, а в фильтре URI не указан. Другими словами, +предполагается, что компонент поддерживает данные {@code content:} и {@code file:}, если в его +фильтре указан только тип MIME.

  8. +
+ +

+Это последнее правило (правило (d)) отражает ожидание того, +что компоненты будут в состоянии получать локальные данные из файла или от поставщика контента. +Поэтому их фильтры могут содержать только тип данных, а явно +указывать схемы {@code content:} и {@code file:} не требуется. +Это типичный случай. Например, такой элемент {@code <data>}, как +приведенный далее, сообщает системе Android, что компонент может получать данные изображений от поставщика +контента и выводить их на экран: +

+ +
+<intent-filter>
+    <data android:mimeType="image/*" />
+    ...
+</intent-filter>
+ +

+Поскольку имеющиеся данные преимущественно распространяются поставщиками контента, фильтры, в которых +указан тип данных и нет URI, вероятно, являются самыми распространенными. +

+ +

+Другой стандартной конфигурацией являются фильтры со схемой и типом данных. Например, +такой элемент {@code <data>}, +как приведенный далее, сообщает системе Android, что +компонент может получать видеоданные из сети для выполнения действия: +

+ +
+<intent-filter>
+    <data android:scheme="http" android:type="video/*" />
+    ...
+</intent-filter>
+ + + +

Сопоставление объектов Intent

+ +

Объекты Intent сопоставляются с фильтрами Intent не только для определения целевого +компонента, который требуется активировать, но также для выявления определенных сведений о наборе +компонентов, имеющихся на устройстве. Например, приложение главного экрана заполняет средство запуска приложений +путем поиска всех операций с фильтрами Intent, в которых указано действие +{@link android.content.Intent#ACTION_MAIN} и категория +{@link android.content.Intent#CATEGORY_LAUNCHER}.

+ +

Ваше приложение может использовать сопоставление объектов Intent таким же образом. +В {@link android.content.pm.PackageManager} имеется набор методов {@code query...()}, +которые возвращают все компоненты, способные принять определенный объект, а также +схожий набор методов {@code resolve...()}, которые определяют наиболее подходящий +компонент, способный реагировать на объект Intent. Например, метод +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} возвращает передаваемый как аргумент список всех операций, которые могут выполнить +объект Intent, а метод {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} возвращает такой же список служб. +Ни тот, ни другой метод не активирует компоненты; они просто перечисляют те из них, которые +могут откликнуться. Имеется схожий метод для приемников широковещательных сообщений ( +{@link android.content.pm.PackageManager#queryBroadcastReceivers +). +

+ + + + diff --git a/docs/html-intl/intl/ru/guide/components/loaders.jd b/docs/html-intl/intl/ru/guide/components/loaders.jd new file mode 100644 index 0000000000000000000000000000000000000000..eea72a2296ee510769d348791950b4116ca57aa9 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=Загрузчики +parent.title=Операции +parent.link=activities.html +@jd:body + + +

Загрузчики, которые появились в Android 3.0, упрощают асинхронную загрузку данных +в операцию или фрагмент. Загрузчики имеют следующие свойства:

+
    +
  • Они имеются для любых операций {@link android.app.Activity} и фрагментов {@link +android.app.Fragment}.
  • +
  • Они обеспечивают асинхронную загрузку данных.
  • +
  • Они отслеживают источник своих данных и выдают новые результаты при +изменении контента.
  • +
  • Они автоматически переподключаются к последнему курсору загрузчика при +воссоздании после изменения конфигурации. Таким образом, им не требуется повторно запрашивать свои +данные.
  • +
+ +

Сводная информация об API-интерфейсе загрузчика

+ +

Имеется несколько классов и интерфейсов, которые могут использовать +загрузчики в приложении. Они приведены в этой таблице:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Класс/интерфейсОписание
{@link android.app.LoaderManager}Абстрактный класс, связываемый с {@link android.app.Activity} или +{@link android.app.Fragment} для управления одним или несколькими интерфейсами {@link +android.content.Loader}. Это позволяет приложению управлять +длительно выполняющимися операциями вместе с жизненным циклом {@link android.app.Activity} +или {@link android.app.Fragment}; чаще всего этот класс используется с +{@link android.content.CursorLoader}, однако приложения могут писать +свои собственные загрузчики для работы с другими типами данных. +
+
+ Имеется только один класс {@link android.app.LoaderManager} на операцию или фрагмент. Однако у класса {@link android.app.LoaderManager} может быть +несколько загрузчиков.
{@link android.app.LoaderManager.LoaderCallbacks}Интерфейс обратного вызова, обеспечивающий взаимодействие клиента с {@link +android.app.LoaderManager}. Например, с помощью метода обратного вызова {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +создается новый загрузчик.
{@link android.content.Loader}Абстрактный класс, который выполняет асинхронную загрузку данных. Это +базовый класс для загрузчика. Обычно используется {@link +android.content.CursorLoader}, но можно реализовать и собственный подкласс. Когда +загрузчики активны, они должны отслеживать источник своих данных и выдавать новые +результаты при изменении контента.
{@link android.content.AsyncTaskLoader}Абстрактный загрузчик, который предоставляет {@link android.os.AsyncTask} для выполнения работы.
{@link android.content.CursorLoader}Подкласс класса {@link android.content.AsyncTaskLoader}, который запрашивает +{@link android.content.ContentResolver} и возвращает {@link +android.database.Cursor}. Этот класс реализует протокол {@link +android.content.Loader} стандартным способом для выполнения запросов к курсорам. +Он строится на {@link android.content.AsyncTaskLoader} для выполнения запроса к курсору +в фоновом потоке, чтобы не блокировать пользовательский интерфейс приложения. Использование +этого загрузчика — это лучший способ асинхронной загрузки данных из {@link +android.content.ContentProvider} вместо выполнения управляемого запроса через +платформу или API-интерфейсы операции.
+ +

Приведенные в этой таблице классы и интерфейсы являются наиболее важными компонентами, +с помощью которых в приложении реализуется загрузчик. При создании каждого загрузчика +не нужно использовать все эти компоненты, однако всегда следует указывать ссылку на {@link +android.app.LoaderManager} для инициализации загрузчика и использовать реализацию +класса {@link android.content.Loader}, например {@link +android.content.CursorLoader}. В следующих разделах рассказывается, как использовать эти +классы и интерфейсы в приложении.

+ +

Использование загрузчиков в приложении

+

В этом разделе описывается использование загрузчиков в приложении для Android. В +приложениях, использующих загрузчики, обычно имеются следующие элементы:

+
    +
  • {@link android.app.Activity} или {@link android.app.Fragment};
  • +
  • экземпляр {@link android.app.LoaderManager};
  • +
  • {@link android.content.CursorLoader} для загрузки данных, выдаваемых {@link +android.content.ContentProvider}. Вы также можете реализовать собственный подкласс +класса {@link android.content.Loader} или {@link android.content.AsyncTaskLoader} для +загрузки данных из другого источника;
  • +
  • реализация для {@link android.app.LoaderManager.LoaderCallbacks}. +Именно здесь создаются новые загрузчики и ведется управление ссылками на существующие +загрузчики;
  • +
  • способ отображения данных загрузчика, например {@link +android.widget.SimpleCursorAdapter};
  • +
  • источник данных, например {@link android.content.ContentProvider}, когда используется +{@link android.content.CursorLoader}.
  • +
+

Запуск загрузчика

+ +

{@link android.app.LoaderManager} управляет одним или несколькими экземплярами {@link +android.content.Loader} в {@link android.app.Activity} или +{@link android.app.Fragment}. Имеется только один {@link +android.app.LoaderManager} на каждую операцию или каждый фрагмент.

+ +

{@link android.content.Loader} обычно +инициализируется в методе {@link +android.app.Activity#onCreate onCreate()} операции или в методе фрагмента +{@link android.app.Fragment#onActivityCreated onActivityCreated()}. Делается +это следующим образом:

+ +
// Prepare the loader.  Either re-connect with an existing one,
+// or start a new one.
+getLoaderManager().initLoader(0, null, this);
+ +

Метод {@link android.app.LoaderManager#initLoader initLoader()} принимает +следующие параметры:

+
    +
  • уникальный идентификатор, обозначающий загрузчик. В данном примере идентификатором является 0;
  • +
  • необязательные аргументы, которые передаются загрузчику при +построении (в данном примере это null);
  • + +
  • реализация {@link android.app.LoaderManager.LoaderCallbacks}, которая +вызывает класс {@link android.app.LoaderManager} для выдачи событий загрузчика. В данном +примере локальный класс реализует интерфейс {@link +android.app.LoaderManager.LoaderCallbacks}, поэтому он передает ссылку +самому себе: {@code this}.
  • +
+

Вызов {@link android.app.LoaderManager#initLoader initLoader()} обеспечивает инициализацию +загрузчика. Возможен один из двух результатов:

+
    +
  • Если загрузчик, указанный с помощью идентификатора, уже существует, будет повторно использован загрузчик, созданный +последним.
  • +
  • Если загрузчик, указанный с помощью идентификатора, не существует, +{@link android.app.LoaderManager#initLoader initLoader()} вызывает метод +{@link android.app.LoaderManager.LoaderCallbacks} из {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. +Именно здесь реализуется код для создания экземпляра и возврата нового загрузчика. +Более подробные сведения см. в разделе onCreateLoader.
  • +
+

В любом случае данная реализация {@link android.app.LoaderManager.LoaderCallbacks} +связывается с загрузчиком и будет вызываться +при изменении состояния загрузчика. Если в момент этого вызова вызывающий компонент находится в +запущенном состоянии, это означает, что запрошенный загрузчик уже существует и сформировал свои +данные. В этом случае система сразу же вызовет {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} + (во время{@link android.app.LoaderManager#initLoader initLoader()}), +будьте готовы к этому. Более подробные сведения об этом обратном вызове см. в разделе +onLoadFinished.

+ +

Обратите внимание, что метод {@link android.app.LoaderManager#initLoader initLoader()} +возвращает создаваемый класс {@link android.content.Loader}, но записывать +ссылку на него не требуется. Класс {@link android.app.LoaderManager} управляет +жизненным циклом загрузчика автоматически. Класс {@link android.app.LoaderManager} +начинает загрузку и заканчивает ее при необходимости, а также поддерживает состояние загрузчика +и связанного с ним контента. А это подразумевает, что вы будете редко взаимодействовать с загрузчиками +напрямую (однако пример использования методов загрузчика для тонкой настройки его +поведения см. в образце кода LoaderThrottle). +Для вмешательства в процесс загрузки при возникновении определенных событий обычно используются методы {@link +android.app.LoaderManager.LoaderCallbacks} +. Более подробные сведения об этом см. в разделе Использование обратных вызовов LoaderManager.

+ +

Перезапуск загрузчика

+ +

При использовании метода {@link android.app.LoaderManager#initLoader initLoader()}, как +показано выше, он задействует существующий загрузчик с указанным идентификатором — в случае его наличия. +Если такого загрузчика нет, метод его создаст. Однако иногда старые данные нужно отбросить +и начать все заново.

+ +

Для удаления старых данных используется метод {@link +android.app.LoaderManager#restartLoader restartLoader()}. Например, эта +реализация метода{@link android.widget.SearchView.OnQueryTextListener} перезапускает +загрузчик, когда изменяется запрос пользователя. Загрузчик необходимо перезагрузить, + с тем чтобы он мог использовать измененный фильтр поиска для выполнения нового запроса:

+ +
+public boolean onQueryTextChanged(String newText) {
+    // Called when the action bar search text has changed.  Update
+    // the search filter, and restart the loader to do a new query
+    // with this filter.
+    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+    getLoaderManager().restartLoader(0, null, this);
+    return true;
+}
+ +

Использование обратных вызовов класса LoaderManager

+ +

{@link android.app.LoaderManager.LoaderCallbacks} представляет собой интерфейс обратного вызова, +который позволяет клиенту взаимодействовать с классом {@link android.app.LoaderManager}.

+

Ожидается, что загрузчики, в частности {@link android.content.CursorLoader}, будут сохранять +свои данные после их остановки. Это позволяет приложениям сохранять свои +данные в методах {@link android.app.Activity#onStop +onStop()} и {@link android.app.Activity#onStart onStart()} операции или фрагмента, с тем чтобы, +когда пользователь вернется в приложение, ему не пришлось ждать, пока данные +загрузятся заново. Методы {@link android.app.LoaderManager.LoaderCallbacks} используются, +чтобы узнать, когда требуется создать новый загрузчик, а также для того, чтобы указать приложению, когда +пришло время перестать использовать данные загрузчика.

+ +

Интерфейс {@link android.app.LoaderManager.LoaderCallbacks} использует следующие +методы:

+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — +создает экземпляр и возвращает новый класс {@link android.content.Loader} для данного идентификатора. +
+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +— вызывается, когда созданный ранее загрузчик завершил загрузку. +
+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} +— вызывается, когда состояние созданного ранее загрузчика сбрасывается, в результате чего его данные +теряются. +
  • +
+

Более подробно эти методы описаны в разделах ниже.

+ +

onCreateLoader

+ +

При попытке доступа к загрузчику (например, посредством метода {@link +android.app.LoaderManager#initLoader initLoader()}), он проверяет, существует ли +загрузчик, указанный с помощью идентификатора. Если он не существует, он вызывает метод {@link +android.app.LoaderManager.LoaderCallbacks} {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. Именно +здесь и создается новый загрузчик. Обычно это будет класс {@link +android.content.CursorLoader}, однако можно реализовать и собственный подкласс класса {@link +android.content.Loader}.

+ +

В этом примере метод обратного вызова {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +создает класс {@link android.content.CursorLoader}. Вы должны построить +класс {@link android.content.CursorLoader} с помощью его метода конструктора, для чего +требуется полный набор информации, которая нужна для выполнения запроса к {@link +android.content.ContentProvider}. В частности, требуется:

+
    +
  • uri — URI контента, который необходимо получить;
  • +
  • projection — список столбцов, которые будут возвращены. При передаче +null будут возвращены все столбцы, а это неэффективно;
  • +
  • selection — фильтр, объявляющий, какие строки возвращать, +отформатированный в виде предложения SQL WHERE (за исключением самого WHERE). При передаче +null будут возвращены все строки для данного URI;
  • +
  • selectionArgs — в выборку можно включить символы "?", которые будут +заменены значениями из selectionArgs в порядке следования в +выборке. Значения будут привязаны как строки;
  • +
  • sortOrder — порядок расположения строк, отформатированный в виде предложения SQL +ORDER BY (за исключением самого ORDER BY). При передаче null будет +использоваться стандартный порядок сортировки, поэтому, список, возможно, будет неотсортирован.
  • +
+

Например:

+
+ // If non-null, this is the current filter the user has provided.
+String mCurFilter;
+...
+public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+    // This is called when a new Loader needs to be created.  This
+    // sample only has one Loader, so we don't care about the ID.
+    // First, pick the base URI to use depending on whether we are
+    // currently filtering.
+    Uri baseUri;
+    if (mCurFilter != null) {
+        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+                  Uri.encode(mCurFilter));
+    } else {
+        baseUri = Contacts.CONTENT_URI;
+    }
+
+    // Now create and return a CursorLoader that will take care of
+    // creating a Cursor for the data being displayed.
+    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+            + Contacts.DISPLAY_NAME + " != '' ))";
+    return new CursorLoader(getActivity(), baseUri,
+            CONTACTS_SUMMARY_PROJECTION, select, null,
+            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+}
+

onLoadFinished

+ +

Этот метод вызывается, когда созданный ранее загрузчик завершил загрузку. +Этот метод гарантировано вызывается до высвобождения последних данных, +которые были предоставлены этому загрузчику. К этому моменту необходимо полностью перестать использовать +старые данные (поскольку они скоро будут заменены). Однако этого не следует делать +самостоятельно, поскольку данными владеет загрузчик и он позаботится об этом.

+ + +

Загрузчик высвободит данные, как только узнает, что приложение их больше не +использует. Например, если данными является курсор из {@link +android.content.CursorLoader}, не следует вызывать {@link +android.database.Cursor#close close()} самостоятельно. Если курсор +размещается в {@link android.widget.CursorAdapter}, следует использовать метод {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} с тем, чтобы +старый {@link android.database.Cursor} не закрылся. Например:

+ +
+// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in.  (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}
+ +

onLoaderReset

+ +

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

+

Данная реализация вызывает +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} +со значением null:

+ +
+// This is the Adapter being used to display the list's data.
+SimpleCursorAdapter mAdapter;
+...
+
+public void onLoaderReset(Loader<Cursor> loader) {
+    // This is called when the last Cursor provided to onLoadFinished()
+    // above is about to be closed.  We need to make sure we are no
+    // longer using it.
+    mAdapter.swapCursor(null);
+}
+ + +

Пример

+ +

В качестве примера далее приведена полная реализация фрагмента {@link +android.app.Fragment}, который отображает {@link android.widget.ListView} с +результатами запроса к поставщику такого контента, как контакты. Для управления запросом к поставщику используется класс {@link +android.content.CursorLoader}.

+ +

Чтобы приложение могло обращаться к контактам пользователя, как показано в этом примере, в его +манифесте должно присутствовать разрешение +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.

+ +
+public static class CursorLoaderListFragment extends ListFragment
+        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
+
+    // This is the Adapter being used to display the list's data.
+    SimpleCursorAdapter mAdapter;
+
+    // If non-null, this is the current filter the user has provided.
+    String mCurFilter;
+
+    @Override public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Give some text to display if there is no data.  In a real
+        // application this would come from a resource.
+        setEmptyText("No phone numbers");
+
+        // We have a menu item to show in action bar.
+        setHasOptionsMenu(true);
+
+        // Create an empty adapter we will use to display the loaded data.
+        mAdapter = new SimpleCursorAdapter(getActivity(),
+                android.R.layout.simple_list_item_2, null,
+                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
+                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
+        setListAdapter(mAdapter);
+
+        // Prepare the loader.  Either re-connect with an existing one,
+        // or start a new one.
+        getLoaderManager().initLoader(0, null, this);
+    }
+
+    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        // Place an action bar item for searching.
+        MenuItem item = menu.add("Search");
+        item.setIcon(android.R.drawable.ic_menu_search);
+        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        SearchView sv = new SearchView(getActivity());
+        sv.setOnQueryTextListener(this);
+        item.setActionView(sv);
+    }
+
+    public boolean onQueryTextChange(String newText) {
+        // Called when the action bar search text has changed.  Update
+        // the search filter, and restart the loader to do a new query
+        // with this filter.
+        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+        getLoaderManager().restartLoader(0, null, this);
+        return true;
+    }
+
+    @Override public boolean onQueryTextSubmit(String query) {
+        // Don't care about this.
+        return true;
+    }
+
+    @Override public void onListItemClick(ListView l, View v, int position, long id) {
+        // Insert desired behavior here.
+        Log.i("FragmentComplexList", "Item clicked: " + id);
+    }
+
+    // These are the Contacts rows that we will retrieve.
+    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
+        Contacts._ID,
+        Contacts.DISPLAY_NAME,
+        Contacts.CONTACT_STATUS,
+        Contacts.CONTACT_PRESENCE,
+        Contacts.PHOTO_ID,
+        Contacts.LOOKUP_KEY,
+    };
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        // This is called when a new Loader needs to be created.  This
+        // sample only has one Loader, so we don't care about the ID.
+        // First, pick the base URI to use depending on whether we are
+        // currently filtering.
+        Uri baseUri;
+        if (mCurFilter != null) {
+            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+                    Uri.encode(mCurFilter));
+        } else {
+            baseUri = Contacts.CONTENT_URI;
+        }
+
+        // Now create and return a CursorLoader that will take care of
+        // creating a Cursor for the data being displayed.
+        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+                + Contacts.DISPLAY_NAME + " != '' ))";
+        return new CursorLoader(getActivity(), baseUri,
+                CONTACTS_SUMMARY_PROJECTION, select, null,
+                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+    }
+
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        // Swap the new cursor in.  (The framework will take care of closing the
+        // old cursor once we return.)
+        mAdapter.swapCursor(data);
+    }
+
+    public void onLoaderReset(Loader<Cursor> loader) {
+        // This is called when the last Cursor provided to onLoadFinished()
+        // above is about to be closed.  We need to make sure we are no
+        // longer using it.
+        mAdapter.swapCursor(null);
+    }
+}
+

Другие примеры

+ +

В ApiDemos есть несколько различных примеров, которые +иллюстрируют использование загрузчиков:

+
    +
  • +LoaderCursor — полная версия +показанного выше фрагмента.
  • +
  • LoaderThrottle — пример того, как использовать регулирование +для сокращения числа запросов, выполняемых поставщиком контента при изменении его данных.
  • +
+ +

Сведения о загрузке и установке образцов кода SDK см. в статье Получение +образцов кода.

+ diff --git a/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd b/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd new file mode 100644 index 0000000000000000000000000000000000000000..fd298e0d3c4936ac0c2f0f14f8407b3c2e4b51c1 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=Процессы и потоки +page.tags=жизненный цикл,фон + +@jd:body + + + +

Когда компонент приложения запускается при отсутствии других работающих компонентов +, система Android запускает новый процесс Linux для приложения с одним потоком +выполнения. По умолчанию все компоненты одного приложения работают в одном процессе и потоке +(называется «главным потоком»). Если компонент приложения запускается при наличии процесса +для этого приложения (так как существует другой компонент из приложения), тогда компонент +запускается в этом процессе и использует тот же поток выполнения. Однако можно организовать выполнение +других компонентов приложения в отдельных процессах и создавать дополнительный +поток для любого процесса.

+ +

В этом документе обсуждается работа процессов и потоков в приложении Android.

+ + +

Процессы

+ +

По умолчанию все компоненты одного приложения работают в одном процессе, и большинство приложений +не должно менять это поведение. Однако, если необходимо контролировать, к какому процессу принадлежат определенный +компонент, можно сделать это в файле манифеста.

+ +

Запись манифеста для каждого типа элементов компонента —{@code +<activity>}, {@code +<service>}, {@code +<receiver>} и {@code +<provider>} —поддерживает атрибут {@code android:process}, позволяющий задавать +процесс, в котором следует выполнять этот компонент. Можно установить этот атрибут так, чтобы каждый компонент выполнялся +в собственном процессе, или так, чтобы только некоторые компоненты совместно использовали один процесс. Можно также настроить процесс +{@code android:process} так, чтобы компоненты разных приложений выполнялись в одном +процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с +одним сертификатом.

+ +

Элемент {@code +<application>} также поддерживает атрибут {@code android:process}, позволяющий задать +значение по умолчанию, которое применяется ко всем компонентам.

+ +

Android может остановить процесс в некоторой точке, когда не хватает памяти и она необходима другим +процессам, которые обслуживают пользователя в данный момент. Работа компонентов +приложения, работающих в этом процессе, последовательно останавливается. Процесс для этих компонентов запускается +повторно, когда для них появляется работа.

+ +

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

+ + +

Жизненный цикл процесса

+ +

Система Android пытается сохранять процесс приложения как можно дольше, но + в конечном счете вынуждена удалять старые процессы, чтобы восстановить память для новых или более важных процессов. Чтобы +определить, какие процессы сохранить, +а какие удалить, система помещает каждый процесс в «иерархию важности» на основе +компонентов, выполняющихся в процессе, и состояния этих компонентов. Процессы с самым низким уровнем +важности исключаются в первую очередь, затем исключаются процессы следующего уровня важности и т. д., насколько это необходимо +для восстановления ресурсов системы.

+ +

В иерархии важности предусмотрено пять уровней. В следующем списке представлены различные +типы процессов в порядке важности (первый процесс является наиболее важным и +удаляется в последнюю очередь):

+ +
    +
  1. Процесс переднего плана +

    Процесс, необходимый для текущей деятельности пользователя. Процесс + считается процессом переднего плана, если выполняется любое из следующих условий:

    + +
      +
    • Он содержит действие {@link android.app.Activity}, с которым взаимодействует пользователь (вызван метод {@link +android.app.Activity} +{@link android.app.Activity#onResume onResume()}).
    • + +
    • Он содержит службу {@link android.app.Service}, связанную с действием, с которым +взаимодействует пользователь.
    • + +
    • Он содержит службу {@link android.app.Service}, которая выполняется "на переднем плане", — службу, +которая называется {@link android.app.Service#startForeground startForeground()}. + +
    • Он содержит службу{@link android.app.Service}, которая выполняет один из обратных вызовов + жизненного цикла ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart +onStart()} или {@link android.app.Service#onDestroy onDestroy()}).
    • + +
    • Он содержит ресивер {@link android.content.BroadcastReceiver}, который выполняет метод {@link +android.content.BroadcastReceiver#onReceive onReceive()}.
    • +
    + +

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

  2. + +
  3. Видимые процессы +

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

    + +
      +
    • Он содержит действие {@link android.app.Activity}, которое не находится на переднем плане, но +видно пользователю (вызван метод {@link android.app.Activity#onPause onPause()}). +Например, это может происходить, если действие на переднем плане запустило диалоговое окно, которое позволяет видеть +предыдущее действие позади него.
    • + +
    • Он содержит службу {@link android.app.Service}, связанную с видимым +действием или действием переднего плана.
    • +
    + +

    Видимый процесс считается исключительно важным, его следует удалять +только в случае, если требуется сохранить работу всех процессов переднего плана.

    +
  4. + +
  5. Служебный процесс +

    Процесс, который выполняет службу, запущенную с помощью метода {@link +android.content.Context#startService startService()}, и не попадает ни в одну из двух +категорий более высокого уровня. Хотя служебные процессы не связаны непосредственно с тем, что видит пользователь, +они обычно выполняют важные для пользователя действия (например, воспроизводят музыку в фоновом режиме или +загружают данные в сеть), поэтому система сохраняет их выполнение, если памяти достаточно для +их работы наряду со всеми видимыми процессами и процессами переднего плана.

    +
  6. + +
  7. Фоновый процесс +

    Процесс, содержащий действия, которые не видны пользователю в настоящее время (вызван метод +{@link android.app.Activity#onStop onStop()} действия). Эти процессы не оказывают непосредственного +воздействия на работу пользователя, и система может удалить их в любой момент, чтобы освободить память для +процессов переднего плана, +видимых или служебных процессов. Обычно выполняется множество фоновых процессов, поэтому они хранятся в списке +LRU (недавно использованные), чтобы процессы, содержащие самые +недавние действия, которые видел пользователь, удалялись в последнюю очередь. Если для действия правильно реализованы методы жизненного цикла, +и действие сохраняет текущее состояние, удаление процесса этого действия не оказывает видимого воздействия на +работу пользователя, так как когда пользователь возвращается к этому действию, оно восстанавливает +все элементы своего видимого состояния. Информацию о сохранении и восстановлении состояния см. в документе Действия +.

    +
  8. + +
  9. Пустой процесс +

    Процесс, не содержащий никаких компонентов активного приложения. Единственная причина сохранять процесс +такого типа — это кэширование, которое улучшает время следующего запуска +компонента в этом процессе. Система часто удаляет эти процессы для равномерного распределения всех системных +ресурсов между кэшем процесса и кэшем базового ядра.

    +
  10. +
+ + +

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

+ +

Кроме того, уровень процесса может быть повышен, поскольку имеются другие процессы, зависимые от него. +Например, процесс, обслуживающий другой процесс, не может иметь уровень ниже уровня обслуживаемого +процесса. Например, если поставщик контента в процессе A обслуживает клиента в процессе B или +служебный процесс A связан с компонентом в процессе B, процесс A всегда считается не менее +важным, чем процесс B.

+ +

Так как процесс, выполняющий службу, оценивается выше процесса с фоновыми действиям, +действие, запускающее долговременную операцию, может запустить службу для этой операции, а не просто +создать рабочий поток, особенно в случае, если операция продлится дольше действия. +Например, действие, которое загружает изображение на веб-сайт, должно запустить службу для выполнения +загрузки, так что загрузка может продолжаться в фоновом режиме даже после выхода пользователя из действия. +Использование службы гарантирует, что операция будет иметь приоритет не ниже «служебного процесса», +независимо от того, что происходит с действием. По этой же причине ресиверы должны +использовать службы, а не просто ставить в поток операции, требующие много времени для выполнения.

+ + + + +

Потоки

+ +

При запуске приложения система создает поток выполнения для приложения, +который называется «главным». Этот поток очень важен, так как он отвечает за диспетчеризацию событий +на виджеты соответствующего интерфейса пользователя, включая события графического представления. Он также является потоком, в котором +приложение взаимодействует с компонентами из набора инструментов пользовательского интерфейса Android (компонентами из пакетов {@link +android.widget} и {@link android.view}). По существу, главный поток — это то, что иногда называют +потоком пользовательского интерфейса.

+ +

Система не создает отдельного потока для каждого экземпляра компонента. Все +компоненты, которые выполняются в одном процессе, создают экземпляры в потоке пользовательского интерфейса, и системные вызовы +каждого компонента отправляются из этого потока. Поэтому методы, которые отвечают на системные +обратные вызовы (такие как метод {@link android.view.View#onKeyDown onKeyDown()} для сообщения о действиях пользователя +или метод обратного вызова жизненного цикла), всегда выполняются в потоке пользовательского интерфейса процесса.

+ +

Например, когда пользователь нажимает кнопку на экране, поток пользовательского интерфейса вашего приложения отправляет +событие нажатия в виджет, который, в свою очередь, устанавливает кнопку в нажатое состояние и отправляет запрос на аннулирование +в очередь событий. Поток пользовательского интерфейса исключает запрос из очереди и уведомляет виджет, что он должен +отобразиться повторно.

+ +

Когда приложение выполняет интенсивную работу в ответ на действия пользователя, эта одиночная модель потока +может показывать плохую производительность, если приложение реализовано неправильно. То есть, если +все происходит в потоке пользовательского интерфейса, выполнение долговременных операций, таких как сетевой доступ или +запросы к базе данных, будет блокировать весь пользовательский интерфейс. Когда поток заблокирован, не могут обрабатываться никакие события, +включая события изменения отображения. С точки зрения пользователя +приложение выглядит зависшим. Хуже того, если поток пользовательского интерфейса заблокирован более нескольких секунд +(в настоящее время около 5 секунд), отображается печально известное диалоговое окно «приложение не +отвечает». После этого недовольный пользователь может выйти из вашего приложения +и удалить его.

+ +

Кроме того, набор инструментов пользовательского интерфейса Andoid не является потокобезопасным. Поэтому, вы не должны работать +с пользовательским интерфейсом из рабочего потока. Манипуляции с пользовательским интерфейсом необходимо выполнять из +потока пользовательского интерфейса. Таким образом, существует только два правила однопоточной модели Android:

+ +
    +
  1. Не блокируйте поток пользовательского интерфейса +
  2. Не обращайтесь к набору инструментов пользовательского интерфейса Android снаружи потока пользовательского интерфейса +
+ +

Рабочие потоки

+ +

Вследствие описанной выше однопоточной модели для динамичности пользовательского интерфейса ваших приложений +очень важно не блокировать поток пользовательского интерфейса. Если требуется выполнять операции, +занимающие некоторое время, обязательно выполняйте их в отдельных потоках (»фоновых» или +«рабочих» потоках).

+ +

Например, ниже приведен код контроля нажатий, который загружает изображение из отдельного +потока и отображает их в виджете {@link android.widget.ImageView}:

+ +
+public void onClick(View v) {
+    new Thread(new Runnable() {
+        public void run() {
+            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
+            mImageView.setImageBitmap(b);
+        }
+    }).start();
+}
+
+ +

На первый взгляд, он должен работать хорошо, так как он создает новый поток для обработки сетевой +операции. Однако, он нарушает второе правило однопоточной модели: не обращайтесь к набору инструментов пользовательского интерфейса +Android снаружи потока пользовательского интерфейса — этот пример изменяет {@link +android.widget.ImageView} из рабочего потока, а не из потока пользовательского интерфейса. Это может привести +к неопределенному и непредвиденному поведению, отследить которое будет трудно.

+ +

Для устранения этой проблемы Android предлагает несколько путей доступа к потоку пользовательского интерфейса из других +потоков. Ниже приведен список полезных методов:

+ +
    +
  • {@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}
  • +
  • {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}
  • +
  • {@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}
  • +
+ +

Например, можно исправить приведенный выше код с помощью метода {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)}:

+ +
+public void onClick(View v) {
+    new Thread(new Runnable() {
+        public void run() {
+            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
+            mImageView.post(new Runnable() {
+                public void run() {
+                    mImageView.setImageBitmap(bitmap);
+                }
+            });
+        }
+    }).start();
+}
+
+ +

Теперь реализация является потокобезопасной: сетевая операция выполняется из отдельного потока, +тогда как {@link android.widget.ImageView} работает из потока пользовательского интерфейса.

+ +

Однако по мере роста сложности, код такого типа может становиться запутанным и сложным +для поддержания. Чтобы обрабатывать более сложные взаимодействия с рабочим потоком, можно +использовать метод {@link android.os.Handler} в рабочем потоке для обработки сообщений, поступающих из потока +пользовательского интерфейса. Вероятно, самым лучшим решением является расширение класса {@link android.os.AsyncTask}, +которое упрощает выполнение заданий рабочего потока, которые должны взаимодействовать с пользовательским интерфейсом.

+ + +

Использование AsyncTask

+ +

Метод {@link android.os.AsyncTask} позволяет выполнять асинхронную работу в пользовательском +интерфейсе. Он выполняет операции блокирования в рабочем потоке и затем публикует результаты в потоке +пользовательского интерфейса без необходимости самостоятельно обрабатывать потоки и/или обработчики.

+ +

Для использования этого метода необходимо создать подкласс {@link android.os.AsyncTask} и реализовать метод обратного вызова {@link +android.os.AsyncTask#doInBackground doInBackground()}, который работает в пуле +фоновых потоков. Чтобы обновить пользовательский интерфейс, следует реализовать метод {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, который доставляет результат из {@link +android.os.AsyncTask#doInBackground doInBackground()} и работает в потоке пользовательского интерфейса, так что вы можете безопасно +обновлять пользовательский интерфейс. Задача выполняется через вызов метода {@link android.os.AsyncTask#execute execute()} +из потока пользовательского интерфейса.

+ +

Например, можно реализовать предыдущий пример с помощью метода {@link android.os.AsyncTask} следующим +образом:

+ +
+public void onClick(View v) {
+    new DownloadImageTask().execute("http://example.com/image.png");
+}
+
+private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
+    /** The system calls this to perform work in a worker thread and
+      * delivers it the parameters given to AsyncTask.execute() */
+    protected Bitmap doInBackground(String... urls) {
+        return loadImageFromNetwork(urls[0]);
+    }
+    
+    /** The system calls this to perform work in the UI thread and delivers
+      * the result from doInBackground() */
+    protected void onPostExecute(Bitmap result) {
+        mImageView.setImageBitmap(result);
+    }
+}
+
+ +

Теперь пользовательский интерфейс защищен и код стал проще, так как работа разделена на +часть, которая должна выполняться в рабочем потоке, и часть, которая должна выполняться в потоке пользовательского интерфейса.

+ +

Прочитайте статью {@link android.os.AsyncTask}, чтобы полностью понять +использование этого класса. Здесь приведен краткий обзор его работы:

+ +
    +
  • Можно указывать тип параметров, значения хода выполнения и конечное +значение задания с помощью универсальных компонентов
  • +
  • Метод {@link android.os.AsyncTask#doInBackground doInBackground()} выполняется автоматически +в рабочем потоке
  • +
  • Методы {@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link +android.os.AsyncTask#onPostExecute onPostExecute()} и {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} запускаются в потоке пользовательского интерфейса
  • +
  • Значение, возвращенное методом {@link android.os.AsyncTask#doInBackground doInBackground()}, отправляется в метод +{@link android.os.AsyncTask#onPostExecute onPostExecute()}
  • +
  • Можно вызвать {@link android.os.AsyncTask#publishProgress publishProgress()} в любой момент в {@link +android.os.AsyncTask#doInBackground doInBackground()} для выполнения {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} в потоке пользовательского интерфейса
  • +
  • Задание можно отменить в любой момент из любого потока
  • +
+ +

Предупреждение! Другая проблема, с которой вы можете столкнуться при использовании рабочего +потока, состоит в непредсказуемом перезапуске действия вследствие изменения конфигурации в режиме выполнения, +(например, когда пользователь изменяет ориентацию экрана), что может разрушить рабочий поток. Чтобы +увидеть, как можно сохранить задание во время одного из подобных перезапусков и как правильно отменить задание +при разрушении действия, изучите исходный код примера приложения Shelves.

+ + +

Потокобезопасные методы

+ +

В некоторых ситуациях реализованные методы могут вызываться из нескольких потоков и, следовательно, +должны быть написаны с сохранением потокобезопасности.

+ +

В первую очередь это относится к методам, которые можно вызывать удаленно, например, к методам в связанной службе. Когда вызов +метода реализуется в классе {@link android.os.IBinder}, происходящем из того же процесса, в котором выполняется +{@link android.os.IBinder IBinder}, метод выполняется в потоке вызывающего метода. +Однако, когда вызов происходит из другого процесса, метод выполняется в потоке, выбранном из пула +потоков, которые система поддерживает в том же процессе, что и {@link android.os.IBinder +IBinder} (он не выполняется в потоке пользовательского интерфейса процесса). Например, поскольку метод +{@link android.app.Service#onBind onBind()} службы будет вызываться из потока пользовательского интерфейса +процесса службы, методы, реализованные в объекте, который возвращает {@link android.app.Service#onBind +onBind()} (например, подкласс, который реализует методы RPC), будут вызываться из потоков +в пуле. Так как служба может иметь несколько клиентов, несколько потоков из пула могут одновременно использовать +один и тот же метод {@link android.os.IBinder IBinder}. Поэтому методы {@link android.os.IBinder +IBinder} должны быть реализованы с сохранением потокобезопасности.

+ +

Аналогичным образом поставщик контента может получать запросы данных, которые происходят из другого процесса. +Хотя классы {@link android.content.ContentResolver} и {@link android.content.ContentProvider} +скрывают подробности управления взаимодействием процессов, методы {@link +android.content.ContentProvider}, которые отвечают на эти запросы, —методы {@link +android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert +insert()}, {@link android.content.ContentProvider#delete delete()}, {@link +android.content.ContentProvider#update update()} и {@link android.content.ContentProvider#getType +getType()} —вызываются из пула потоков в процессе поставщика контента, а не в процессе +потока пользовательского интерфейса. Поскольку эти методы могут вызываться из любого числа потоков одновременно, +они также должны быть реализованы с сохранением потокобезопасности.

+ + +

Взаимодействие процессов

+ +

Система Android предлагает механизм взаимодействия процессов (IPC) с помощью удаленного вызова процедуры +(RPC), при котором метод вызывается действием или другим компонентом приложения, но выполняется +удаленно (в другом процессе) с возвратом всех результатов +вызывающему компоненту. Это влечет разложение вызова метода и его данных до уровня, понятного +операционной системе, передачу его из локального процесса и адресного пространства удаленному процессу и +адресному пространству, а затем повторную сборку и восстановление вызова. После этого возвращенные значения +передаются в обратном направлении. Система Android содержит все коды для выполнения этих механизмов IPC, +так что вы можете сосредоточиться на определении и реализации программного интерфейса RPC.

+ +

Для выполнения IPC приложение должно быть привязано к службе с помощью метода {@link +android.content.Context#bindService bindService()}. Дополнительные сведения представлены в разделе Службы руководства для разработчиков.

+ + + diff --git a/docs/html-intl/intl/ru/guide/components/recents.jd b/docs/html-intl/intl/ru/guide/components/recents.jd new file mode 100644 index 0000000000000000000000000000000000000000..37206318e2f3ffcc82d56f1ccc03f1b4661d73d0 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=Экран обзора +page.tags="recents","overview" + +@jd:body + + + +

Экран обзора (также используются названия: экран последних задач, список последних задач или последние приложения) +является элементом пользовательского интерфейса системного уровня, в котором содержится список последних +операций и задач. Пользователь +может перемещаться по списку и выбирать задачи для возобновления, или жестом удалять задачи +из списка. В версии Android 5.0 (уровень API 21) несколько экземпляров +одной операции, содержащие различные документы, могут отображаться в виде задач на экране обзора. Например, +Google Диск может иметь задачу для каждого из нескольких документов Google. На экране обзора каждый документ +отображается в виде задачи.

+ + +

Рисунок 1. Экран обзора, на котором показаны три документа Google Диск, + представленные в виде отдельных задач.

+ +

Обычно следует разрешить системе определить способ представления ваших задач и +операций на экране обзора. Вам не нужно менять это поведение. +Однако приложение может определять способ и время появления операции на экране обзора. С помощью класса +{@link android.app.ActivityManager.AppTask} можно управлять задачами, а с помощью флагов операции класса +{@link android.content.Intent} указывается, когда операция добавляется на экран обзора +или удаляется с него. Кроме того, атрибуты +<activity> позволяют устанавливать поведение в манифесте.

+ +

Добавление задач на экран обзора

+ +

Использование флагов класса {@link android.content.Intent} для добавления задачи обеспечивает лучшее управление +временем и способом открытия или повторного открытия документа на экране обзора. С помощью атрибутов +<activity> +можно выбрать открытие документа в новой задаче или повторное использование +существующей задачи для документа.

+ +

Использование флага Intent для добавления задачи

+ +

При создании нового документа для операции вы вызываете метод +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +класса {@link android.app.ActivityManager.AppTask}, передавая ему intent, +который запускает операцию. Для вставки логического разрыва, чтобы система обрабатывала вашу операцию как новую +задачу на экране обзора, передайте флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +в метод {@link android.content.Intent#addFlags(int) addFlags()} {@link android.content.Intent}, +который запускает операцию.

+ +

Примечание. Флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +замещает флаг {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, +который является устаревшим для систем Android 5.0 и выше (уровень API 21).

+ +

Если вы установили флаг {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} при создании +нового документа, система всегда создает новую задачу с целевой операцией в качестве корня. +Этот параметр позволяет открывать один документ в нескольких задачах. Следующий код показывает, +как это делает основная операция:

+ +

+DocumentCentricActivity.java

+
+public void createNewDocument(View view) {
+      final Intent newDocumentIntent = newDocumentIntent();
+      if (useMultipleTasks) {
+          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+      }
+      startActivity(newDocumentIntent);
+  }
+
+  private Intent newDocumentIntent() {
+      boolean useMultipleTasks = mCheckbox.isChecked();
+      final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
+      newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+      newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
+      return newDocumentIntent;
+  }
+
+  private static int incrementAndGet() {
+      Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
+      return mDocumentCounter++;
+  }
+}
+
+ +

Примечание. Операции, запущенные с флагом {@code FLAG_ACTIVITY_NEW_DOCUMENT}, +должны иметь значение атрибута {@code android:launchMode="standard"} (по умолчанию), установленное +в манифесте.

+ +

Когда основная операция запускает новую операцию, система ищет в существующих задачах одну, +значение intent которой соответствует имени компонента и данным Intent для операции. Если задача +не найдена или intent содержит флаг {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, +создается новая задача с операцией в качестве корня. Если задача найдена, система выводит + эту задачу на передний план и передает новое значение intent в {@link android.app.Activity#onNewIntent onNewIntent()}. +Новая операция получает intent и создает новый документ на экране обзора, как +в следующем примере:

+ +

+NewDocumentActivity.java

+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_new_document);
+    mDocumentCount = getIntent()
+            .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
+    mDocumentCounterTextView = (TextView) findViewById(
+            R.id.hello_new_document_text_view);
+    setDocumentCounterText(R.string.hello_new_document_counter);
+}
+
+@Override
+protected void onNewIntent(Intent intent) {
+    super.onNewIntent(intent);
+    /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
+    is reused to create a new document.
+     */
+    setDocumentCounterText(R.string.reusing_document_counter);
+}
+
+ + +

Использование атрибута Операция для добавления задачи

+ +

В манифесте операции можно также указать, что операция всегда запускается в новой задаче. Для этого используется атрибут +<activity> +, +{@code android:documentLaunchMode}. Этот атрибут имеет четыре значения, которые работают следующим образом, +когда пользователь открывает документ в приложении:

+ +
+
"{@code intoExisting}"
+
Операция повторно использует существующую задачу для документа. Это равносильно установке +флага {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} без установки +флага {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, как описано в разделе +Использование флага Intent для добавления задачи выше.
+ +
"{@code always}"
+
Операция создает новую задачу для документа, даже если документ уже открыт. Использование +этого значения равносильно установке обоих флагов {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +и {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.
+ +
"{@code none”}"
+
Операция не создает новой задачи для документа. Экран обзора обрабатывает +операцию как операцию по умолчанию: на экране обзора отображается одна задача для приложения, которая +возобновляется с любой последней операции, вызванной пользователем.
+ +
"{@code never}"
+
Операция не создает новой задачи для документа. Установка этого значения +переопределяет поведение флагов {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + и {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, если оба они установлены в +intent, и на экране обзора отображается одна задача для приложения, которая +возобновляется с любой последней операции, вызванной пользователем.
+
+ +

Примечание. Для значений кроме {@code none} и {@code never} +операция должна быть определена с атрибутом {@code launchMode="standard"}. Если этот атрибут не указан, +используется {@code documentLaunchMode="none"}.

+ +

Удаление задач

+ +

По умолчанию задача документа автоматически удаляется с экрана обзора после завершения +соответствующей операции. Можно переопределить это поведение с помощью класса {@link android.app.ActivityManager.AppTask}, +с флагом {@link android.content.Intent} или атрибутом +<activity>.

+ +

Можно в любой момент полностью убрать задачу с экрана обзора, установив для атрибута +<activity> + +{@code android:excludeFromRecents} значение {@code true}.

+ +

Можно установить максимальное число задач, которое ваше приложение может включить в экран обзора, установив для атрибута +<activity> + {@code android:maxRecents} + целое значение. Значение по умолчанию: 16. При достижении максимального количества задач самая +долго не используемая задача удаляется с экрана обзора. Максимальное значение {@code android:maxRecents} составляет +50 (25 для устройств с малым объемом памяти); Значения менее 1 не допускаются.

+ +

Использование класса AppTask для удаления задач

+ +

В операции, которая создает новую задачу на экране обзора, +можно указать время удаления задачи и завершения всех связанных с ней операций, вызвав +метод {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}.

+ +

+NewDocumentActivity.java

+
+public void onRemoveFromRecents(View view) {
+    // The document is no longer needed; remove its task.
+    finishAndRemoveTask();
+}
+
+ +

Примечание. Использование +метода {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}, +который переопределяет использование тега {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, +рассмотрен ниже.

+ +

Сохранение завершенных задач

+ +

Чтобы сохранить задачу на экране обзора, даже если ее операция завершена, передайте +флаг {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} в метод +{@link android.content.Intent#addFlags(int) addFlags()} объекта Intent, который запускает операцию.

+ +

+DocumentCentricActivity.java

+
+private Intent newDocumentIntent() {
+    final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
+    newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+      android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
+    newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
+    return newDocumentIntent;
+}
+
+ +

Для достижения того же результата установите для атрибута +<activity> + +{@code android:autoRemoveFromRecents} значение {@code false}. Значение по умолчанию {@code true} +для операций документа и {@code false} для обычных операций. Использование этого атрибута переопределяет +флаг {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, описанный выше.

+ + + + + + + diff --git a/docs/html-intl/intl/ru/guide/components/services.jd b/docs/html-intl/intl/ru/guide/components/services.jd new file mode 100644 index 0000000000000000000000000000000000000000..62a6a7ef9d15a8460cce5afbaf5fbb9b36f7a83c --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=Службы +@jd:body + + + + +

{@link android.app.Service} является компонентом приложения, который может выполнять +длительные операции в фоновом режиме и не содержит пользовательского интерфейса. Другой компонент +приложения может запустить службу, которая продолжит работу в фоновом режиме даже в том случае, когда пользователь +перейдет в другое приложение. Кроме того, компонент может привязаться к службе для +взаимодействия с ней и даже выполнять межпроцессное взаимодействие (IPC). Например, служба может +обрабатывать сетевые транзакции, воспроизводить музыку, выполнять ввод-вывод файла или взаимодействовать с поставщиком контента, и все +это в фоновом режиме.

+ +

Фактически служба может принимать две формы:

+ +
+
Запущенная
+
Служба является «запущенной», когда компонент приложения (например, операция) запускает ее +вызовом {@link android.content.Context#startService startService()}. После запуска служба +может работать в фоновом режиме в течение неограниченного времени, даже если уничтожен компонент, который ее запустил. Обычно +запущенная служба выполняет одну операцию и не возвращает результатов вызывающему компоненту. +Например, она может загружать или выгружать файл по сети. Когда операция выполнена, +служба должна остановиться самостоятельно.
+
Привязанная
+
Служба является «привязанной», когда компонент приложения привязывается к ней вызовом {@link +android.content.Context#bindService bindService()}. Привязанная служба предлагает интерфейс клиент-сервер, +который позволяет компонентам взаимодействовать со службой, отправлять запросы, получать результаты и даже +делать это между разными процессами посредством межпроцессного взаимодействия (IPC). Привязанная служба работает только пока +к ней привязан другой компонент приложения. К службе могут быть привязаны несколько компонентов одновременно, +но когда все они отменяют привязку, служба уничтожается.
+
+ +

Хотя в этой документации эти два типа служб обсуждаются отдельно, служба может +работать обеими способами — она может быть запущенной (и работать в течение неограниченного времени) и допускать привязку. +Это зависит от реализации пары методов обратного вызова: {@link +android.app.Service#onStartCommand onStartCommand()} позволяет компонентам запускать службу, а {@link +android.app.Service#onBind onBind()} позволяет выполнять привязку.

+ +

Независимо от состояния приложения (запущенное, привязанное или и оба сразу) любой компонент приложения +может использовать службу (даже из отдельного приложения) подобно тому, как любой компонент может использовать +операцию — запустив ее с помощью {@link android.content.Intent}. Однако вы можете объявить закрытую +службу в файле манифеста и заблокировать доступ к ней из других приложений. Более подробно +это обсуждается в разделе Объявление службы +в манифесте.

+ +

Внимание! Служба работает +в основном потоке ведущего процесса — служба не создает своего потока +и не выполняется в отдельном процессе (если вы не указали иное). Это означает, +что если ваша служба собирается выполнять любую работу с высокой нагрузкой ЦП или блокирующие операции (например, воспроизведение MP3 +или сетевые операции), вы должны создать в службе новый поток для выполнения этой работы. Используя +отдельный поток, вы снижаете риск возникновения ошибок «Приложение не отвечает», и +основной поток приложения может отрабатывать взаимодействие пользователя с вашими операциями.

+ + +

Основы

+ + + +

Чтобы создать службу, необходимо создать подкласс класса {@link android.app.Service} (или одного +из существующих его подклассов). В вашей реализации необходимо переопределить некоторые методы обратного вызова, +которые обрабатывают ключевые моменты жизненного цикла службы и при необходимости +предоставляют механизм привязывания компонентов. Наиболее важные методы обратного вызова, которые необходимо переопределить:

+ +
+
{@link android.app.Service#onStartCommand onStartCommand()}
+
Система вызывает этот метод, когда другой компонент, например, операция, +запрашивает запуск этой службы, вызывая {@link android.content.Context#startService +startService()}. После выполнения этого метода служба запускается и может в течение неограниченного времени +работать в фоновом режиме. Если вы реализуете такой метод, вы обязаны остановить службу +посредством вызова {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}. (Если требуется только обеспечить привязку, +реализовывать этот метод не обязательно).
+
{@link android.app.Service#onBind onBind()}
+
Система вызывает этот метод, когда другой компонент хочет выполнить привязку +к службе (например, для выполнения удаленного вызова процедуры) путем вызова {@link android.content.Context#bindService +bindService()}. В вашей реализации этого метода вы должны обеспечить интерфейс, который клиенты +используют для взаимодействия со службой, возвращая {@link android.os.IBinder}. Всегда необходимо реализовывать +этот метод, но если вы не хотите разрешать привязку, необходимо возвращать значение null.
+
{@link android.app.Service#onCreate()}
+
Система вызывает этот метод при первом создании службы для выполнения однократных +процедур настройки (перед вызовом {@link android.app.Service#onStartCommand onStartCommand()} или +{@link android.app.Service#onBind onBind()}). Если служба уже запущена, этот метод не +вызывается.
+
{@link android.app.Service#onDestroy()}
+
Система вызывает этот метод, когда служба более не используется и выполняется ее уничтожение. +Ваша служба должна реализовать это для очистки ресурсов, таких как потоки, зарегистрированные +приемники, ресиверы и т. д. Это последний вызов, который получает служба.
+
+ +

Если компонент запускает службу посредством вызова {@link +android.content.Context#startService startService()} (что приводит к вызову {@link +android.app.Service#onStartCommand onStartCommand()}), то служба +продолжает работу, пока она не остановится самостоятельно с помощью {@link android.app.Service#stopSelf()} или другой +компонент не остановит ее посредством вызова {@link android.content.Context#stopService stopService()}.

+ +

Если компонент вызывает +{@link android.content.Context#bindService bindService()} для создания службы (и {@link +android.app.Service#onStartCommand onStartCommand()} не вызывается), то служба работает, пока +к ней привязан компонент. Как только выполняется отмена привязки службы ко всем клиентам, +система уничтожает службу.

+ +

Система Android будет принудительно останавливать службу только в том случае, когда не хватает памяти, и необходимо восстановить системные +для операции, которая отображается на переднем плане. Если служба привязана к операции, которая отображается на переднем плане, +менее вероятно, что она будет уничтожена, и если служба объявлена для выполнения в фоновом режиме (как обсуждалось выше), она почти никогда не будет уничтожаться. +В противном случае, если служба была запущена и является длительной, система со временем будет опускать ее положение в списке +фоновых задач, и служба станет очень чувствительной к +уничтожению — если ваша служба запущена, вы должны предусмотреть изящную обработку ее перезапуска +системой. Если система уничтожает вашу службу, она перезапускает ее, как только снова появляется +доступ к ресурсам (хотя это также зависит от значения, возвращаемого методом {@link +android.app.Service#onStartCommand onStartCommand()}, как обсуждается ниже). Дополнительная информация +о ситуациях, в которых система может уничтожить службу приведена в документе Процессы и потоки +.

+ +

В следующих разделах описаны способы создания служб каждого типа и использования +их из других компонентов приложения.

+ + + +

Объявление службы в манифесте

+ +

Все службы, как и операции (и другие компоненты), должны быть объявлены в файле +манифеста вашего приложения.

+ +

Чтобы объявить службу, добавьте элемент {@code <service>} +, в качестве дочернегоэлемента {@code <application>} +. Например:

+ +
+<manifest ... >
+  ...
+  <application ... >
+      <service android:name=".ExampleService" />
+      ...
+  </application>
+</manifest>
+
+ +

Дополнительные сведения об объявлении службы +в манифесте см. в справке по элементу {@code <service>}.

+ +

Имеются другие атрибуты, которые можно включить в элемент {@code <service>} для +задания свойств, например, необходимых для запуска разрешений, и процесса, +в котором должна выполняться служба. Атрибут {@code android:name} +является единственным обязательным атрибутом — он указывает имя класса для службы. После +публикации вашего приложения вам не следует менять это имя, поскольку это может разрушить +код из-за зависимости от явных намерений, используемых, чтобы запустить или привязать службу (ознакомьтесь с публикацией Вещи, которые +нельзя менять в блоге разработчиков). + +

Для обеспечения безопасности приложения всегда используйте явное намерение при запуске +или привязке {@link android.app.Service} и не объявляйте фильтров намерений для службы. Если вам +важно допустить некоторую неопределенность в отношении того, какая служба запускается, вы можете +предоставить фильтры намерений для ваших служб и исключить имя компонента из {@link +android.content.Intent}, но затем вы должны установить пакет для намерения с помощью {@link +android.content.Intent#setPackage setPackage()}, который обеспечивает достаточное устранение неоднозначности +для целевой службы.

+ +

Дополнительно можно обеспечить доступность вашей службы только для вашего приложения, +включив атрибут {@code android:exported} +и установив для него значение {@code "false"}. Это не позволяет другим приложениям запускать +вашу службу даже при использовании явного намерения.

+ + + + +

Создание запущенной службы

+ +

Запущенная служба — это служба, которую запускает другой компонент вызовом {@link +android.content.Context#startService startService()}, что приводит к вызову +метода {@link android.app.Service#onStartCommand onStartCommand()} службы.

+ +

При запуске служба обладает сроком жизни, не зависящим от запустившего ее компонента, +и может работать в фоновом режиме в течение неограниченного времени, +даже если уничтожен компонент, который ее запустил. Поэтому после выполнения своей работы служба должна остановиться самостоятельно +посредством вызова метода {@link android.app.Service#stopSelf stopSelf()}, либо ее может остановить другой компонент +посредством вызова метода{@link android.content.Context#stopService stopService()}.

+ +

Компонент приложения, например, операция, может запустить службу, вызвав метод {@link +android.content.Context#startService startService()} и передав объект {@link android.content.Intent}, +который указывает службу и любые данные, которые служба должна использовать. Служба получает +этот объект {@link android.content.Intent} в методе {@link android.app.Service#onStartCommand +onStartCommand()}.

+ +

Предположим, что операции требуется сохранить некоторые данные в сетевой базе данных. Операция может +запустить службу и предоставить ей данные для сохранения, передав намерение в метод {@link +android.content.Context#startService startService()}. Служба получает намерение в методе {@link +android.app.Service#onStartCommand onStartCommand()}, подключается к Интернету и выполняет транзакцию +с базой данных. Когда транзакция выполнена, служба останавливается +самостоятельно и уничтожается.

+ +

Внимание! По умолчанию службы работают в том же процессе, что и приложение, +в котором они объявлены, а также в основном потоке этого приложения. Поэтому, если ваша служба +выполняет интенсивные или блокирующие операции, в то время как пользователь взаимодействует с операцией из того же +приложения, служба будет замедлять выполнение операции. Чтобы избежать негативного воздействия на скорость работы +приложения, вы должны запустить новый поток внутри службы.

+ +

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

+
+
{@link android.app.Service}
+
Это базовый класс для всех служб. Когда вы наследуете этот класс, важно +создать новый поток, в котором будет выполняться вся работа службы, поскольку по умолчанию служба использует основной поток вашего +приложения, что может замедлить любую операцию, которую +выполняет ваше приложение.
+
{@link android.app.IntentService}
+
Это подкласс класса {@link android.app.Service}, который использует рабочий поток для обработки всех +запросов запуска поочередно. Это оптимальный вариант, если вам не требуется, чтобы ваша служба +обрабатывала несколько запросов одновременно. Достаточно реализовать метод {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, который получает намерение для каждого +запроса запуска, позволяя выполнять фоновую работу.
+
+ +

В следующих разделах описано, как реализовать службу с помощью любого их этих +классов.

+ + +

Наследование класса IntentService

+ +

Так как большинству запущенных приложений не требуется обрабатывать несколько запросов одновременно, +(что может быть действительно опасным сценарием), вероятно будет лучше, если вы +реализуете свою службу с помощью класса {@link android.app.IntentService}.

+ +

Класс {@link android.app.IntentService} делает следующее:

+ +
    +
  • Создает рабочий поток по умолчанию, который выполняет все намерения, доставленные в метод {@link +android.app.Service#onStartCommand onStartCommand()}, отдельно от основного потока +вашего приложения.
  • +
  • Создает рабочую очередь, которая передает намерения по одному в вашу реализацию метода {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, поэтому вы не должны беспокоиться +относительно многопоточности.
  • +
  • Останавливает службу после обработки всех запросов запуска, поэтому вам никогда не требуется вызывать +{@link android.app.Service#stopSelf}.
  • +
  • Предоставляет реализацию метода {@link android.app.IntentService#onBind onBind()} по умолчанию, которая +возвращает null.
  • +
  • Предоставляет реализацию метода {@link android.app.IntentService#onStartCommand +onStartCommand()} по умолчанию, которая отправляет намерение в рабочую очередь и затем в вашу реализацию {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.
  • +
+ +

Все это означает, что вам достаточно реализовать метод {@link +android.app.IntentService#onHandleIntent onHandleIntent()} для выполнения работы, предоставленной +клиентом. (Хотя, кроме того, вы должны предоставить маленький конструктор для службы).

+ +

Здесь приведен пример реализации класса {@link android.app.IntentService}:

+ +
+public class HelloIntentService extends IntentService {
+
+  /**
+   * A constructor is required, and must call the super {@link android.app.IntentService#IntentService}
+   * constructor with a name for the worker thread.
+   */
+  public HelloIntentService() {
+      super("HelloIntentService");
+  }
+
+  /**
+   * The IntentService calls this method from the default worker thread with
+   * the intent that started the service. When this method returns, IntentService
+   * stops the service, as appropriate.
+   */
+  @Override
+  protected void onHandleIntent(Intent intent) {
+      // Normally we would do some work here, like download a file.
+      // For our sample, we just sleep for 5 seconds.
+      long endTime = System.currentTimeMillis() + 5*1000;
+      while (System.currentTimeMillis() < endTime) {
+          synchronized (this) {
+              try {
+                  wait(endTime - System.currentTimeMillis());
+              } catch (Exception e) {
+              }
+          }
+      }
+  }
+}
+
+ +

Это все, что нужно: конструктор и реализация класса {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.

+ +

Если вы решили переопределить также и другие методы обратного вызова, такие как {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()} или {@link +android.app.IntentService#onDestroy onDestroy()}, обязательно вызовите реализацию суперкласса, +чтобы класс {@link android.app.IntentService} мог правильно обрабатывать жизненный цикл рабочего потока.

+ +

Например, метод {@link android.app.IntentService#onStartCommand onStartCommand()} должен возвращать +реализацию по умолчанию (которая доставляет намерение в {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):

+ +
+@Override
+public int onStartCommand(Intent intent, int flags, int startId) {
+    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
+    return super.onStartCommand(intent,flags,startId);
+}
+
+ +

Помимо {@link android.app.IntentService#onHandleIntent onHandleIntent()}, единственный метод, +из которого вам не требуется вызывать суперкласс, это метод {@link android.app.IntentService#onBind +onBind()} (но его нужно реализовывать только в случае, если ваша служба допускает привязку).

+ +

В следующем разделе вы увидите, как реализовывается служба такого же типа при наследовании +базового класса {@link android.app.Service}, которая содержит намного больше кода, но которая может +подойти, если вам требуется обрабатывать одновременные запросы запуска.

+ + +

Наследование класса Service

+ +

Как вы видели в предыдущем разделе, использование класса {@link android.app.IntentService} значительно упрощает +реализацию запущенной службы. Однако, если необходимо, чтобы ваша служба +поддерживала многопоточность (вместо обработки запросов запуска через рабочую очередь), можно +наследовать класс {@link android.app.Service} для обработки каждого намерения.

+ +

В качестве примера приведена следующая реализация класса {@link +android.app.Service}, которая выполняет ту же работу, как и пример выше, использующий класс {@link +android.app.IntentService}. То есть для каждого запроса запуска он использует рабочий поток для выполнения +задания и обрабатывает запросы по одному.

+ +
+public class HelloService extends Service {
+  private Looper mServiceLooper;
+  private ServiceHandler mServiceHandler;
+
+  // Handler that receives messages from the thread
+  private final class ServiceHandler extends Handler {
+      public ServiceHandler(Looper looper) {
+          super(looper);
+      }
+      @Override
+      public void handleMessage(Message msg) {
+          // Normally we would do some work here, like download a file.
+          // For our sample, we just sleep for 5 seconds.
+          long endTime = System.currentTimeMillis() + 5*1000;
+          while (System.currentTimeMillis() < endTime) {
+              synchronized (this) {
+                  try {
+                      wait(endTime - System.currentTimeMillis());
+                  } catch (Exception e) {
+                  }
+              }
+          }
+          // Stop the service using the startId, so that we don't stop
+          // the service in the middle of handling another job
+          stopSelf(msg.arg1);
+      }
+  }
+
+  @Override
+  public void onCreate() {
+    // Start up the thread running the service.  Note that we create a
+    // separate thread because the service normally runs in the process's
+    // main thread, which we don't want to block.  We also make it
+    // background priority so CPU-intensive work will not disrupt our UI.
+    HandlerThread thread = new HandlerThread("ServiceStartArguments",
+            Process.THREAD_PRIORITY_BACKGROUND);
+    thread.start();
+
+    // Get the HandlerThread's Looper and use it for our Handler
+    mServiceLooper = thread.getLooper();
+    mServiceHandler = new ServiceHandler(mServiceLooper);
+  }
+
+  @Override
+  public int onStartCommand(Intent intent, int flags, int startId) {
+      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
+
+      // For each start request, send a message to start a job and deliver the
+      // start ID so we know which request we're stopping when we finish the job
+      Message msg = mServiceHandler.obtainMessage();
+      msg.arg1 = startId;
+      mServiceHandler.sendMessage(msg);
+
+      // If we get killed, after returning from here, restart
+      return START_STICKY;
+  }
+
+  @Override
+  public IBinder onBind(Intent intent) {
+      // We don't provide binding, so return null
+      return null;
+  }
+
+  @Override
+  public void onDestroy() {
+    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
+  }
+}
+
+ +

Как можно видеть, этот код значительно длиннее, чем код с использованием класса {@link android.app.IntentService}.

+ +

Однако, так как вы обрабатываете каждый вызов {@link android.app.Service#onStartCommand +onStartCommand()} самостоятельно, вы можете выполнять несколько запросов одновременно. Данный код +выполняет не совсем эту работу, но при необходимости вы можете создавать новые потоки для каждого +запроса и сразу запускать их (а не ожидать завершения предыдущего запроса).

+ +

Обратите внимание, что метод {@link android.app.Service#onStartCommand onStartCommand()} должен +возвращать целое число. Это целое число описывает, как система должна продолжать выполнение службы в случае, +когда система уничтожила ее (как описано выше, реализация по умолчанию для класса {@link +android.app.IntentService} обрабатывает эту ситуацию, хотя вы изменить ход реализации). Значение, +возвращаемое методом {@link android.app.Service#onStartCommand onStartCommand()}, должно быть одной из следующих +констант:

+ +
+
{@link android.app.Service#START_NOT_STICKY}
+
Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, не нужно повторно создавать службу, если нет ожидающих +доставки намерений. Это самый безопасный вариант, позволяющий избежать запуска вашей службы, когда это не нужно +и когда ваше приложение может просто перезапустить любые незавершенные задания.
+
{@link android.app.Service#START_STICKY}
+
Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, повторно создайте службу и вызовите {@link +android.app.Service#onStartCommand onStartCommand()}, но не передавайте последнее намерение повторно. +Вместо этого система вызывает метод {@link android.app.Service#onStartCommand onStartCommand()} с намерением, +которое имеет значение null, если нет ожидающих намерений для запуска службы. Если ожидающие намерения есть, +они доставляются. Это подходит для мультимедийных проигрывателей (или подобных служб), которые не +выполняют команды, а работают независимо и ожидают задание.
+
{@link android.app.Service#START_REDELIVER_INTENT}
+
Если система уничтожает службу после возвращения из {@link android.app.Service#onStartCommand +onStartCommand()}, повторно создайте службу и вызовите {@link +android.app.Service#onStartCommand onStartCommand()} с последним намерением, которое было доставлено +в службу. Все ожидающие намерения доставляются по очереди. Это подходит для служб, +активно выполняющих задание, которое должно быть возобновлено немедленно, например, для загрузок файла.
+
+

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

+ + + +

Запуск службы

+ +

Можно запустить службу из операции или другого компонента приложения, передав объект +{@link android.content.Intent} (указывающий службу, которую требуется запустить) в {@link +android.content.Context#startService startService()}. Система Android вызывает метод {@link +android.app.Service#onStartCommand onStartCommand()} службы и передает ей {@link +android.content.Intent}. (Ни в коем случае не следует вызывать метод {@link android.app.Service#onStartCommand +onStartCommand()} напрямую).

+ +

Например, операция может запустить службу из примера в предыдущем разделе ({@code +HelloSevice}), используя явное намерение с помощью {@link android.content.Context#startService +startService()}:

+ +
+Intent intent = new Intent(this, HelloService.class);
+startService(intent);
+
+ +

Метод {@link android.content.Context#startService startService()} возвращается немедленно, и система +Android вызывает метод службы {@link android.app.Service#onStartCommand +onStartCommand()}. Если служба еще не выполняется, система сначала вызывает {@link +android.app.Service#onCreate onCreate()}, а затем {@link android.app.Service#onStartCommand +onStartCommand()}.

+ +

Если служба также не представляет привязку, намерение, доставляемое с помощью {@link +android.content.Context#startService startService()}, является единственным режимом связи между +компонентом приложения и службой. Однако, если вы хотите, чтобы служба оправляла результат обратно, +клиент, который запускает службу, может создать объект {@link android.app.PendingIntent} для сообщения +(с помощью {@link android.app.PendingIntent#getBroadcast getBroadcast()}) и доставить его в службу +в объекте {@link android.content.Intent}, который запускает службу. Затем служба может использовать +сообщение для доставки результата.

+ +

Несколько запросов запуска службы приводят к нескольким соответствующим вызовам метода +{@link android.app.Service#onStartCommand onStartCommand()} службы. Однако для ее остановки достаточно только одного запроса на остановку +службы (с помощью {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}).

+ + +

Остановка службы

+ +

Запущенная служба должна управлять своим жизненным циклом. То есть, система не останавливает и не +уничтожает службу, если не требуется восстановить память системы, и служба +продолжает работу после возвращения из метода {@link android.app.Service#onStartCommand onStartCommand()}. Поэтому +служба должна останавливаться самостоятельно посредством вызова метода {@link android.app.Service#stopSelf stopSelf()}, либо другой +компонент может остановить ее посредством вызова метода {@link android.content.Context#stopService stopService()}.

+ +

Получив запрос на остановку посредством {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}, система как можно скорее уничтожает службу +.

+ +

Однако, если служба обрабатывает несколько запросов {@link +android.app.Service#onStartCommand onStartCommand()} одновременно, вы не должны останавливать службу +после завершения обработки запроса запуска, поскольку вы, вероятно, уже получили новый +запрос запуска (остановка в конце первого запроса привела бы к прерыванию второго). Чтобы избежать +этой проблемы, вы можете использовать метод {@link android.app.Service#stopSelf(int)}, гарантирующий, что ваш запрос на +остановку службы всегда основан на самом последнем запросе запуска. То есть, когда вы вызываете {@link +android.app.Service#stopSelf(int)}, вы передаете идентификатор запроса запуска (идентификатор startId, +доставленный в {@link android.app.Service#onStartCommand onStartCommand()}), которому соответствует ваш +запрос остановки. Тогда, если служба получит новый запрос запуска до того, как вы сможете вызвать {@link +android.app.Service#stopSelf(int)}, идентификатор не будет совпадать и служба не будет остановлена.

+ +

Внимание! Ваше приложение обязательно должно останавливать свои службы по окончании работы, +чтобы избежать расходования ресурсов системы и потребления энергии аккумулятора. При необходимости +другие компоненты могут остановить службу посредством вызова метода {@link +android.content.Context#stopService stopService()}. Даже если вы можете выполнять привязку службы, +следует всегда останавливать службу самостоятельно, если она когда-либо получила вызов {@link +android.app.Service#onStartCommand onStartCommand()}.

+ +

Дополнительные сведения о жизненном цикле службы представлены в разделе Управление жизненным циклом службы ниже.

+ + + +

Создание привязанной службы

+ +

Привязанная служба — это служба, которая допускает привязку к ней компонентов приложения посредством вызова {@link +android.content.Context#bindService bindService()} для создания долговременного соединения +(и обычно не позволяет компонентам запускать ее посредством вызова {@link +android.content.Context#startService startService()}).

+ +

Вы должны создать привязанную службу, когда вы хотите взаимодействовать со службой из операций +и других компонентов вашего приложения или показывать некоторые функции вашего приложения +другим приложениям посредством межпроцессного взаимодействия (IPC).

+ +

Чтобы создать привязанную службу, необходимо реализовать метод обратного вызова {@link +android.app.Service#onBind onBind()} для возвращения объекта {@link android.os.IBinder}, +который определяет интерфейс взаимодействия со службой. После этого другие компоненты приложения могут вызвать +метод {@link android.content.Context#bindService bindService()} для извлечения интерфейса и +начать вызывать методы службы. Служба существует только для обслуживания привязанного к ней компонента приложения, +поэтому, когда нет компонентов, привязанных к службе, система уничтожает ее +(вам не требуется останавливать привязанную службу, как это требуется для службы, запущенной +посредством {@link android.app.Service#onStartCommand onStartCommand()}).

+ +

Чтобы создать привязанную службу, необходимо в первую очередь определить интерфейс, взаимодействия +клиента со службой. Этот интерфейс между службой +и клиентом должен быть реализацией объекта {@link android.os.IBinder}, которую ваша служба должна +возвращать из метода обратного вызова {@link android.app.Service#onBind +onBind()}. После того, как клиент получает объект {@link android.os.IBinder}, он может начать +взаимодействие со службой посредством этого интерфейса.

+ +

Одновременно к службе могут быть привязаны несколько клиентов. Когда клиент заканчивает взаимодействие +со службой, он вызывает {@link android.content.Context#unbindService unbindService()} для отмены привязки. Как только +не остается ни одного клиента, привязанного к службе, система уничтожает службу.

+ +

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

+ + + +

Отправка уведомлений пользователю

+ +

После запуска служба может уведомлять пользователя о событиях, используя Всплывающие уведомления или Уведомления в строке состояния.

+ +

Всплывающее уведомление — это сообщение, кратковременно появляющееся на поверхности текущего окна, +тогда как уведомление в строке состояния — это значок в строке состояния с сообщением, +который пользователь может выбрать, чтобы выполнить действие (такое как запуск операции).

+ +

Обычно уведомление в строке состояния является самым удобным решением, когда завершается какая-то фоновая работа +(например, завершена +загрузка файла), и пользователь может действовать. Когда пользователь выбирает уведомление в +расширенном виде, уведомление может запустить операцию (например, для просмотра загруженного файла).

+ +

Дополнительную информацию см. в руководствах для разработчиков Всплывающие уведомления и +Уведомления в строке состояния.

+ + + +

Запуск службы на переднем плане

+ +

Служба переднего плана — это служба, о которой пользователь активно +осведомлен, и поэтому она не является кандидатом для удаления системой в случае нехватки памяти. Служба +переднего плана должна выводить уведомление в строку состояния, которая находится под заголовком +«Постоянные». Это означает, что уведомление не может быть удалено, пока служба +не будет остановлена или удалена с переднего плана.

+ +

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

+ +

Для запроса на выполнение вашей службы на переднем плане вызовите метод {@link +android.app.Service#startForeground startForeground()}. Этот метод имеет два параметра: целое число, +которое однозначно идентифицирует уведомление и объект {@link +android.app.Notification} для строки состояния. Например:

+ +
+Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
+        System.currentTimeMillis());
+Intent notificationIntent = new Intent(this, ExampleActivity.class);
+PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
+notification.setLatestEventInfo(this, getText(R.string.notification_title),
+        getText(R.string.notification_message), pendingIntent);
+startForeground(ONGOING_NOTIFICATION_ID, notification);
+
+ +

Внимание! Целочисленный идентификатор ID, который вы передаете в метод {@link +android.app.Service#startForeground startForeground()}, не должен быть равен 0.

+ + +

Чтобы удалить службу с переднего плана, вызовите {@link +android.app.Service#stopForeground stopForeground()}. Этот метод содержит логическое значение, указывающее, +следует ли также удалять уведомление в строке состояния. Этот метод не останавливает +службу. Однако, если вы останавливаете службу, работающую на переднем плане, +уведомление также удаляется.

+ +

Дополнительную информацию об уведомлениях см. в разделе Создание уведомлений +в строке состояния.

+ + + +

Управление жизненным циклом службы

+ +

Жизненный цикл службы намного проще, чем жизненный цикл операции. Однако, намного важнее +уделить пристальное внимание тому, как ваша служба создается и уничтожается, так как служба +может работать в фоновом режиме без ведома пользователя.

+ +

Жизненный цикл службы от создания до уничтожения может следовать двум +разным путям:

+ +
    +
  • Запущенная служба +

    Служба создается, когда другой компонент вызывает метод {@link +android.content.Context#startService startService()}. Затем служба работает в течение неограниченного времени и должна +остановиться самостоятельно посредством вызова метода {@link +android.app.Service#stopSelf() stopSelf()}. Другой компонент также может остановить службу +посредством вызова метода {@link android.content.Context#stopService +stopService()}. Когда служба останавливается, система уничтожает ее.

  • + +
  • Привязанная служба +

    Служба создается, когда другой компонент (клиент) вызывает метод {@link +android.content.Context#bindService bindService()}. Затем клиент взаимодействует со службой +через интерфейс {@link android.os.IBinder}. Клиент может закрыть соединение посредством вызова +метода {@link android.content.Context#unbindService unbindService()}. К одной службе могут быть привязано +несколько клиентов, и когда все они отменяют привязку, система уничтожает службу. (Служба +не должна останавливаться самостоятельно.)

  • +
+ +

Эти два способа необязательно работают независимо друг от друга. То есть вы можете привязать службу, которая уже была +запущена посредством метода {@link android.content.Context#startService startService()}. Например, фоновая +музыкальная служба может быть запущена посредством вызова метода {@link android.content.Context#startService +startService()} с объектом {@link android.content.Intent}, который идентифицирует музыку для воспроизведения. Позже, +например, когда пользователь хочет получить доступ к управлению проигрывателем или информацию о текущем произведении, +операция может установить привязку к службе посредством вызова метода {@link +android.content.Context#bindService bindService()}. В подобных случаях методы {@link +android.content.Context#stopService stopService()} и {@link android.app.Service#stopSelf +stopSelf()} фактически не останавливают службу, пока не будет отменена привязка всех клиентов.

+ + +

Реализация обратных вызовов жизненного цикла

+ +

Подобно операции, служба содержит методы обратного вызова жизненного цикла, которые можно реализовать для контроля +изменений состояния службы и выполнения работы в соответствующие моменты времени. Указанная ниже базовая +служба показывает каждый из методов жизненного цикла.

+ +
+public class ExampleService extends Service {
+    int mStartMode;       // indicates how to behave if the service is killed
+    IBinder mBinder;      // interface for clients that bind
+    boolean mAllowRebind; // indicates whether onRebind should be used
+
+    @Override
+    public void {@link android.app.Service#onCreate onCreate}() {
+        // The service is being created
+    }
+    @Override
+    public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) {
+        // The service is starting, due to a call to {@link android.content.Context#startService startService()}
+        return mStartMode;
+    }
+    @Override
+    public IBinder {@link android.app.Service#onBind onBind}(Intent intent) {
+        // A client is binding to the service with {@link android.content.Context#bindService bindService()}
+        return mBinder;
+    }
+    @Override
+    public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) {
+        // All clients have unbound with {@link android.content.Context#unbindService unbindService()}
+        return mAllowRebind;
+    }
+    @Override
+    public void {@link android.app.Service#onRebind onRebind}(Intent intent) {
+        // A client is binding to the service with {@link android.content.Context#bindService bindService()},
+        // after onUnbind() has already been called
+    }
+    @Override
+    public void {@link android.app.Service#onDestroy onDestroy}() {
+        // The service is no longer used and is being destroyed
+    }
+}
+
+ +

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

+ + +

Рисунок 2. Жизненный цикл службы. На схеме слева +показан жизненный цикл, когда служба создана посредством метода {@link android.content.Context#startService +startService()}, а на схеме справа показан жизненный цикл, когда служба создана +посредством метода {@link android.content.Context#bindService bindService()}.

+ +

С помощью реализации этих методов можно отслеживать два вложенных цикла в жизненном цикле службы:

+ +
    +
  • Весь жизненный цикл службы происходит между вызовом метода {@link +android.app.Service#onCreate onCreate()} и возвратом из метода {@link +android.app.Service#onDestroy}. Подобно операции, служба выполняет начальную настройку в методе +{@link android.app.Service#onCreate onCreate()} и освобождает все оставшиеся ресурсы в методе {@link +android.app.Service#onDestroy onDestroy()}. Например, +служба воспроизведения музыки может создать поток для воспроизведения музыки в методе {@link +android.app.Service#onCreate onCreate()}, затем остановить поток в методе {@link +android.app.Service#onDestroy onDestroy()}. + +

    Методы {@link android.app.Service#onCreate onCreate()} и {@link android.app.Service#onDestroy +onDestroy()} вызываются для всех служб, независимо от метода создания: +{@link android.content.Context#startService startService()} или {@link +android.content.Context#bindService bindService()}.

  • + +
  • Активный жизненный цикл службы начинается с вызова метода {@link +android.app.Service#onStartCommand onStartCommand()} или {@link android.app.Service#onBind onBind()}. +Каждый метод направляется намерением {@link +android.content.Intent}, которое было передано методу {@link android.content.Context#startService +startService()} или {@link android.content.Context#bindService bindService()}, соответственно. +

    Если служба запущена, активный жизненный цикл заканчивается одновременно с окончанием +всего жизненного цикла (служба активна даже после возврата из метода {@link android.app.Service#onStartCommand +onStartCommand()}). Если служба является привязанной, активный жизненный цикл заканчивается, когда возвращается метод {@link +android.app.Service#onUnbind onUnbind()}.

    +
  • +
+ +

Примечание. Хотя запущенная служба останавливается посредством вызова +метода {@link android.app.Service#stopSelf stopSelf()} или {@link +android.content.Context#stopService stopService()}, для службы не существует соответствующего обратного вызова +(нет обратного вызова {@code onStop()}). Поэтому, если служба не привязана к клиенту, +система уничтожает ее при остановке службы — метод {@link +android.app.Service#onDestroy onDestroy()} является единственным получаемым методом обратного вызова.

+ +

Рисунок 2 иллюстрирует типичные методы обратного вызова для службы. Хотя на рисунке отделены +службы, созданные посредством метода {@link android.content.Context#startService startService()}, от служб, +созданных посредством метода {@link android.content.Context#bindService bindService()}, помните, +что любая служба, независимо от способа запуска, позволяет клиентам выполнять привязку к ней. +Поэтому служба, изначально созданная посредством метода {@link android.app.Service#onStartCommand +onStartCommand()} (клиентом, который вызвал {@link android.content.Context#startService startService()}), +может получать вызов метода {@link android.app.Service#onBind onBind()} (когда клиент вызывает +метод {@link android.content.Context#bindService bindService()}).

+ +

Дополнительные сведения о создании службы, которая обеспечивает привязку, см. в документе Привязанные службы, +который содержит дополнительную информацию о методе обратного вызова {@link android.app.Service#onRebind onRebind()} +в разделе Управление жизненным циклом +привязанной службы.

+ + + diff --git a/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd new file mode 100644 index 0000000000000000000000000000000000000000..c9fdc0e069bb013f7aff7318a77198f23ac662a6 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=Задачи и стек переходов назад +parent.title=Операции +parent.link=activities.html +@jd:body + + + + +

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

+ +

Операция может даже запускать операции, существующие в других приложениях на устройстве. Например, +если ваше приложение хочет отправить сообщение электронной почты, вы можете определить намерение для выполнения +действия «отправить» и включить в него некоторые данные, например, адрес электронной почты и текст сообщения. После этого открывается операция из другого +приложения, которая объявила, что она обрабатывает намерения такого типа. В этом случае намерение состоит в том, чтобы +отправить сообщение электронной почты, поэтому в приложении электронной почты запускается операция «составить сообщение» (если одно намерение +может обрабатываться несколькими операциями, система предлагает пользователю выбрать, какую из операций использовать). После отправки сообщения электронной почты +ваша операция возобновляет работу, и все выглядит так, будто операция отправки электронной почты является частью вашего приложения. Хотя +операции могут быть частями разных приложений, система Android поддерживает удобство работы +пользователя, сохраняя обе операции в одной задаче.

+ +

Задача — это коллекция операций, с которыми взаимодействует пользователь +при выполнении определенного задания. Операции упорядочены в виде стека (стека переходов назад), в том +порядке, в котором открывались операции.

+ + + +

Начальным местом для большинства задач является главный экран устройства. Когда пользователь касается значка в средстве +запуска +приложений (или ярлыка на главном экране), эта задача приложения переходит на передний план. Если для +приложения нет задач (приложение не использовалось в последнее время), тогда создается новая задача +и открывается «основная» операция для этого приложения в качестве корневой операции в стеке.

+ +

Когда текущая операция запускает другую, новая операция помещается в вершину стека +и получает фокус. Предыдущая операция остается в стеке, но ее выполнение останавливается. Когда операция останавливается +, система сохраняет текущее состояние ее пользовательского интерфейса. Когда пользователь нажимает кнопку +Назад, +текущая операция удаляется из вершины стека (операция уничтожается) и возобновляется +работа предыдущей операции (восстанавливается предыдущее состояние ее пользовательского интерфейса). Операции в стеке +никогда не переупорядочиваются, только добавляются в стек и удаляются из него — добавляются в стек при запуске текущей операцией +и удаляются, когда пользователь выходит из нее с помощью кнопки Назад. По существу, +стек +переходов назад работает по принципу «последним вошел — первым вышел». На рисунке 1 это поведение +показано на временной шкале: состояние операций и текущее состояние стека переходов назад + показано в каждый момент времени.

+ + +

Рисунок 1. Иллюстрация того, как каждая новая операция в задаче +добавляет элемент в стек переходов назад. Когда пользователь нажимает кнопкуНазад, текущая +операция +уничтожается, и возобновляется работа предыдущей операции.

+ + +

Если пользователь продолжает нажимать кнопку Назад, операции поочередно удаляются из стека, +открывая +предыдущую операцию, пока пользователь не вернется на главный экран (или в операцию, которая была запущена +в начале выполнения задачи). Когда все операции удалены из стека, задача прекращает существование.

+ +
+

Рисунок 2. Две задачи: Задача B взаимодействует с пользователем +на переднем плане, тогда как Задача A находится в фоновом режиме, ожидая возобновления.

+
+
+

Рисунок 3. Создается несколько экземпляров одной операции.

+
+ +

Задача — это связанный блок, который может переходить в фоновый режим, когда пользователи начинают новую задачу или переходят +на главный экран с помощью кнопки Домой. В фоновом режиме все операции +задачи +остановлены, но стек обратного вызова для задачи остается неизменным. Задача просто потеряла фокус во время +выполнения другой задачи, как показано на рисунке 2. Затем задача может вернуться на передний план, так что пользователи +могут продолжить ее с прерванного места. Предположим, например, что текущая задача (Задача A) содержит три операции +в своем стеке — две операции под текущей операцией. Пользователь нажимает кнопку Домой, + затем запускает +новое приложение из средства запуска приложений. Когда появляется главный экран, Задача A переходит +в фоновый режим. Когда запускается новое приложение, система запускает задачу для этого приложения +(Задачу B) со своим собственным стеком операций. После взаимодействия с этим +приложением пользователь снова возвращается на главный экран и выбирает изначально запущенную +Задачу A. Теперь Задача A переходит на передний +план — все три операции ее стека остались неизменными, и возобновляется операция, находящаяся на +вершине стека. В этот +момент пользователь может также переключиться обратно на Задачу B, перейдя на главный экран и выбрав значок приложения, +которое запустило эту задачу (или выбрав задачу приложения на +экране обзора). +Это пример многозадачности в системе Android.

+ +

Примечание. В фоновом режиме может находиться несколько задач одновременно. +Однако, если пользователь запускает много фоновых задач одновременно, система может начать +уничтожение фоновых операций для освобождения памяти, что приведет к потере состояния задач. +См. следующий раздел Состояние операции.

+ +

Поскольку операции в стеке никогда не переупорядочиваются, если ваше приложение позволяет +пользователям запускать определенную операцию из нескольких операций, новый экземпляр +такой операции создается и помещается в стек (вместо помещения любого из предыдущих экземпляров +операции на вершину стека). По существу, для одной операции вашего приложения может быть создано несколько +экземпляров (даже из разных задач), как показано на рисунке 3. Поэтому, если пользователь переходит назад с помощью +кнопки Назад, каждый экземпляр операции появляется в том порядке, в котором они +были открыты (каждый +со своим состоянием пользовательского интерфейса). Однако вы можете изменить это поведение, если вы не хотите, чтобы создавалось несколько +экземпляров операции. Это описано в разделе Управление задачами ниже.

+ + +

Подведем итоги поведения операций и задач:

+ +
    +
  • Когда Операция A запускает Операцию B, Операция A останавливается, но система сохраняет ее состояние +(например, положение прокрутки и текст, введенный в формы). +Если пользователь нажимает кнопку Назад в Операции B, Операция A возобновляет работу +из сохраненного состояния.
  • +
  • Когда пользователь выходит из задачи нажатием кнопки Домой, текущая операция +останавливается и +ее задача переводится в фоновый режим. Система сохраняет состояние каждой операции в задаче. Если +пользователь впоследствии возобновляет задачу, выбирая значок запуска задачи, она переводится +на передний план и возобновляет операцию на вершине стека.
  • +
  • Если пользователь нажимает кнопку Назад, текущая операция удаляется из стека +и +уничтожается. Возобновляется предыдущая операция в стеке. Когда операция уничтожается, система +не сохраняет состояние операции.
  • +
  • Можно создавать несколько экземпляров операции, даже из других задач.
  • +
+ + +
+

Дизайн навигации

+

Для получения дополнительной информации о работе навигации в приложении Android, прочитайте раздел Навигация руководства «Дизайн для Android».

+
+ + +

Сохранение состояния операции

+ +

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

+ +

Когда система останавливает одну из ваших операций (например, когда запускается новая операция или задача +перемещается в фоновый режим), система может полностью уничтожить эту операцию, если необходимо восстановить +память системы. Когда это происходит, информация о состоянии операции теряется. Если это происходит, +система +знает, что операция находится в стеке переходов назад, но когда операция переходит +на вершину стека, система должна создать ее повторно (а не возобновить ее). Чтобы избежать +потери работы пользователя, вы должны с упреждением сохранять ее путем реализации методов +обратного вызова {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +в вашей операции.

+ +

Дополнительную информацию о сохранении состояния вашей операции см. в документе +Операции.

+ + + +

Управление задачами

+ +

Для большинства приложений способ, которым Android управляет задачами и стеком переходов назад, описанный выше, — помещение всех +операций последовательно в одну задачу в стек «последним вошёл — первым вышел», — +работает хорошо, и вы не должны беспокоиться о связи ваших операций с задачами +или об их существовании в стеке переходов назад. Однако вы можете решить, что вы хотите прервать +обычное поведение. Возможно, вы хотите, чтобы операция в вашем приложении начинала новую задачу +при запуске (вместо помещения в текущую задачу), или при запуске операции вы хотите +перенести на передний план ее существующий экземпляр (вместо создания нового +экземпляра на вершине стека переходов назад), или вы хотите чтобы при выходе пользователя из задачи из вашего стек переходов удалялись все +операции, кроме корневой операции.

+ +

Вы можете совершать эти и многие другие действия с помощью атрибутов в элементе манифеста +{@code <activity>} +и с помощью флагов в намерении, которое вы передаете в +{@link android.app.Activity#startActivity startActivity()}.

+ +

В этом смысле главными атрибутами +{@code <activity>}, которые вы можете использовать, являются следующие:

+ + + +

А главными флагами намерений, которые вы можете использовать, являются следующие:

+ +
    +
  • {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
  • +
  • {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
  • +
  • {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
  • +
+ +

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

+ +

Кроме того, отдельно обсуждаются рекомендации о представлении задач и операций и управлении ими +на экране обзора. Дополнительную информацию см. в разделе +Экран обзора. Обычно следует разрешить системе определить способ представления вашей задачи и +операций на экране обзора. Вам не нужно менять это поведение.

+ +

Внимание! В большинстве приложений не следует прерывать поведение +операций и задач по умолчанию. Если вы обнаружили, что вашей операции необходимо изменить +поведение по умолчанию, будьте внимательны и протестируйте удобство работы с операцией во время +запуска и при обратной навигации к ней из других операций и задач с помощью кнопки Назад. +Обязательно протестируйте поведение навигации, которое может противоречить поведению, ожидаемому пользователем.

+ + +

Определение режимов запуска

+ +

Режимы запуска позволяют вам определять связь нового экземпляра операции +с текущей задачей. Вы можете задавать различные режимы запуска двумя способами:

+
    +
  • Использование файла манифеста +

    Когда вы объявляете операцию в вашем файле манифеста, вы можете указать, как операция +должна связываться с задачами при ее запуске.

  • +
  • Использование флагов намерений +

    Когда вы вызываете {@link android.app.Activity#startActivity startActivity()}, +вы можете включить флаг в {@link android.content.Intent}, который объявляет, как должна быть связана +новая операция с текущей задачей (и должна ли).

  • +
+ +

По существу, если Операция A запускает Операцию B, Операция B может определить в своем манифесте, как она +должна быть связана с текущей задачей (если вообще должна), а Операция A может также запросить, как Операция B +должна быть связана с текущей задачей. Если обе операции определяют, как Операция B +должна быть связана с задачей, тогда запрос Операции A (как определено в намерении) обрабатывается через +запрос Операции B (как определено в ее манифесте).

+ +

Примечание. Некоторые режимы запуска, доступные для файла манифеста, +недоступны в виде флагов для намерения и, аналогичным образом, некоторые режимы запуска, доступные в виде флагов +для намерения, не могут быть определены в манифесте.

+ + +

Использование файла манифеста

+ +

При объявлении операции в вашем файле манифеста вы можете указать, как операция должна +быть связана с задачей посредством атрибута {@code +launchMode} +элемента {@code <activity>}.

+ +

Атрибут {@code +launchMode} указывает инструкцию по запуску операции в +задаче. Существует четыре различных режима запуска, +которые вы можете назначить атрибуту +launchMode:

+ +
+
{@code "standard"} (режим по умолчанию)
+
Режим по умолчанию. Система создает новый экземпляр операции в задаче, из +которой она была запущена, и направляет ему намерение. Может быть создано несколько экземпляров операции, +каждый экземпляр может принадлежать различным задачам, и одна задача может содержать несколько экземпляров.
+
{@code "singleTop"}
+
Если экземпляр операции уже существует на вершине текущей задачи, система +направляет намерение в этот экземпляр путем вызова его метода {@link +android.app.Activity#onNewIntent onNewIntent()}, а не путем создания нового экземпляра +операции. Может быть создано несколько экземпляров операции, каждый экземпляр может +принадлежать различным задачам, и одна задача может содержать несколько экземпляров (но только если +операция на вершине стека переходов назад не является существующим экземпляром операции). +

Предположим, что стек переходов назад задачи состоит из корневой операции A с операциями B, C +и D на вершине (стек имеет вид A-B-C-D и D находится на вершине). Поступает намерение для операции типа D. +Если D имеет режим запуска {@code "standard"} по умолчанию, запускается новый экземпляр класса и +стек принимает вид A-B-C-D-D. Однако, если D имеет режим запуска {@code "singleTop"}, существующий экземпляр +D получает намерение через {@link +android.app.Activity#onNewIntent onNewIntent()}, так как этот экземпляр находится на вершине стека — +стек сохраняет вид A-B-C-D. Однако, если поступает намерение для операции типа B, тогда в стек +добавляется новый экземпляр B, даже если он имеет режим запуска {@code "singleTop"}.

+

Примечание. Когда создается новый экземпляр операции, +пользователь может нажать кнопку Назад для возврата к предыдущей операции. Но когда существующий +экземпляр +операции обрабатывает новое намерение, пользователь не может нажать кнопку Назад для возврата к +состоянию +операции до поступления нового намерения в {@link android.app.Activity#onNewIntent +onNewIntent()}.

+
+ +
{@code "singleTask"}
+
Система создает новую задачу и создает экземпляр операции в корне новой задачи. +Однако, если экземпляр операции уже существует в отдельной задаче, система направляет +намерение в существующий экземпляр путем вызова его метода {@link +android.app.Activity#onNewIntent onNewIntent()}, а не путем создания нового экземпляра. Одновременно +может существовать только один экземпляр операции. +

Примечание. Хотя операция запускает новую задачу, кнопка +Назад возвращает пользователя к предыдущей операции.

+
{@code "singleInstance"}.
+
То же, что и {@code "singleTask"}, но при этом система не запускает никаких других операций +в задаче, содержащей этот экземпляр. Операция всегда является единственным членом своей задачи; +любые операции, запущенные этой операцией, открываются в отдельной задаче.
+
+ + +

В качестве другого примера: приложение Android Browser объявляет, что операция веб-браузера должна +всегда открываться в своей собственной задаче — путем указания режима запуска {@code singleTask} в элементе {@code <activity>}. +Это означает, что если ваше приложение выдает +намерение открыть Android Browser, его операция не помещается в ту же +задачу, что и ваше приложение. Вместо этого, либо для браузера запускается новая задача, либо, если браузер уже +имеет задачу, работающую в фоновом режиме, эта задача переводится на передний план для обработки нового +намерения.

+ +

И при запуске операции в новой задаче, и при запуске операции в существующей задаче, + кнопка Назад всегда возвращает пользователя к предыдущей операции. Однако, если вы +запускаете операцию, которая указывает режим запуска {@code singleTask}, вся задача переводится на передний план, если экземпляр +этой операции существует в фоновой задаче. В этот момент +стек переходов назад помещает все операции из задачи, переведенной на передний план, на вершину +стека. Рисунок 4 иллюстрирует сценарий этого типа.

+ + +

Рисунок 4. Представление того, как операция с режимом +запуска singleTask добавляется в стек переходов назад. Если операция уже является частью +фоновой задачи со своим собственным стеком переходов назад, то весь стек переходов назад также переносится вверх, +на вершину текущей задачи.

+ +

Дополнительную информацию об использовании режимов запуска в файле манифеста см. в документации элемента +<activity>, + где более подробно обсуждаются атрибут {@code launchMode} +и принимаемые значения.

+ +

Примечание. Поведение, которое вы указываете для вашей операции с помощью атрибута {@code launchMode}, +может быть переопределено флагами, включенными в намерение, которое запускает вашу операцию, как описано +в следующем разделе.

+ + + +

Использование флагов намерений

+ +

При запуске операции вы можете изменить связывание операции с ее задачей по умолчанию +путем включения флагов в намерение, которое доставляется в {@link +android.app.Activity#startActivity startActivity()}. Для изменения поведения по умолчанию +вы можете использовать следующие флаги:

+ +

+

{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
+
Запуск операции в новой задаче. Если задача уже работает для операции, которую вы запускаете +сейчас, эта задача переводится на передний план, ее последнее состояние восстанавливается, и операция получает +новое намерение в {@link android.app.Activity#onNewIntent onNewIntent()}. +

Это приводит к тому же поведению, что и значение {@code launchMode} в режиме {@code "singleTask"}, +как описано в предыдущем разделе.

+
{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
+
Если запускаемая операция является текущей операцией (находится на вершине стека переходов назад), тогда +вызов в {@link android.app.Activity#onNewIntent onNewIntent()} получает существующий экземпляр, + без создания нового экземпляра операции. +

Это приводит к тому же поведению, что и значение {@code launchMode} в режиме {@code "singleTop"}, +как описано в предыдущем разделе.

+
{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
+
Если запускаемая операция уже работает в текущей задаче, тогда вместо +запуска нового экземпляра этой операции уничтожаются все другие операции, расположенные в стеке выше нее +, и это намерение доставляется в возобновленный экземпляр этой операции (которая теперь находится на вершине стека) +посредством {@link android.app.Activity#onNewIntent onNewIntent()}). +

Для формирования такого поведения не существует значения для атрибута +{@code launchMode}.

+

Флаг {@code FLAG_ACTIVITY_CLEAR_TOP} чаще всего используется совместно с +флагом {@code FLAG_ACTIVITY_NEW_TASK}. +При использовании вместе эти флаги позволяют найти существующую операцию +в другой задаче и поместить ее в положение, где она сможет реагировать на намерение.

+

Примечание. Если для назначенной операции установлен режим запуска +{@code "standard"}, +она также удаляется из стека и на ее месте запускается новый экземпляр, чтобы обработать +входящее намерение. Именно поэтому в режиме запуска {@code "standard"} всегда создается новый +экземпляр для нового намерения.

+
+ + + + + + +

Обработка привязок

+ +

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

+ +

Вы можете изменить привязку любой данный операции с помощью +атрибута {@code taskAffinity} +элемента {@code <activity>}.

+ +

Атрибут {@code taskAffinity} +принимает строковое значение, которое должно отличаться от имени пакета по умолчанию, +объявленного в элементе +{@code <manifest>} +, поскольку система использует это имя для идентификации привязки задачи по умолчанию +для приложения.

+ +

Привязка вступает в игру в двух случаях:

+
    +
  • Когда намерение, запускающее +операцию, содержит флаг +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}. + +

    Новая операция по умолчанию запускается в задаче той операции, +которая вызвала {@link android.app.Activity#startActivity startActivity()}. Она помещается в тот же +стек переходов назад, что и вызывающая операция. Однако, если намерение, переданное +в {@link android.app.Activity#startActivity startActivity()}, +содержит флаг {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}, + система ищет другую задачу для помещения новой операции. Часто это новая задача. +Но необязательно. Если уже существует задача с той же привязкой, что +и у новой операции, операция запускается в этой задаче. Если нет, операция начинает новую задачу.

    + +

    Если этот флаг приводит к тому, что операция начинает новую задачу, и пользователь нажимает кнопку Домой +для выхода из нее, +должен существовать способ, позволяющий пользователю вернуться к задаче. Некоторые объекты (такие как +диспетчер уведомлений) всегда запускают операции во внешней задаче, а не в составе собственной, поэтому +они всегда помещают флаг {@code FLAG_ACTIVITY_NEW_TASK} в намерения, которые они передают +в {@link android.app.Activity#startActivity startActivity()}. +Если у вас есть операция, которую можно вызвать +внешним объектом, использующим этот флаг, позаботьтесь о том, чтобы у пользователя был независимый способ +вернуться в запущенную задачу, например, с помощью значка запуска (корневая операция задачи +содержит фильтр намерений {@link android.content.Intent#CATEGORY_LAUNCHER}; см. раздел Запуск задачи ниже).

    +
  • + +
  • Если для атрибута +{@code allowTaskReparenting} операции установлено значение {@code "true"}. +

    В этом случае операция может переместиться из запустившей ее задачи в задачу, к которой у операции есть привязка, +когда эта задача переходит на передний план.

    +

    Предположим, что операция, которая сообщает о погодных условиях в выбранных городах, +определена в составе приложения для путешественников. Она имеет ту же привязку, что и другие операции в том же +приложении (привязка приложения по умолчанию), и допускает переподчинение с этим атрибутом. +Когда одна из ваших операций запускает операцию прогноза погоды, она изначально принадлежит той же +задаче, что и ваша операция. Однако, когда задача из приложения для путешественников переходит на передний план, +операция прогноза погоды переназначается этой задаче и отображается внутри нее.

    +
  • +
+ +

Совет. Если файл {@code .apk} содержит более одного «приложения» +с точки зрения пользователя, вы, вероятно, захотите использовать атрибут {@code taskAffinity} +для назначения разных привязок операциям, связанным с каждым «приложением».

+ + + +

Очистка стека переходов назад

+ +

Если пользователь выходит из задачи на длительное время, система удаляет из задачи все операции, кроме +корневой операции. Когда пользователь возвращается в задачу, восстанавливается только корневая операция. +Система ведет себя таким образом, так как после продолжительного времени пользователи обычно уже забросили то, +чем они занимались ранее, и возвращаются в задачу, чтобы начать что-то новое.

+ +

Для изменения такого поведения предусмотрено несколько атрибутов операции, которыми вы можете воспользоваться:

+ +
+
alwaysRetainTaskState +
+
Если для этого атрибута установлено значение {@code "true"} в корневой операции задачи, +описанное выше поведение по умолчанию не происходит. +Задача восстанавливает все операции в своем стеке даже по истечении длительного периода времени.
+ +
clearTaskOnLaunch
+
Если для этого атрибута установлено значение {@code "true"} в корневой операции задачи, +стек очищается до корневой операции каждый раз, когда пользователь выходит из задачи +и возвращается в нее. Другими словами, этот атрибут противоположен атрибуту + +{@code alwaysRetainTaskState}. Пользователь всегда возвращается в задачу в ее +исходном состоянии, даже после кратковременного выхода из нее.
+ +
finishOnTaskLaunch +
+
Этот атрибут похож на {@code clearTaskOnLaunch}, +но он действует на +одну операцию, а не на всю задачу. Он также может приводить к удалению любой операции, +включая корневую операцию. Когда для него установлено значение {@code "true"}, +операция остается частью задачи только для текущего сеанса. Если пользователь +выходит из задачи, а затем возвращается в нее, операция уже отсутствует.
+
+ + + + +

Запуск задачи

+ +

Вы можете сделать операцию точкой входа, назначая ей фильтр намерений со значением +{@code "android.intent.action.MAIN"} в качестве указанного действия и +{@code "android.intent.category.LAUNCHER"} +в качестве указанной категории. Например:

+ +
+<activity ... >
+    <intent-filter ... >
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+    ...
+</activity>
+
+ +

Фильтр намерений такого типа приводит к тому, что в средстве запуска приложения отображаются значок и метка для +операции, что позволяет пользователю запускать операцию +и возвращаться в создавшую ее задачу в любой момент после ее запуска. +

+ +

Эта вторая возможность очень важна: пользователи должны иметь возможность выходить из задачи и затем возвращаться в нее +с помощью этого средства запуска операции. Поэтому два режима +запуска, которые отмечают, что операции всегда инициируют задачу, {@code "singleTask"} и +{@code "singleInstance"}, должны использоваться только в тех случаях, когда операция содержит +{@link android.content.Intent#ACTION_MAIN} +и фильтр {@link android.content.Intent#CATEGORY_LAUNCHER}. Представьте, например, что может произойти, +если фильтр отсутствует: намерение запускает операцию {@code "singleTask"}, которая инициирует +новую задачу, и пользователь некоторое время работает в этой задаче. Затем пользователь нажимает кнопку +Домой. Задача переводится в фоновый режим и не отображается. Теперь у пользователя нет возможности вернуться +к задаче, так как она отсутствует в средстве запуска приложения.

+ +

Для таких случаев, когда вы не хотите, чтобы пользователь мог вернуться к операции, установите для атрибута +{@code finishOnTaskLaunch} +элемента +<activity> +значение {@code "true"} (см. раздел Очистка стека).

+ +

Дополнительную информацию о представлении задач и операций и управлении ими +на экране обзора см. в разделе +Экран обзора.

+ + diff --git a/docs/html-intl/intl/ru/guide/index.jd b/docs/html-intl/intl/ru/guide/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..b0732722e1b853d4bba65e162f2ddbc659a3875c --- /dev/null +++ b/docs/html-intl/intl/ru/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Общие сведения о платформе Android + +@jd:body + + + + +

Система Android предоставляет разностороннюю платформу приложений, на основе которой можно создавать инновационные приложения и игры +для мобильных устройств в среде языка Java. В документах, ссылки на которые приведены на панели навигации слева, +рассказывается о том, как создавать приложения с помощью различных API-интерфейсов Android.

+ +

Если создание программ для Android является для вас новым делом, вам важно усвоить +следующие основные концепции, касающиеся платформы приложений Android:

+ + +
+ +
+ +

Приложения имеют несколько точек входа

+ +

Приложения для Android строятся из отдельных компонентов, которые можно вызывать +независимо друг от друга. Например, отдельная операция предоставляет один +экран для пользовательского интерфейса, а служба независимо выполняет +работу в фоновом режиме.

+ +

С помощью объекта Intent из одного компонента можно запустить другой компонент. Можно даже запустить +компонент из другого приложения, скажем, операцию из картографического приложения, чтобы показать адрес. Эта модель +формирует несколько точек входа для одного приложения, и при этом пользователь может выбрать любое приложение для выполнения по умолчанию +того или иного действия, которое могут вызывать другие приложения.

+ + +

Подробнее:

+ + +
+ + +
+ +

Приложения адаптируются к различным устройствам

+ +

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

+ +

Если каким-либо функциям приложения требуется определенное оборудование, например камера, можно +запрашивать его наличие в устройстве во время выполнения. При необходимости также можно объявлять функции, которые требуются приложению, +с тем чтобы такие магазины приложений, как Google Play, не позволяли устанавливать приложения на устройствах, в которых +этой функции нет.

+ + +

Подробнее:

+ + +
+ +
+ + + diff --git a/docs/html-intl/intl/ru/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/ru/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 0000000000000000000000000000000000000000..f2c5a9e0359096bfcc080c52840922ea5555f2e8 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=Манифест приложения +@jd:body + + + +

+ В корневой папке каждого приложения должен находиться файл AndroidManifest.xml (который именно так и называется +). Файл манифеста + содержит важную информацию о приложении, которая требуется системе Android. + Только получив эту информацию, система может выполнить какой-либо код + приложения. Среди прочего файл манифеста выполняет следующие действия: +

+ +
    +
  • Он задает имя пакета Java для приложения. +Это имя пакета служит уникальным идентификатором приложения.
  • + +
  • Он описывает компоненты приложения — операции, +службы, приемники широковещательных сообщений и поставщиков контента, из которых состоит +приложение. Он содержит имена классов, которые реализуют каждый компонент, и +публикует их возможности (указывает, например, какие сообщения {@link android.content.Intent +Intent} они могут принимать). На основании этих деклараций система Android +может определить, из каких компонентов состоит приложение и при каких условиях их можно запускать.
  • + +
  • Он определяет, в каких процессах будут размещаться компоненты приложения.
  • + +
  • Он объявляет, какие разрешения должны быть выданы приложению, чтобы оно могло получить +доступ к защищенным частям API-интерфейса и взаимодействовать с другими приложениями.
  • + +
  • Он также объявляет разрешения, требуемые для +взаимодействия с компонентами данного приложения.
  • + +
  • Он содержит список классов {@link android.app.Instrumentation}, которые при выполнении приложения предоставляют +сведения о профиле и прочую информацию. Эти объявления +присутствуют в файле манифеста только во время разработки и отладки +приложения и удаляются перед его публикацией.
  • + +
  • Он объявляет минимальный уровень API-интерфейса Android, который требуется +приложению.
  • + +
  • Он содержит список библиотек, с которыми должно быть связано приложение.
  • +
+ + +

Структура файла манифеста

+ +

+Приведенная далее схема позволяет ознакомиться с общей структурой файла манифеста и +всеми элементами, которые могут в нем содержаться. Каждый элемент вместе со всеми своими +атрибутами, полностью описывается в отдельном файле. Для просмотра подробных +сведений о любом элементе, щелкните имя элемента на схеме, +в алфавитном списке элементов, приведенном после схемы, или +в любом другом месте, где этот элемент упоминается. +

+ +
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest>
+
+    <uses-permission />
+    <permission />
+    <permission-tree />
+    <permission-group />
+    <instrumentation />
+    <uses-sdk />
+    <uses-configuration />  
+    <uses-feature />  
+    <supports-screens />  
+    <compatible-screens />  
+    <supports-gl-texture />  
+
+    <application>
+
+        <activity>
+            <intent-filter>
+                <action />
+                <category />
+                <data />
+            </intent-filter>
+            <meta-data />
+        </activity>
+
+        <activity-alias>
+            <intent-filter> . . . </intent-filter>
+            <meta-data />
+        </activity-alias>
+
+        <service>
+            <intent-filter> . . . </intent-filter>
+            <meta-data/>
+        </service>
+
+        <receiver>
+            <intent-filter> . . . </intent-filter>
+            <meta-data />
+        </receiver>
+
+        <provider>
+            <grant-uri-permission />
+            <meta-data />
+            <path-permission />
+        </provider>
+
+        <uses-library />
+
+    </application>
+
+</manifest>
+
+ +

+Далее приведен список всех элементов, расположенных в алфавитном порядке, которые могут +присутствовать в файле манифеста. Там могут находиться только эти элементы, а никакие другие +элементы или атрибуты добавлять нельзя. +

+ +

+<action> +
<activity> +
<activity-alias> +
<application> +
<category> +
<data> +
<grant-uri-permission> +
<instrumentation> +
<intent-filter> +
<manifest> +
<meta-data> +
<permission> +
<permission-group> +
<permission-tree> +
<provider> +
<receiver> +
<service> +
<supports-screens> +
<uses-configuration> +
<uses-feature> +
<uses-library> +
<uses-permission> +
<uses-sdk> +

+ + + + +

Соглашения о компонентах файла

+ +

+Ко всем элементам и атрибутам +из файла манифеста применяется рад соглашений и правил: +

+ +
+
Элементы
+
Обязательными +являются только элементы<manifest> и +<application> +. Оба они должны присутствовать в файле манифеста, при этом указать их можно только один раз. +Большинство других элементов можно указывать по нескольку раз или не указывать вовсе — хотя по +крайней мере некоторые из них нужны, чтобы файл манифеста был сколько-нибудь +информативным. + +

+Если в элементе и есть какое-то содержимое, то это другие элементы. +Все значения задаются с помощью атрибутов, а не как символьные данные в элементе. +

+ +

+Элементы, находящиеся на одном уровне, обычно не упорядочиваются. Например, + элементы <activity>, +<provider> и +<service> +можно указать в любой последовательности. (Элемент +<activity-alias> +является исключением из этого правила. Он должен следовать за элементом +<activity>, +псевдонимом которого он является.) +

+ +
Атрибуты
+
Формально все атрибуты являются необязательными. Однако некоторые их них +указывать необходимо, чтобы файл мог выполнять свое предназначение. В качестве руководства используйте эту +документацию. В отношении атрибутов, которые являются и вправду необязательными, в ней указывается значение, +используемое по умолчанию, или говорится, что произойдет, если такой атрибут не будет указан. + +

За исключением некоторых атрибутов корневого элемента +<manifest> +, имена всех атрибутов должны начинаться с префикса {@code android:} — +например, {@code android:alwaysRetainTaskState}. Поскольку этот префикс является +универсальным, в документации при указании атрибутов по имени +он обычно опускается.

+ +
Объявление имен классов
+
Многие элементы соответствуют объектам Java, в том числе элементы для самого +приложения (элемент +<application> +) и основных его компонентов — операций +(<activity>), +служб +(<service>), +приемников широковещательных сообщений +(<receiver>) +и поставщиков контента +(<provider>). + +

+Если вы определяете подкласс, а это практически всегда делается для классов компонентов +({@link android.app.Activity}, {@link android.app.Service}, +{@link android.content.BroadcastReceiver} и {@link android.content.ContentProvider}), +выполняется это с помощью атрибута {@code name}. В состав имени должно входить +полное обозначение пакета. +Например, подкласс {@link android.app.Service} можно объявить следующим образом: +

+ +
<manifest . . . >
+    <application . . . >
+        <service android:name="com.example.project.SecretService" . . . >
+            . . .
+        </service>
+        . . .
+    </application>
+</manifest>
+ +

+Однако его можно укоротить. Если первым символом в строке указать точку, эта +строка будет добавляться к имени пакета приложения (указанного атрибутом +package +элемента +package +). Следующее назначение является таким же, как приведенное выше: +

+ +
<manifest package="com.example.project" . . . >
+    <application . . . >
+        <service android:name=".SecretService" . . . >
+            . . .
+        </service>
+        . . .
+    </application>
+</manifest>
+ +

+При запуске компонента Android создает экземпляр подкласса, указанного по имени. +Если подкласс не указан, система создает экземпляр базового класса. +

+ +
Несколько значений
+
Если можно указать несколько значений, элемент почти всегда +приводится повторно. Делается это вместо перечисления нескольких значений в одном элементе. +Например, в фильтре Intent может быть перечислено несколько действий: + +
<intent-filter . . . >
+    <action android:name="android.intent.action.EDIT" />
+    <action android:name="android.intent.action.INSERT" />
+    <action android:name="android.intent.action.DELETE" />
+    . . .
+</intent-filter>
+ +
Значения ресурсов
+
Значения некоторых атрибутов могут отображаться на экране — например, +метка и значок операции. Значения этих атрибутов +следует локализовать, поэтому они должны задаваться в ресурсе или теме. Значения +ресурсов выражаются в следующем формате:

+ +

{@code @[пакет:]тип:имя}

+ +

+где имя пакета можно опустить, если ресурс находится в одном пакете +с приложением, тип — это тип ресурса, — например "string" или +"drawable", — а имя — это имя, определяющее ресурс. +Например: +

+ +
<activity android:icon="@drawable/smallPic" . . . >
+ +

+Значения из темы выражаются схожим образом, только в начале у них идет "{@code ?}", +а не "{@code @}": +

+ +

{@code ?[пакет:]тип:имя} +

+ +
Строковые значения
+
Когда значением атрибута является строка, следует использовать двойную обратную косую черту ("{@code \\}") +для выделения управляющей последовательности символов, — например "{@code \\n}" для +новой строки или "{@code \\uxxxx}" для символа Юникода.
+
+ + +

Отображение функций в файле

+ +

+В следующих разделах описано, как некоторые функции Android отображаются +в файле манифеста. +

+ + +

Фильтры объектов Intent

+ +

+Базовые компоненты приложения (его операции, службы и +приемники широковещательных сообщений) активируются объектами Intent. Intent — +это совокупность информации (объект {@link android.content.Intent}), описывающей +требуемое действие, — в том числе в ней указаны данные, с которыми следует выполнить это действие, категория +компонентов, которые должны выполнять это действие, и другие уместные инструкции. +Система Android находит компонент, который отреагирует на объект Intent, запускает +новый экземпляр компонента, если он требуется, и передает ему +объект Intent. +

+ +

+Компоненты объявляют свои возможности — виды объектов Intent, на которые они могут +реагировать, — с помощью фильтров Intent. Поскольку система Android +должна узнать, какие объекты Intent может обрабатывать тот или иной компонент, до того как она его запустит, +фильтры Intent указываются в файле манифеста как +элементы <intent-filter> +. Компонент может иметь любое количество фильтров, каждый из которых описывает +отдельную возможность компонента. +

+ +

+Объект Intent, в котором целевой компонент явно указан по имени, активирует этот компонент, +и фильтр при этом не учитывается. Но объект Intent, в котором имя целевого +компонента не указано, может активировать компонент, только если он может пройти через один из фильтров +компонента. +

+ +

+Сведения о том, каким образом объекты Intent проверяются по фильтрам Intent, +см. в отдельном документе +Объекты Intent +и фильтры объектов Intent. +

+ + +

Значки и метки

+ +

+У ряда элементов есть атрибуты {@code icon} и {@code label} для +небольшого значка и текстовой метки, которые могут отображаться на экране. У некоторых из них также есть атрибут +{@code description} для более длинного описательного текста, который также может +отображаться на экране. Например, элемент +<permission> +имеет все три таких атрибута, поэтому, когда пользователю задается вопрос, предоставить ли +разрешение запросившему его приложению, на экране может отображаться значок, +представляющий разрешение, имя разрешения и описание того, что оно +за собой влечет. +

+ +

+В любом случае значок и метка, заданные в элементе-контейнере, становятся параметрами +{@code icon} и {@code label}, используемыми по умолчанию для всех вложенных в этот контейнер дочерних элементов. +Так, значок и метка, заданные в элементе +<application>, +являются значком и меткой, используемыми по умолчанию для каждого компонента приложения. +Точно так же, значок и метка, заданные для компонента, — например элемента +<activity>, — +являются параметрами, используемыми по умолчанию для каждого элемента +<intent-filter> + компонента. Если в элементе +<application> +задана метка, а в операции и ее фильтре Intent — нет, +метка приложения будет считаться меткой и для операции, и для +фильтра Intent. +

+ +

+Значок и метка, заданные для фильтра Intent, используются для обозначения компонента, +когда он представляется пользователю, для указания функции, +которую анонсирует фильтр. Например, фильтр с параметрами +"{@code android.intent.action.MAIN}" и +"{@code android.intent.category.LAUNCHER}" сообщает, что эта операция +инициирует приложение, — то есть он обозначает ее как + операцию, которая должна быть отображена в средстве запуска приложений. Отсюда следует, что значок и метка, +заданные в фильтре, отображаются в средстве запуска. +

+ + +

Разрешения

+ +

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

+ +

+Каждое разрешение обозначается уникальной меткой. Зачастую метка обозначает +действие, выполнение которого ограничивается. Например, вот некоторые разрешения, определенные +системой Android: +

+ +

{@code android.permission.CALL_EMERGENCY_NUMBERS} +
{@code android.permission.READ_OWNER_DATA} +
{@code android.permission.SET_WALLPAPER} +
{@code android.permission.DEVICE_POWER}

+ +

+Функцию можно защитить не более чем одним разрешением. +

+ +

+Если приложению требуется доступ к функции, защищенной разрешением, +оно должно объявить, что ему необходимо это разрешение, с помощью элемента +<uses-permission> +в файле манифеста. Затем, когда приложение устанавливается на +устройство, установщик определяет, выдать ли запрошенное +разрешение, проверяя полномочия органов, подписавших сертификаты +приложения, а также, в некоторых случаях, спрашивая об этом пользователя. +Если разрешение предоставляется, приложение сможет использовать защищенные +функции. В противном случае его попытки доступа к этим функциям будут безуспешными, +причем пользователь не получит никакого уведомления об этом. +

+ +

+Приложение также может защищать с помощью разрешений собственные компоненты (операции, службы, +приемники широковещательных сообщений и поставщиков контента). Оно может использовать +любые разрешения, определенные системой Android (они приведены в объекте +{@link android.Manifest.permission android.Manifest.permission}) или объявленные +другими приложениями. Либо оно может определить разрешения самостоятельно. Новое разрешение объявляется +с помощью элемента +<permission> +. Например, операцию можно защитить следующим образом: +

+ +
+<manifest . . . >
+    <permission android:name="com.example.project.DEBIT_ACCT" . . . />
+    <uses-permission android:name="com.example.project.DEBIT_ACCT" />
+    . . .
+    <application . . .>
+        <activity android:name="com.example.project.FreneticActivity"
+                  android:permission="com.example.project.DEBIT_ACCT"
+                  . . . >
+            . . .
+        </activity>
+    </application>
+</manifest>
+
+ +

+Обратите внимание, что в этом примере разрешение {@code DEBIT_ACCT} не только +объявляется с помощью элемента +<permission> +, его использование также запрашивается с помощью элемента +<uses-permission> +. Чтобы другие компоненты приложения запускали защищенную +операцию, ее использование должно быть запрошено, даже несмотря на то, что защита +наложена самим приложением. +

+ +

+В этом же примере: если атрибут {@code permission} был бы задан как +разрешение, объявленное где-то еще +(например, {@code android.permission.CALL_EMERGENCY_NUMBERS}), его бы не +нужно было объявлять еще раз с помощью элемента +<permission> +. Однако все равно нужно было бы запрашивать его использование с помощью +<uses-permission>. +

+ +

+Элемент +<permission-tree> +объявляет пространство имен для группы разрешений, которые будут определены в +коде. А элемент +<permission-group> +определяет метку для набора разрешений (как для разрешений, объявленных в файле манифеста с помощью элементов +<permission> +, так и для объявленных где-то еще). Это влияет только на то, каким образом разрешения +группируются, когда отображаются пользователю. Элемент +<permission-group> +не указывает, какие разрешения относятся к группе. +Он просто дает группе имя. Чтобы включить разрешение в группу, +атрибуту +permissionGroup + его элемента +<permission> +необходимо присвоить имя группы. +

+ + +

Библиотеки

+ +

+Каждое приложение связывается с используемой по умолчанию библиотекой Android, в которой +имеются базовые пакеты для построения приложений (со стандартными классами, +например Activity, Service, Intent, View, Button, Application, ContentProvider +и так далее). +

+ +

+Однако некоторые пакеты находятся в собственных библиотеках. Если ваше приложение +использует код из одного из таких пакетов, оно должно в явном виде потребовать, чтобы его связали +с этим пакетом. Файл манифеста должен содержать отдельный элемент +<uses-library> +для указания имени каждой библиотеки. (Имя библиотеки можно найти в +документации по пакету.) +

diff --git a/docs/html-intl/intl/ru/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/ru/guide/topics/providers/calendar-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..2d12e1261d9332adacd75ed6e1a343c7a56c490a --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=Поставщик календаря +@jd:body + + + +

Поставщик календаря представляет собой репозиторий для событий календаря пользователя. API +поставщика календаря позволяет запрашивать, вставлять, обновлять и удалять календари, +события, участников, напоминания и т. д.

+ + +

API поставщика календаря может использоваться как приложениями, так и адаптерами синхронизации. Правила +зависят от типа программы, которая выполняет вызовы. В этой статье +главным образом рассматривается использование API поставщика календаря в качестве приложения. Сведения о различиях +между адаптерами синхронизации представлены в разделе +Адаптеры синхронизации.

+ + +

Обычно, чтобы считать или записать данные календаря, в манифесте приложения +должны быть включены надлежащие разрешения, которые описываются в разделе Разрешения +пользователей. Чтобы упростить выполнение часто используемых операций, в поставщике +календаря предусмотрен набор намерений, как описано в разделе Намерения +календаря. Эти намерения позволяют пользователям переходить в приложение календаря для вставки, просмотра +и редактирования событий. После взаимодействия пользователя с календарем он возвращается +в исходное приложение. Поэтому вашему приложению не нужно запрашивать разрешения, +а также предоставлять пользовательский интерфейс для просмотра или создания событий.

+ +

Основы

+ +

Поставщики контента хранят в себе данные и предоставляют к ним доступ +для приложений. Поставщики контента, предлагаемые платформой Android (включая поставщик календаря) обычно представляют данные в виде набора таблиц, в основе +которых лежит модель реляционной базы данных. Каждая строка в такой таблице представляет собой запись, а каждый столбец — данные +определенного типа и значения. Благодаря API поставщика календаря приложения +и адаптеры синхронизации получают доступ на чтение/запись к таблицам в базе данных, в которых +представлены данные календаря пользователя.

+ +

Каждый поставщик календаря предоставляет общедоступный URI (упакованный в объект +{@link android.net.Uri}), +который служит уникальным идентификатором своего набора данных. Поставщик контента, который управляет +несколькими наборами данных (несколькими таблицами), предоставляет отдельный URI для каждого набора. Все +URI поставщиков начинаются со строки content://. Она +определяет данные, которые находятся под управлением поставщика контента. Поставщик календаря +задает константы для URI каждого из своих классов (таблиц). Такие +URI имеют формат <class>.CONTENT_URI. Например, +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}.

+ +

На рисунке 1 изображено графическое представление модели данных поставщика календаря. На нем представлены +основные таблицы и поля, которые связывают их друг с другом.

+ +Calendar Provider Data Model +

Рисунок 1. Модель данных поставщика календаря.

+ +

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

+ +

Класс {@link android.provider.CalendarContract} определяет модель данных календаря и информацию, относящуюся к событиям. Эти данные хранятся в различных таблицах, указанных ниже.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Таблица (класс)Описание

{@link android.provider.CalendarContract.Calendars}

В этой таблице находится +информация о календарях. В каждой строке этой таблицы представлены сведения +об отдельном календаре, например, его название, цвет, информация о синхронизации и т. д.
{@link android.provider.CalendarContract.Events}В этой таблице находится +информация о событиях. В каждой строке этой таблицы содержится информация об отдельном +событии —например, заголовок события, место проведения, время начала, время +завершения и т. д. Событие может быть однократным или повторяющимся. Сведения об участниках, +напоминаниях и расширенные свойства хранятся в отдельных таблицах. +В каждой из них имеется целочисленная переменная {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}, +которая ссылается на объект {@link android.provider.BaseColumns#_ID} в таблице событий.
{@link android.provider.CalendarContract.Instances}В этой таблице содержатся данные о времени +начала и окончания каждого повторения события. В каждой строке этой таблицы +представлено одно повторение события. Однократные события сопоставляются с повторениями +один к одному. Для повторяющихся событий автоматически создаются несколько строк, +которые соответствуют нескольким повторениям события.
{@link android.provider.CalendarContract.Attendees}В этой таблице находится +информация об участниках (гостях). В каждой строке этой таблицы указан один +гость. В ней указываются тип гостя и информация о том, +посетит ли он событие.
{@link android.provider.CalendarContract.Reminders}В этой таблице находятся +данные уведомлений или оповещений. В каждой строке этой таблицы указано одно уведомление или оповещение. Для одного +события можно создать несколько напоминаний. Максимальное количество таких напоминаний для события +задается с помощью +целочисленной переменной {@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS}, +значение которой задает адаптер синхронизации, владеющий +указанным календарем. Напоминания задаются в минутах до начала события и +имеют метод, который определяет порядок уведомления пользователя.
+ +

API поставщика календаря обеспечивает достаточную гибкость и эффективность. В то же время +важно предоставить интерфейс, который будет удобным для пользователя, +и обеспечить защиту целостности календаря и его данных. Поэтому существует +ряд моментов, которые следует учитывать при использовании этого API.

+ +
    + +
  • Вставка, обновление и просмотр событий календаря. Чтобы вставить, изменить и считать события напрямую из поставщика календаря, требуются соответствующие разрешения. Однако, если вы не планируете создавать полнофункциональное приложение календаря или адаптер синхронизации, запрашивать такие разрешения не обязательно. Вместо этого можно использовать намерения, поддерживаемые приложением «Календарь» Android, для обработки операций чтения и записи в этом приложении. При использовании намерений ваше приложение отправляет пользователям приложение «Календарь» для выполнения требуемой операции +в предварительно заполненной форме. По завершении они возвращаются в приложение. +Реализовав в вашем приложении возможность выполнения часто используемых операций через приложение «Календарь», +вы обеспечиваете для пользователей единообразный и функциональный пользовательский интерфейс. Мы рекомендуем использовать +именно такой подход. Дополнительные сведения представлены в разделе Намерения +календаря.

    + + +
  • Адаптеры синхронизации. Адаптер синхронизации синхронизирует данные календаря +на устройстве пользователя с данными на сервере или в другом источнике данных. В таблицах +{@link android.provider.CalendarContract.Calendars} и +{@link android.provider.CalendarContract.Events} имеются +столбцы, зарезервированные для адаптеров синхронизации. +Ни поставщик, ни приложения не должны изменять их. Фактически они скрыты +до тех пор, пока адаптер синхронизации не начнет использовать их. Дополнительные сведения об +адаптерах синхронизации представлены в разделе Адаптеры синхронизации.
  • + +
+ + +

Разрешения пользователей

+ +

Чтобы считать данные календаря, в файл манифеста приложения необходимо включить разрешение {@link +android.Manifest.permission#READ_CALENDAR}. Также в него +следует включить разрешение {@link android.Manifest.permission#WRITE_CALENDAR} +для удаления, вставки или обновления данных календаря:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
+    <uses-sdk android:minSdkVersion="14" />
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+    ...
+</manifest>
+
+ + +

Таблица календарей

+ +

В таблице {@link android.provider.CalendarContract.Calendars} содержатся подробные сведения +о каждом отдельном календаре. Выполнять запись в указанные ниже столбцы +этой таблицы могут и приложение, и адаптер синхронизации. +Полный список поддерживаемых полей представлен в справке по классу +{@link android.provider.CalendarContract.Calendars}.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
КонстантаОписание
{@link android.provider.CalendarContract.Calendars#NAME}Название календаря.
{@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}Название этого календаря, которое отображается для пользователя.
{@link android.provider.CalendarContract.Calendars#VISIBLE}Логическое значение, обозначающее, выбран ли календарь для отображения. Значение +«0» указывает на то, что события, связанные с +этим календарем, не отображаются. Значение «1» указывает на то, что события, связанные с +этим календарем, отображаются. Это значение влияет на создание строк в таблице {@link +android.provider.CalendarContract.Instances}.
{@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}Логическое значение, обозначающее, следует ли синхронизировать календарь и хранить имеющиеся в нем события +на устройстве. Значение «0» указывает, что не следует синхронизировать этот календарь или +хранить имеющиеся в нем события на устройстве. Значение «1» указывает, что этот календарь следует синхронизировать и +хранить имеющиеся в нем события на устройстве.
+ +

Запрос календаря

+ +

Ниже представлен пример того, как получить календари, которыми +владеет определенный пользователь. Для простоты демонстрации операция запроса в этом примере находится в +потоке пользовательского интерфейса («основной поток»). На практике это следует делать в асинхронном +потоке, а не в основном. Дополнительные сведения представлены в статье +Загрузчики. Если же вы не только +считываете данные, но и вносите в них изменения, обратитесь к справке по классу {@link android.content.AsyncQueryHandler}. +

+ + +
+// Projection array. Creating indices for this array instead of doing
+// dynamic lookups improves performance.
+public static final String[] EVENT_PROJECTION = new String[] {
+    Calendars._ID,                           // 0
+    Calendars.ACCOUNT_NAME,                  // 1
+    Calendars.CALENDAR_DISPLAY_NAME,         // 2
+    Calendars.OWNER_ACCOUNT                  // 3
+};
+  
+// The indices for the projection array above.
+private static final int PROJECTION_ID_INDEX = 0;
+private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
+private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
+private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
+ + + + + +

В следующей части примера создается запрос. С помощью выбора определяются +критерии для запроса. В этом примере выполняется поиск +календарей со следующими значениями параметров: ACCOUNT_NAME +— sampleuser@google.com, ACCOUNT_TYPE +— com.google и OWNER_ACCOUNT + — sampleuser@google.com. Для просмотра всех просмотренных +пользователем календарей, а не только имеющихся у него, не указывайте параметр OWNER_ACCOUNT. +Запрос возвращает объект {@link android.database.Cursor}, +который можно использовать для перебора результатов, возвращенных запросом к базе +данных. Дополнительные сведения об использовании запросов в поставщиках контента +представлены в статье Поставщики контента.

+ + +
// Run query
+Cursor cur = null;
+ContentResolver cr = getContentResolver();
+Uri uri = Calendars.CONTENT_URI;   
+String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" 
+                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
+                        + Calendars.OWNER_ACCOUNT + " = ?))";
+String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
+        "sampleuser@gmail.com"}; 
+// Submit the query and get a Cursor object back. 
+cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
+ +

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

+ +
// Use the cursor to step through the returned records
+while (cur.moveToNext()) {
+    long calID = 0;
+    String displayName = null;
+    String accountName = null;
+    String ownerName = null;
+      
+    // Get the field values
+    calID = cur.getLong(PROJECTION_ID_INDEX);
+    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
+    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
+    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
+              
+    // Do something with the values...
+
+   ...
+}
+
+ +

Изменение календаря

+ +

Чтобы обновить календарь, можно указать {@link +android.provider.BaseColumns#_ID} календаря: либо в виде идентификатора, +добавленного к URI + +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}), +либо в качестве первого элемента выделения. Выделение +должно начинаться с "_id=?", а первым аргументом +selectionArg должен быть {@link +android.provider.BaseColumns#_ID} календаря. +Также для выполнения обновлений можно закодировать идентификатор в URI. В этом примере для +изменения отображаемого имени календаря используется +подход +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}):

+ +
private static final String DEBUG_TAG = "MyActivity";
+...
+long calID = 2;
+ContentValues values = new ContentValues();
+// The new display name for the calendar
+values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
+Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
+int rows = getContentResolver().update(updateUri, values, null, null);
+Log.i(DEBUG_TAG, "Rows updated: " + rows);
+ +

Вставка календаря

+ +

Для управления календарями в основном используются адаптеры синхронизации, поэтому +новые календари следует вставлять исключительно как адаптер синхронизации. По большей части +приложения могут вносить в календари только поверхностные изменения, такие как изменение отображаемого имени. Если +приложению требуется создать локальный календарь, это можно сделать путем +вставки календаря в виде адаптера синхронизации с помощью параметра {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} типа {@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL}. +{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} +представляет собой особый тип аккаунтов для календарей, которые не связаны +с аккаунтом устройства. Календари этого типа не синхронизируются с сервером. Дополнительные сведения +об адаптерах синхронизации представлены в статье Адаптеры синхронизации.

+ +

Таблица событий

+ +

В таблице {@link android.provider.CalendarContract.Events} содержатся подробные сведения +о каждом отдельном событии. Чтобы получить возможность добавлять, обновлять или удалять события, +в файл манифеста +приложения необходимо включить разрешение {@link android.Manifest.permission#WRITE_CALENDAR}.

+ +

Выполнять запись в указанные ниже столбцы этой таблицы могут и приложение, и +адаптер синхронизации. Полный список поддерживаемых полей представлен в справке по классу {@link +android.provider.CalendarContract.Events}.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КонстантаОписание
{@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}{@link android.provider.BaseColumns#_ID} календаря, к которому принадлежит событие.
{@link android.provider.CalendarContract.EventsColumns#ORGANIZER}Адрес эл. почты организатора (владельца) события.
{@link android.provider.CalendarContract.EventsColumns#TITLE}Название события.
{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}Место проведения.
{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}Описание события.
{@link android.provider.CalendarContract.EventsColumns#DTSTART}Время начала события по UTC (в миллисекундах) от точки отсчета.
{@link android.provider.CalendarContract.EventsColumns#DTEND}Время окончания события по UTC (в миллисекундах) от точки отсчета.
{@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}Часовой пояс события.
{@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}Часовой пояс для времени окончания события.
{@link android.provider.CalendarContract.EventsColumns#DURATION}Продолжительность события в формате RFC5545. +Например, значение "PT1H" обозначает, что событие +должно длиться один час, а значение "P2W" указывает на продолжительность +в 2 недели.
{@link android.provider.CalendarContract.EventsColumns#ALL_DAY}Значение «1» обозначает, что это событие занимает весь день по +местному часовому поясу. Значение «0» указывает на то, что это регулярное событие, которое может начаться +и завершиться в любое время в течение дня.
{@link android.provider.CalendarContract.EventsColumns#RRULE}Правило повторения для формата события. Например, +"FREQ=WEEKLY;COUNT=10;WKST=SU". С другими +примерами можно ознакомиться здесь.
{@link android.provider.CalendarContract.EventsColumns#RDATE}Даты повторения события. +Обычно {@link android.provider.CalendarContract.EventsColumns#RDATE} +используется вместе с {@link android.provider.CalendarContract.EventsColumns#RRULE} +для определения агрегированного набора +повторяющихся событий. Дополнительные сведения представлены в спецификации RFC5545.
{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}Если событие считается как занятое или как свободное время, + доступное для планирования.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}Указывает, могут ли гости вносить изменения в событие.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}Указывает, могут ли гости приглашать других гостей.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}Указывает, могут ли гости просматривать список участников.
+ +

Добавление событий

+ +

Когда ваше приложение вставляет новое событие, мы рекомендуем использовать намерение +{@link android.content.Intent#ACTION_INSERT INSERT}, как описано в разделе Использование намерения для вставки события. Однако при +необходимости вы можете вставлять события напрямую. В этом разделе как раз описывается то, как это +сделать.

+ + +

Ниже указаны правила, которыми следует руководствоваться для вставки нового события.

+
    + +
  • Необходимо указать {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} и {@link +android.provider.CalendarContract.EventsColumns#DTSTART}.
  • + +
  • Необходимо указать {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}. Чтобы получить список +установленных в системе идентификаторов часовых поясов, воспользуйтесь методом {@link +java.util.TimeZone#getAvailableIDs()}. Обратите внимание, что это правило не применяется при +вставке события с помощью намерения {@link +android.content.Intent#ACTION_INSERT INSERT}, как описано в разделе Использование намерения для вставки события, — в этом +случае используется часовой пояс по умолчанию.
  • + +
  • Для однократных событий необходимо указать {@link +android.provider.CalendarContract.EventsColumns#DTEND}.
  • + + +
  • Для повторяющихся событий необходимо указать {@link +android.provider.CalendarContract.EventsColumns#DURATION} в дополнение к {@link +android.provider.CalendarContract.EventsColumns#RRULE} или {@link +android.provider.CalendarContract.EventsColumns#RDATE}. Обратите внимание, что это правило не применяется при +вставке события с помощью намерения {@link +android.content.Intent#ACTION_INSERT INSERT}, как описано в разделе Использование намерения для вставки события, — в этом +случае можно использовать {@link +android.provider.CalendarContract.EventsColumns#RRULE} в сочетании с {@link android.provider.CalendarContract.EventsColumns#DTSTART} и {@link android.provider.CalendarContract.EventsColumns#DTEND}; кроме того, приложение «Календарь» + автоматически преобразует указанный период в продолжительность.
  • + +
+ +

Ниже представлен пример вставки события. Для простоты все это выполняется в потоке +пользовательского интерфейса. На практике же все вставки и обновления следует выполнять в +асинхронном потоке, чтобы переместить операцию в фоновый поток. Дополнительные сведения представлены в справке по +{@link android.content.AsyncQueryHandler}.

+ + +
+long calID = 3;
+long startMillis = 0; 
+long endMillis = 0;     
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2012, 9, 14, 7, 30);
+startMillis = beginTime.getTimeInMillis();
+Calendar endTime = Calendar.getInstance();
+endTime.set(2012, 9, 14, 8, 45);
+endMillis = endTime.getTimeInMillis();
+...
+
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Events.DTSTART, startMillis);
+values.put(Events.DTEND, endMillis);
+values.put(Events.TITLE, "Jazzercise");
+values.put(Events.DESCRIPTION, "Group workout");
+values.put(Events.CALENDAR_ID, calID);
+values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
+Uri uri = cr.insert(Events.CONTENT_URI, values);
+
+// get the event ID that is the last element in the Uri
+long eventID = Long.parseLong(uri.getLastPathSegment());
+// 
+// ... do something with event ID
+//
+//
+ +

Примечание. Ниже демонстрируется, как в примере кода выполняется захват +идентификатора события после создания этого события. Это самый простой способ получить идентификатор события. Зачастую +идентификатор события необходим для выполнения других действий с календарем — например, для добавления участников или +напоминаний о событии.

+ + +

Обновление событий

+ +

Когда ваше приложение хочет предоставить пользователю возможность изменить событие, мы рекомендуем использовать намерение +{@link android.content.Intent#ACTION_EDIT EDIT}, как описано в разделе +Использование намерения для вставки события. +Однако при необходимости вы можете редактировать события напрямую. Чтобы обновить +событие, можно указать +_ID события: либо в виде идентификатора, добавленного к URI({@link +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}), +либо в качестве первого элемента выделения. Выделение +должно начинаться с "_id=?", а первым аргументом +selectionArg должен быть _ID события. Также можно обновлять +выделения без идентификаторов. Ниже представлен пример обновления +события. Это пример изменения названия события с помощью +метода +{@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}:

+ + +
private static final String DEBUG_TAG = "MyActivity";
+...
+long eventID = 188;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+Uri updateUri = null;
+// The new title for the event
+values.put(Events.TITLE, "Kickboxing"); 
+updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+int rows = getContentResolver().update(updateUri, values, null, null);
+Log.i(DEBUG_TAG, "Rows updated: " + rows);  
+ +

Удаление событий

+ +

Удалить событие можно по его {@link +android.provider.BaseColumns#_ID}, который добавлен в качестве идентификатора к URI, или с помощью +стандартного выделения. В случае использования добавленного идентификатора невозможно также выполнить и выделение. +Существует две версии операции удаления: для приложения и для адаптера синхронизации. При удалении +для приложения в столбце deleted устанавливается значение «1». Этот флаг +сообщает адаптеру синхронизации о том, что строка была удалена и информацию об удалении следует +передать серверу. При удалении для адаптера синхронизации событие удаляется из +базы данных вместе со всеми связанными с ним данными. Ниже представлен пример удаления +события для приложения по его {@link android.provider.BaseColumns#_ID}.

+ + +
private static final String DEBUG_TAG = "MyActivity";
+...
+long eventID = 201;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+Uri deleteUri = null;
+deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+int rows = getContentResolver().delete(deleteUri, null, null);
+Log.i(DEBUG_TAG, "Rows deleted: " + rows);  
+
+ +

Таблица участников

+ +

В каждой строке таблицы {@link android.provider.CalendarContract.Attendees} +указан один участник или гость события. При вызове метода +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} +возвращается список участников для события +с заданным {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}. +Этот {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +должен соответствовать {@link +android.provider.BaseColumns#_ID} определенного события.

+ +

В таблице ниже указаны +поля, доступные для записи. При вставке нового участника необходимо указать все эти поля, кроме +ATTENDEE_NAME. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КонстантаОписание
{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}Идентификатор события.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}Имя участника.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}Адрес эл. почты участника.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}

Связь участника с событием. Одно из следующего:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}
  • +
+
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}

Тип участника. Одно из следующего:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}
  • +
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}

Статус посещения события участником. Одно из следующего:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}
  • +
+ +

Добавление участников

+ +

Ниже представлен пример добавления одного участника события. Обратите внимание, что нужно в обязательном порядке +указать +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}.

+ +
+long eventID = 202;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Attendees.ATTENDEE_NAME, "Trevor");
+values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
+values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
+values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
+values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
+values.put(Attendees.EVENT_ID, eventID);
+Uri uri = cr.insert(Attendees.CONTENT_URI, values);
+
+ +

Таблица напоминаний

+ +

В каждой строке таблицы {@link android.provider.CalendarContract.Reminders} +указано одно напоминание о событии. При вызове метода +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()}возвращается список напоминаний для события +с заданным +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}.

+ + +

В таблице ниже указаны поля, доступные для записи. При вставке нового +напоминания необходимо указать все эти поля. Обратите внимание, что адаптеры синхронизации задают +типы напоминаний, которые они поддерживают, в таблице {@link +android.provider.CalendarContract.Calendars}. Подробные сведения см. +в справке по +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS}.

+ + + + + + + + + + + + + + + + + + + +
КонстантаОписание
{@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}Идентификатор события.
{@link android.provider.CalendarContract.RemindersColumns#MINUTES}Время срабатывания уведомления (в минутах) до начала события.
{@link android.provider.CalendarContract.RemindersColumns#METHOD}

Метод уведомления, заданный на сервере. Одно из следующего:

+
    +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}
  • +
+ +

Добавление напоминаний

+ +

Ниже представлен пример добавления напоминания в событие. Напоминание срабатывает за 15 +минут до начала события.

+
+long eventID = 221;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Reminders.MINUTES, 15);
+values.put(Reminders.EVENT_ID, eventID);
+values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
+Uri uri = cr.insert(Reminders.CONTENT_URI, values);
+ +

Таблица экземпляров

+ +

В таблице +{@link android.provider.CalendarContract.Instances} содержатся данные о времени +начала и окончания повторений события. В каждой строке этой таблицы +представлено одно повторение события. Таблица экземпляров недоступна для записи; она предоставляет только +возможность запрашивать повторения событий.

+ +

В таблице ниже перечислены некоторые из полей, которые можно запросить для экземпляра. Обратите внимание, +что часовой пояс задается параметрами +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} +и +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES}.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КонстантаОписание
{@link android.provider.CalendarContract.Instances#BEGIN}Время начала экземпляра в формате UTC (в миллисекундах).
{@link android.provider.CalendarContract.Instances#END}Время окончания экземпляра в формате UTC (в миллисекундах).
{@link android.provider.CalendarContract.Instances#END_DAY}День окончания экземпляра по юлианскому календарю относительно часового пояса +приложения «Календарь». + +
{@link android.provider.CalendarContract.Instances#END_MINUTE}Минута окончания экземпляра, вычисленная от полуночи по часовому поясу +приложения «Календарь».
{@link android.provider.CalendarContract.Instances#EVENT_ID}_ID события для этого экземпляра.
{@link android.provider.CalendarContract.Instances#START_DAY}День начала экземпляра по юлианскому календарю относительно часового пояса приложения «Календарь». +
{@link android.provider.CalendarContract.Instances#START_MINUTE}Минута начала экземпляра, вычисленная от полуночи по часовому поясу +приложения «Календарь». +
+ +

Запрос таблицы экземпляров

+ +

Чтобы запросить таблицу экземпляров, необходимо указать промежуток времени для запроса в +URI. В этом примере {@link android.provider.CalendarContract.Instances} +получает доступ к полю {@link +android.provider.CalendarContract.EventsColumns#TITLE} посредством своей реализации +интерфейса {@link android.provider.CalendarContract.EventsColumns}. +Другими словами, {@link +android.provider.CalendarContract.EventsColumns#TITLE} +возвращается посредством обращения к базе данных, а не путем запроса таблицы {@link +android.provider.CalendarContract.Instances} с необработанными данными.

+ +
+private static final String DEBUG_TAG = "MyActivity";
+public static final String[] INSTANCE_PROJECTION = new String[] {
+    Instances.EVENT_ID,      // 0
+    Instances.BEGIN,         // 1
+    Instances.TITLE          // 2
+  };
+  
+// The indices for the projection array above.
+private static final int PROJECTION_ID_INDEX = 0;
+private static final int PROJECTION_BEGIN_INDEX = 1;
+private static final int PROJECTION_TITLE_INDEX = 2;
+...
+
+// Specify the date range you want to search for recurring
+// event instances
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2011, 9, 23, 8, 0);
+long startMillis = beginTime.getTimeInMillis();
+Calendar endTime = Calendar.getInstance();
+endTime.set(2011, 10, 24, 8, 0);
+long endMillis = endTime.getTimeInMillis();
+  
+Cursor cur = null;
+ContentResolver cr = getContentResolver();
+
+// The ID of the recurring event whose instances you are searching
+// for in the Instances table
+String selection = Instances.EVENT_ID + " = ?";
+String[] selectionArgs = new String[] {"207"};
+
+// Construct the query with the desired date range.
+Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
+ContentUris.appendId(builder, startMillis);
+ContentUris.appendId(builder, endMillis);
+
+// Submit the query
+cur =  cr.query(builder.build(), 
+    INSTANCE_PROJECTION, 
+    selection, 
+    selectionArgs, 
+    null);
+   
+while (cur.moveToNext()) {
+    String title = null;
+    long eventID = 0;
+    long beginVal = 0;    
+    
+    // Get the field values
+    eventID = cur.getLong(PROJECTION_ID_INDEX);
+    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
+    title = cur.getString(PROJECTION_TITLE_INDEX);
+              
+    // Do something with the values. 
+    Log.i(DEBUG_TAG, "Event:  " + title); 
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTimeInMillis(beginVal);  
+    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
+    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));    
+    }
+ }
+ +

Намерения календаря

+

Вашему приложению не нужно запрашивать разрешения на чтение и запись данных календаря. Вместо этого можно использовать намерения, поддерживаемые приложением «Календарь» Android, для обработки операций чтения и записи в этом приложении. В таблице ниже указаны намерения, поддерживаемые поставщиком календаря.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ДействиеURIОписаниеДополнительные данные

+ {@link android.content.Intent#ACTION_VIEW VIEW}

content://com.android.calendar/time/<ms_since_epoch>

+ Сослаться на URI также можно с помощью +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI}. +Пример использования этого намерения представлен в разделе Использование намерений для просмотра данных календаря. + +
Открытие календаря во время, заданное параметром <ms_since_epoch>.Отсутствуют.

{@link android.content.Intent#ACTION_VIEW VIEW}

+ +

content://com.android.calendar/events/<event_id>

+ + Сослаться на URI также можно с помощью +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Пример использования этого намерения представлен в разделе Использование намерений для просмотра данных календаря. + +
Просмотр события, указанного с помощью <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
+
+
+ {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
{@link android.content.Intent#ACTION_EDIT EDIT}

content://com.android.calendar/events/<event_id>

+ + Сослаться на URI также можно с помощью +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Пример использования этого намерения представлен в разделе Использование намерения для редактирования события. + + +
Редактирование события, указанного с помощью <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
+
+
+ {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
{@link android.content.Intent#ACTION_EDIT EDIT}
+
+ {@link android.content.Intent#ACTION_INSERT INSERT}

content://com.android.calendar/events

+ + Сослаться на URI также можно с помощью +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Пример использования этого намерения представлен в разделе Использование намерения для редактирования события. + +
Создание события.Любые из дополнительных данных, указанных в таблице ниже.
+ +

В таблице ниже указаны дополнительные данные намерения, которые поддерживаются поставщиком календаря. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Дополнительные данные намеренияОписание
{@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}Название события.
{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}Время начала события (в миллисекундах) от эпохи.
{@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}Время окончания события (в миллисекундах) от эпохи.
{@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}Логическое значение, обозначающее, что это событие на весь день. Значение может быть +true или false.
{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}Место проведения события.
{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}Описание события.
+ {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}Адреса эл. почты приглашенных (через запятую).
+ {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}Правило повторения для события.
+ {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}Указывает на то, является ли событие общедоступным или закрытым.
{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}Если событие считается как занятое или как свободное время, доступное для планирования.
+

В разделах ниже указан порядок использования этих намерений.

+ + +

Использование намерения для вставки события

+ +

С помощью намерения {@link android.content.Intent#ACTION_INSERT INSERT} +ваше приложение может отправлять задачи вставки события прямо в приложение «Календарь». +Благодаря этому в файл манифеста вашего приложения не нужно включать разрешение {@link +android.Manifest.permission#WRITE_CALENDAR}.

+ + +

Когда пользователи работают с приложением, в котором используется такой подход, приложение отправляет +их в «Календарь» для завершения добавления события. Намерение {@link +android.content.Intent#ACTION_INSERT INSERT} использует дополнительные поля +для предварительного указания в форме сведений о событии в приложении «Календарь». После этого пользователи +могут отменить событие, отредактировать форму или сохранить событие в своем +календаре.

+ + + +

Ниже представлен фрагмент кода, в котором на 19 января 2012 г. планируется событие, которое будет проходить с +7:30 до 8:30. Однако следует учесть некоторые моменты, касающиеся этого примера кода.

+ +
    +
  • В качестве URI в нем задается +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}.
  • + +
  • В нем используются дополнительные поля {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} и {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} для предварительного указания в форме +сведений о времени события. Значения времени должны быть указаны в формате UTC и в миллисекундах от +эпохи.
  • + +
  • В нем используется дополнительное поле {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} +для предоставления списка участников, разделенных запятыми (их адреса эл. почты).
  • + +
+
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2012, 0, 19, 7, 30);
+Calendar endTime = Calendar.getInstance();
+endTime.set(2012, 0, 19, 8, 30);
+Intent intent = new Intent(Intent.ACTION_INSERT)
+        .setData(Events.CONTENT_URI)
+        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
+        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
+        .putExtra(Events.TITLE, "Yoga")
+        .putExtra(Events.DESCRIPTION, "Group class")
+        .putExtra(Events.EVENT_LOCATION, "The gym")
+        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
+        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
+startActivity(intent);
+
+ +

Использование намерения для редактирования события

+ +

Событие можно отредактировать напрямую, как описано в разделе Обновление событий. Благодаря намерению {@link +android.content.Intent#ACTION_EDIT EDIT} приложение, +у которого нет разрешения, может делегировать редактирование события приложению «Календарь». +По завершении редактирования события в приложении «Календарь» пользователи возвращаются +в исходное приложение.

Ниже представлен пример намерения, который задает +новый заголовок для указанного события и позволяет пользователям редактировать событие в приложении «Календарь».

+ + +
long eventID = 208;
+Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+Intent intent = new Intent(Intent.ACTION_EDIT)
+    .setData(uri)
+    .putExtra(Events.TITLE, "My New Title");
+startActivity(intent);
+ +

Использование намерений для просмотра данных календаря

+

Поставщик календаря позволяет использовать намерение {@link android.content.Intent#ACTION_VIEW VIEW} двумя различными способами:

+
    +
  • Открытие приложения «Календарь» на определенной дате.
  • +
  • Просмотр события.
  • + +
+

Ниже представлен пример открытия приложения «Календарь» на определенной дате.

+
// A date-time specified in milliseconds since the epoch.
+long startMillis;
+...
+Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
+builder.appendPath("time");
+ContentUris.appendId(builder, startMillis);
+Intent intent = new Intent(Intent.ACTION_VIEW)
+    .setData(builder.build());
+startActivity(intent);
+ +

Ниже представлен пример открытия события для его просмотра.

+
long eventID = 208;
+...
+Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+Intent intent = new Intent(Intent.ACTION_VIEW)
+   .setData(uri);
+startActivity(intent);
+
+ + +

Адаптеры синхронизации

+ + +

Существуют лишь незначительные различия в том, как приложение и адаптер синхронизации +получают доступ к поставщику календаря.

+ +
    +
  • Адаптеру синхронизации необходимо указать, что он является таковым, задав для параметра {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} значение true.
  • + + +
  • Адаптеру синхронизации необходимо предоставить {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} и {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} в качестве параметров запроса в URI.
  • + +
  • Адаптер синхронизации имеет доступ на запись к большему числу столбцов, чем приложение или виджет. + Например, приложение может изменять только некоторые характеристики календаря, +такие как название, отображаемое имя, настройки видимости и +синхронизации. Тогда как адаптер синхронизации имеет доступ не только к этим столбцам, но и ко многим другим его характеристикам, +таким как цвет календаря, часовой пояс, уровень доступа, местоположение и т. д. +Однако адаптер синхронизации ограничен задаваемыми им параметрами ACCOUNT_NAME и +ACCOUNT_TYPE.
+ +

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

+
 static Uri asSyncAdapter(Uri uri, String account, String accountType) {
+    return uri.buildUpon()
+        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
+        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
+        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
+ }
+
+

Пример реализации адаптера синхронизации (который не связан с приложением «Календарь») представлен в статье, посвященной +SampleSyncAdapter. diff --git a/docs/html-intl/intl/ru/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/ru/guide/topics/providers/contacts-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..4d07856f250ed4252dca47b98ba925294c6f9b18 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=Поставщик контактов +@jd:body +

+
+

Краткое описание

+
    +
  • Репозиторий Android с пользовательскими данными.
  • +
  • + Синхронизация со службами в Интернете. +
  • +
  • + Интеграция с потоками данных из социальных сетей. +
  • +
+

Содержание документа

+
    +
  1. + Структура поставщика контактов +
  2. +
  3. + Необработанные контакты +
  4. +
  5. + Данные +
  6. +
  7. + Контакты +
  8. +
  9. + Данные, полученные от адаптеров синхронизации +
  10. +
  11. + Требуемые разрешения +
  12. +
  13. + Профиль пользователя +
  14. +
  15. + Метаданные поставщика контактов +
  16. +
  17. + Доступ к поставщику контактов +
  18. +
  19. +
  20. + Адаптеры синхронизации поставщика контактов +
  21. +
  22. + Потоки данных из социальных сетей +
  23. +
  24. + Дополнительные возможности поставщика контактов +
  25. +
+

Ключевые классы

+
    +
  1. {@link android.provider.ContactsContract.Contacts}
  2. +
  3. {@link android.provider.ContactsContract.RawContacts}
  4. +
  5. {@link android.provider.ContactsContract.Data}
  6. +
  7. {@code android.provider.ContactsContract.StreamItems}
  8. +
+

Образцы кода по теме

+
    +
  1. + +Диспетчер контактов + +
  2. +
  3. + +Пример адаптера синхронизации +
  4. +
+

См. также:

+
    +
  1. + +Основные сведения о поставщике контента + +
  2. +
+
+
+

+ Поставщик контактов представляет собой эффективный и гибкий компонент Android, который управляет +центральным репозиторием устройства, в котором хранятся пользовательские данные. Поставщик контактов — это источник данных, +которые отображаются в приложении «Контакты» на вашем устройстве. Вы также можете получить доступ к этим данным в своем собственном +приложении и организовать обмен такими данными между устройством и службами в Интернете. Поставщик взаимодействует +с широким набором источников данных и пытается организовать управление как можно большим набором данных о каждом человеке, поэтому +организация поставщика довольно сложная. По этой причине API поставщика +содержит широкий набор классов-контрактов и интерфейсов, отвечающих как за получение данных, так и за их +изменение. +

+

+ В этом руководстве рассматриваются следующие вопросы: +

+
    +
  • + основная структура поставщика; +
  • +
  • + порядок получения данных от поставщика; +
  • +
  • + порядок изменения данных в поставщике; +
  • +
  • + порядок записи адаптера синхронизации для синхронизации данных, полученных с вашего сервера, с данными в +поставщике контактов. +
  • +
+

+ При изучении данного материала подразумевается, что вы уже знакомы с основами поставщиков контента Android. Дополнительные сведения +о поставщиках контента Android представлены в руководстве +Основные +сведения о поставщике контента. Пример +адаптера синхронизации +служит примером использования такого приложения для обмена данными между поставщиком +контактов и приложением, размещенным в веб-службах Google. +

+

Структура поставщика контактов

+

+ Поставщик контактов представляет собой поставщик контента Android. Он содержит в себе три типа +данных о пользователе, каждый из которых указан в отдельной таблице, предоставляемой поставщиком, +как показано на рисунке 1. +

+ +

+ Рисунок 1. Структура таблицы поставщика контактов. +

+

+ В качестве имен этих трех таблиц обычно используются названия соответствующих классов-контрактов. Эти классы +определяют константы для URI контента, названий столбцов и значений в столбцах этих таблиц. +

+
+
+ Таблица {@link android.provider.ContactsContract.Contacts} +
+
+ Строки в этой таблице содержат данные о разных пользователях, полученные путем агрегации строк необработанных контактов. +
+
+ Таблица {@link android.provider.ContactsContract.RawContacts} +
+
+ Строки в этой таблице содержат сводные данные о пользователе, относящиеся к пользовательскому аккаунту и его типу. +
+
+ Таблица {@link android.provider.ContactsContract.Data} +
+
+ Строки в этой таблице содержат сведения о необработанных контактах, такие как адреса эл. почты или номера телефонов. +
+
+

+ Другие таблицы, представленные классами-контрактами в {@link android.provider.ContactsContract}, +представляют собой вспомогательные таблицы, которые поставщик контактов использует для управления своими операциями или поддержки +определенных функций, имеющихся в приложении устройства «Контакты» или приложениях для телефонной связи. +

+

Необработанные контакты

+

+ Необработанный контакт представляет собой данные о пользователе, поступающие из одного аккаунта определенного +типа. Поскольку в качестве источника данных о пользователе в поставщике контактов может выступать сразу несколько онлайн-служб, +для одного и того же пользователя в поставщике контактов может существовать несколько необработанных контактов. + Это также позволяет пользователю объединять пользовательские данные из нескольких аккаунтов +одного и того же типа. +

+

+ Большая часть данных необработанного контакта не хранится в таблице +{@link android.provider.ContactsContract.RawContacts}. Вместо этого они хранятся в одной или нескольких +строках в таблице {@link android.provider.ContactsContract.Data}. В каждой строке данных имеется +столбец {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID}, +в котором содержится значение {@code android.provider.BaseColumns#_ID RawContacts._ID} его +родительской строки{@link android.provider.ContactsContract.RawContacts}. +

+

Важные столбцы необработанных контактов

+

+ В таблице 1 указаны столбцы таблицы {@link android.provider.ContactsContract.RawContacts}, +которые имеют большое значение. Обязательно ознакомьтесь с примечаниями, приведенными после этой таблицы. +

+

+ Таблица 1. Важные столбцы необработанных контактов. +

+ + + + + + + + + + + + + + + + + + + + +
Название столбцаИспользованиеПримечания
+ {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + + Имя аккаунта для типа аккаунта, который выступает в роли источника данных для необработанного контакта. + Например, имя аккаунта Google является одним из эл. адресов почты Gmail +владельца устройства. Дополнительные сведения +см. в следующей записи +{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}. + + Формат этого имени зависит от типа аккаунта. Это не обязательно +адрес эл. почты. +
+ {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + + Тип аккаунта, который выступает в роли источника данных для необработанного контакта. Например, тип аккаунта +Google — com.google. Всегда указывайте тип аккаунта +и идентификатор домена, которым вы владеете или управляете. Это позволит гарантировать уникальность +типа вашего аккаунта. + + У типа аккаунта, выступающего в роли источника данных контактов, обычно имеется связанный с ним адаптер синхронизации, +который синхронизирует данные с поставщиком контактов. +
+ {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + + Флаг deleted для необработанного контакта. + + Этот флаг позволяет поставщику контактов хранить строку внутри себя до тех пор, +пока адаптеры синхронизации не смогут удалить эту строку с серверов, а затем +удалить ее из репозитория. +
+

Примечания

+

+ Ниже представлены важные примечания к таблице +{@link android.provider.ContactsContract.RawContacts}. +

+
    +
  • + Имя необработанного контакта не хранится в его строке в таблице +{@link android.provider.ContactsContract.RawContacts}. Вместо этого оно хранится в +таблице {@link android.provider.ContactsContract.Data} +в строке {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}. У необработанного контакта +имеется в таблице {@link android.provider.ContactsContract.Data} только одна строка такого типа. +
  • +
  • + Внимание! Чтобы использовать в строке необработанного контакта данные собственного аккаунта, строку +, сначала необходимо зарегистрировать его в классе {@link android.accounts.AccountManager}. Для этого предложите +пользователям добавить тип аккаунта и его имя в список аккаунтов. Если +не сделать этого, поставщик контактов автоматически удалит вашу строку необработанного контакта. +

    + Например, если необходимо, чтобы в вашем приложении хранились данные контактов для веб-службы +в домене {@code com.example.dataservice}, и аккаунт пользователя службы + выглядит следующим образом:{@code becky.sharp@dataservice.example.com}, то пользователю сначала необходимо добавить +«тип» аккаунта ({@code com.example.dataservice}) и его «имя» +({@code becky.smart@dataservice.example.com}) прежде чем ваше приложение сможет добавлять строки необработанных контактов. + Это требование можно указать в документации для пользователей, а также можно предложить +пользователю добавить тип и имя своего аккаунта, либо реализовать оба этих варианта. Более подробно типы и имена аккаунтов +рассматриваются в следующем разделе. +

  • +
+

Источники данных необработанных контактов

+

+ Чтобы понять, что такое необработанный контакт, рассмотрим пример с пользователем Emily Dickinson, на устройстве которой имеется три +следующих аккаунта: +

+
    +
  • emily.dickinson@gmail.com;
  • +
  • emilyd@gmail.com;
  • +
  • Аккаунт belle_of_amherst в Twitter
  • +
+

+ Она включила функцию Синхронизировать контакты для всех трех этих аккаунтов +в настройках Аккаунты. +

+

+ Предположим, что Emily Dickinson открывает браузер, входит в Gmail под именем +emily.dickinson@gmail.com, затем открывает +Контакты и добавляет новый контакт Thomas Higginson. Позже она снова входит в Gmail под именем +emilyd@gmail.com и отправляет письмо пользователю Thomas Higginson, который автоматически +добавляется в ее контакты. Также она подписана на новости от colonel_tom (аккаунт пользователя Thomas Higginson в Twitter) в +Twitter. +

+

+ В результате этих действий поставщик контактов создает три необработанных контакта: +

+
    +
  1. + Необработанный контакт Thomas Higginson связан с аккаунтом emily.dickinson@gmail.com. + Тип этого аккаунта — Google. +
  2. +
  3. + Второй необработанный контакт Thomas Higginson связан с аккаунтом emilyd@gmail.com. + Тип этого аккаунта — также Google. Второй необработанный контакт создается даже в том случае, +если его имя полностью совпадает с именем предыдущего контакта, поскольку первый +был добавлен для другого аккаунта. +
  4. +
  5. + Третий необработанный контакт Thomas Higginson связан с аккаунтом belle_of_amherst. Тип этого аккаунта +— Twitter. +
  6. +
+

Данные

+

+ Как уже указывалось ранее, данные необработанного контакта +хранятся в строке {@link android.provider.ContactsContract.Data}, которая связана со значением +_ID необработанного контакта. Благодаря этому для одного необработанного контакта может существовать несколько экземпляров +одного и того же типа данных (например, адресов эл. почты или номеров телефонов). Например, если у контакта +Thomas Higginson для аккаунта {@code emilyd@gmail.com} (строка необработанного контакта Thomas Higginson, +связанная с учетной записью Google emilyd@gmail.com) имеется адрес эл. почты +thigg@gmail.com и служебный адрес эл. почты +thomas.higginson@gmail.com, то поставщик контактов сохраняет две строки +адреса эл. почты и связывает их с этим необработанным контактом. +

+

+ Обратите внимание, что в этой таблице хранятся данные разных типов. Строки со сведениями об отображаемом имени, +номере телефона, адресе эл. почты, почтовом адресе, фото и веб-сайте хранятся в таблице +{@link android.provider.ContactsContract.Data}. Для упрощения управления этими данными в таблице +{@link android.provider.ContactsContract.Data} предусмотрены столбцы с описательными именами, +а также другие столбцы с универсальными именами. Содержимое в столбце с описательным именем имеет то же значение, +независимо от типа данных в строке, тогда как содержимое столбца с универсальным именем +может иметь разное значение в зависимости от типа данных. +

+

Описательные имена столбцов

+

+ Вот некоторые примеры столбцов с описательными именами: +

+
+
+ {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} +
+
+ Значение в столбце _ID необработанного контакта для этих данных. +
+
+ {@link android.provider.ContactsContract.Data#MIMETYPE} +
+
+ Тип данных, хранящихся в этой строке, в виде настраиваемого типа MIME. Поставщик контактов +использует типы MIME, заданные в подклассах класса +{@link android.provider.ContactsContract.CommonDataKinds}. Эти типы MIME являются открытыми, и +их может использовать любое приложение или адаптер синхронизации, поддерживающие работу с поставщиком контактов. +
+
+ {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} +
+
+ Если этот тип данных для необработанного контакта встречается несколько раз, то +столбец {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} помечает флагом +строки данных, в которых содержатся основные данные для этого типа. Например, если +пользователь нажал и удерживает номер телефона контакта и выбрал параметр Использовать по умолчанию, +то в столбце {@link android.provider.ContactsContract.Data} с этим номером телефона +в соответствующем столбце {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} задается +значение, отличное от нуля. +
+
+

Универсальные имена столбцов

+

+ Существует 15 общедоступных столбцов с универсальными именами (DATA1DATA15) +и четыре дополнительных столбца +(SYNC1SYNC4), которые используются только адаптерами +синхронизации. Константы столбцов с универсальными именами применяются всегда, независимо от типа данных, +содержащихся в столбце. +

+

+ Столбец DATA1 является индексируемым. Поставщик контактов всегда использует этот столбец для +данных, которые, как он ожидает, будут наиболее часто являться целевыми в запросах. Например, +в строке с адресами эл. почты в этом столбце указывается фактический адрес эл. почты. +

+

+ Обычно столбец DATA15 зарезервирован для данных больших двоичных объектов +(BLOB), таких как миниатюры фотографий. +

+

Имена столбцов по типам строк

+

+ Для упрощения работы со столбцами определенного типа строк в поставщике контактов +также предусмотрены константы для имен столбцов по типам строк, которые определены в подклассах класса +{@link android.provider.ContactsContract.CommonDataKinds}. Эти константы просто +присваивают одному и тому же имени столбца различные константы, что позволяет вам получать доступ к данным в строке +определенного типа. +

+

+ Например, класс {@link android.provider.ContactsContract.CommonDataKinds.Email} определяет +константы имени столбца для строки {@link android.provider.ContactsContract.Data}, +в которой имеется тип MIME +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE}. В этом классе содержится константа +{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} для столбца адреса +эл. почты. Фактическое значение +{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} — data1, которое совпадает +с универсальным именем столбца. +

+

+ Внимание! Не добавляйте свои настраиваемые данные в таблицу +{@link android.provider.ContactsContract.Data} с помощью строки, в которой имеется один из +предварительно заданных поставщиком типов MIME. В противном случае вы можете потерять данные или вызвать неполадки +в работе поставщика. Например, не следует добавлять строку с типом MIME +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE}, в которой в столбце +DATA1 вместо адреса эл. почты содержится имя пользователя. Если вы укажете в строке собственный настраиваемый тип MIME, вы можете свободно +указывать собственные имена столбцов по типам строк и использовать их так, как пожелаете. +

+

+ На рисунке 2 показано, как столбцы с описательными именами и столбцы данных отображаются в строке +{@link android.provider.ContactsContract.Data}, а также как имена столбцов по типу строк «накладываются» +на универсальные имена столбцов. +

+How type-specific column names map to generic column names +

+ Рисунок 2. Имена столбцов по типам строк и универсальные имена столбцов. +

+

Классы имен столбцов по типам строк

+

+ В таблице 2 перечислены наиболее часто используемые классы имен столбцов по типам строк. +

+

+ Таблица 2. Классы имен столбцов по типам строк

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Класс сопоставленияТип данныхПримечания
{@link android.provider.ContactsContract.CommonDataKinds.StructuredName}Данные об имени необработанного контакта, связанного с этой строкой данных.У необработанного контакта имеется только одна строка такого типа.
{@link android.provider.ContactsContract.CommonDataKinds.Photo}Основная фотография необработанного контакта, связанного с этой строкой данных.У необработанного контакта имеется только одна строка такого типа.
{@link android.provider.ContactsContract.CommonDataKinds.Email}Адрес эл. почты необработанного контакта, связанного с этой строкой данных.У необработанного контакта может быть несколько адресов эл. почты.
{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}Почтовый адрес необработанного контакта, связанного с этой строкой данных.У необработанного контакта может быть несколько почтовых адресов.
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}Идентификатор, с помощью которого необработанный контакт связан с одной из групп в поставщике контактов. + Группы представляют собой необязательный компонент для типа аккаунта и имени аккаунта. Дополнительные сведения о +группах представлены в разделе Группы контактов. +
+

Контакты

+

+ Поставщик контактов объединяет строки с необработанными контактами для всех типов аккаунтов и имен аккаунтов +для создания контакта. Это позволяет упростить отображение и изменение всех данных, +собранных в отношении пользователя. Поставщик контактов управляет процессом создания строк +контактов и агрегированием необработанных контактов, у которых имеется строка контакта. Ни приложениям, ни +адаптерам синхронизации не разрешается добавлять контакты, а некоторые столбцы в строке контакта доступны только для чтения. +

+

+ Примечание. Если попытаться добавить контакт в поставщик контактов с помощью метода +{@link android.content.ContentResolver#insert(Uri,ContentValues) insert()}, будет выдано исключение +{@link java.lang.UnsupportedOperationException}. Если вы попытаетесь обновить столбец, +доступный только для чтения, это действие будет проигнорировано. +

+

+ При добавлении нового необработанного контакта, +который не соответствует ни одному из существующих контактов, поставщик контактов создает новый контакт. Поставщик поступает аналогично в случае, если +данные в строке существующего необработанного контакта изменяются таким образом, что они больше не соответствуют контакту, +с которым они ранее были связаны. При создании приложением или адаптером синхронизации нового контакта, +который соответствует существующему контакту, то новый контакт объединяется с +существующим контактом. +

+

+ Поставщик контактов связывает строку контакта с его строками необработанного контакта посредством столбца +_ID строки контакта в таблице +{@link android.provider.ContactsContract.Contacts Contacts}. Столбец CONTACT_ID в таблице необработанных контактов +{@link android.provider.ContactsContract.RawContacts} содержит значения _ID для +строки контактов, связанной с каждой строкой необработанных контактов. +

+

+ В таблице {@link android.provider.ContactsContract.Contacts} также имеется столбец +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY}, который выступает в роли +«постоянной ссылки» на строку контакта. Поскольку поставщик контактов автоматически сохраняет контакты, +в ответ на агрегирование или синхронизацию он может изменить значение {@code android.provider.BaseColumns#_ID} +строки контакта. Даже если это произойдет, URI контента +{@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, объединенный с +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} контакта, будет +по-прежнему указывать на строку контакта, поэтому вы можете смело использовать +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} +для сохранения ссылок на «избранные» контакты и др. Столбец имеет собственный формат, +который не связан с форматом столбца{@code android.provider.BaseColumns#_ID}. +

+

+ На рисунке 3 показаны взаимосвязи этих трех основных таблиц друг с другом. +

+Contacts provider main tables +

+ Рисунок 3. Взаимосвязи между таблицами контактов, необработанных контактов и сведений. +

+

Данные, полученные от адаптеров синхронизации

+

+ Пользователь вводит данные контактов прямо на устройстве, однако данные также поступают в поставщик контактов +из веб-служб посредством адаптеров синхронизации, что позволяет автоматизировать +обмен данными между устройством и службами в Интернете. Адаптеры синхронизации выполняются в фоновом режиме под +управлением системы, и для управления данными они вызывают методы +{@link android.content.ContentResolver}. +

+

+ В Android веб-служба, с которой работает адаптер синхронизации, определяется по типу аккаунта. + Каждый адаптер синхронизации работает с одним типом аккаунта, однако он может поддерживать несколько имен аккаунтов +такого типа. Вкратце типы и имена аккаунтов рассматриваются +в разделе Источники данных необработанных контактов. Указанные ниже определения позволяют +более точно охарактеризовать связь между типами и именами аккаунтов и адаптерами синхронизации и службами. +

+
+
+ Тип аккаунта +
+
+ Определяет службу, в которой пользователь хранит данные. В большинстве случаев для работы со +службой пользователю необходимо пройти проверку подлинности. Например, Google Контакты представляет собой тип аккаунтов, обозначаемый кодом +google.com. Это значение соответствует типу аккаунта, используемого +{@link android.accounts.AccountManager}. +
+
+ Имя аккаунта +
+
+ Определяет конкретный аккаунт или имя для входа для типа аккаунта. Аккаунты «Контакты Google» +— это то же, что и аккаунты Google, в качестве имени которых используется адрес эл. почты. + В других службах может использоваться имя пользователя, состоящее из одного слова, или числовой идентификатор. +
+
+

+ Типы аккаунтов не обязательно должны быть уникальными. Пользователь может создать несколько аккаунтов Google Контакты +и загрузить данные из них в поставщик контактов; это может произойти в случае, если у пользователя имеется один набор +персональных контактов для личного аккаунта, и другой набор — для служебного аккаунта. Имена +аккаунтов обычно уникальные. Вместе они формируют определенный поток данных между поставщиком контактов +и внешними службами. +

+

+ Если необходимо передать данные из службы в поставщик контактов, необходимо создать +собственный адаптер синхронизации. Дополнительные сведения об этом представлены в разделе +Адаптеры синхронизации поставщика контактов. +

+

+ На рисунке 4 показано, какую роль выполняет поставщик контактов в потоке передачи данных +о пользователях. В области, отмеченной на рисунке как sync adapters, каждый адаптер промаркирован в соответствии с поддерживаемым им типом аккаунтов. +

+Flow of data about people +

+ Рисунок 4. Поток передачи данных через поставщика контактов. +

+

Требуемые разрешения

+

+ Приложения, которым требуется доступ к поставщику контактов, +должны запросить указанные ниже разрешения. +

+
+
Доступ на чтение одной или нескольких таблиц
+
+ {@link android.Manifest.permission#READ_CONTACTS}, указанный в файле +AndroidManifest.xml с элементом + + <uses-permission> в виде +<uses-permission android:name="android.permission.READ_CONTACTS">. +
+
Доступ на запись в одну или несколько таблиц
+
+ {@link android.Manifest.permission#WRITE_CONTACTS}, указанный в файле +AndroidManifest.xml с элементом + + <uses-permission> в виде +<uses-permission android:name="android.permission.WRITE_CONTACTS">. +
+
+

+ Эти разрешения не распространяются на работу с данными профиля пользователя. Профиль пользователя +и требуемые разрешения для работы с ним рассматриваются в разделе +Профиль пользователя ниже. +

+

+ Помните, что данные о контактах пользователя являются личными и конфиденциальными. Пользователи заботятся о своей конфиденциальности, +поэтому не хотят, чтобы приложения собирали данные о них самих или их контактах. + Если пользователю не до конца ясно, для чего требуется разрешение на доступ к данным контактов, они могут присвоить +вашему приложению низкий рейтинг или вообще откажутся его устанавливать. +

+

Профиль пользователя

+

+ В таблице {@link android.provider.ContactsContract.Contacts} имеется всего одна строка, содержащая +данные профиля для устройства пользователя. Эти данные описывают user устройства, +а не один из контактов пользователя. Строка контактов профиля связана со строкой +необработанных контактов в каждой из систем, в которой используется профиль. + В каждой строке необработанного контакта профиля может содержаться несколько строк данных. Константы для доступа к профилю пользователя +представлены в классе {@link android.provider.ContactsContract.Profile}. +

+

+ Для доступа к профилю пользователя требуются особые разрешения. Кроме разрешений +{@link android.Manifest.permission#READ_CONTACTS} и +{@link android.Manifest.permission#WRITE_CONTACTS}, которые требуются для чтения и записи, для доступа к профилю пользователя необходимы разрешения +{@code android.Manifest.permission#READ_PROFILE} и +{@code android.Manifest.permission#WRITE_PROFILE} +на чтение и запись соответственно. +

+

+ Всегда следует помнить, что профиль пользователя представляет собой конфиденциальную информацию. Разрешение +{@code android.Manifest.permission#READ_PROFILE} предоставляет вам доступ к личной информации на устройстве +пользователя. В описании своего приложения обязательно укажите, для +чего вам требуется доступ к профилю пользователя. +

+

+ Чтобы получить строку контакта с профилем пользователя, вызовите метод +{@link android.content.ContentResolver#query(Uri,String[], String, String[], String) +ContentResolver.query()}. Задайте для URI контента значение +{@link android.provider.ContactsContract.Profile#CONTENT_URI} и не указывайте никаких критериев +выборки. Этот URI контента также можно использовать в качестве основного URI для получения +необработанных контактов или данных профиля. Ниже представлен фрагмент кода для получения данных профиля. +

+
+// Sets the columns to retrieve for the user profile
+mProjection = new String[]
+    {
+        Profile._ID,
+        Profile.DISPLAY_NAME_PRIMARY,
+        Profile.LOOKUP_KEY,
+        Profile.PHOTO_THUMBNAIL_URI
+    };
+
+// Retrieves the profile from the Contacts Provider
+mProfileCursor =
+        getContentResolver().query(
+                Profile.CONTENT_URI,
+                mProjection ,
+                null,
+                null,
+                null);
+
+

+ Примечание. Если при извлечении несколько строк контактов необходимо определить, +какой из них является профилем пользователя, обратитесь к столбцу +{@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} этой строки. Если в этом столбце +указано значение «1», то в это строке находится профиль пользователя. +

+

Метаданные поставщика контактов

+

+ Поставщик контактов управляет данными, которые используются для отслеживания состояния данных контакта в +репозитории. Эти метаданные о репозитории хранятся в различных местах, включая +строки необработанных контактов, данные и строки таблицы контактов, таблицу +{@link android.provider.ContactsContract.Settings} и таблицу +{@link android.provider.ContactsContract.SyncState}. В таблице ниже +указаны значения каждого из этих фрагментов метаданных. +

+

+ Таблица 3. Метаданные в поставщике контактов

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ТаблицаСтолбецЗначенияОписание
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#DIRTY}«0» — с момента последней синхронизации изменения не вносились. + Служит для отметки необработанных контактов, которые были изменены на устройстве и которые необходимо снова синхронизировать с +сервером. Значение устанавливается автоматически поставщиком контактов при обновлении строк приложениями +Android. +

+ Адаптеры синхронизации, которые вносят изменения в необработанные контакты или таблицы данных, должны всегда добавлять к используемому ими URI +контента строку +{@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER}. Это позволяет предотвратить пометку таких строк поставщиком как «грязных». + В противном случае изменения, внесенные адаптером синхронизации, будут рассматриваться как локальные изменения, +которые подлежат отправке на сервер, даже если источником таких изменений был сам сервер. +

+
«1» — с момента последней синхронизации были внесены изменения, которые необходимо отразить и на сервере.
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#VERSION}Номер версии этой строки. + Поставщик контактов автоматически увеличивает это значение при каждом +изменении в строке или в связанных с ним данных. +
{@link android.provider.ContactsContract.Data}{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}Номер версии этой строки. + Поставщик контактов автоматически увеличивает это значение при каждом +изменении в строке данных. +
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} + Строковое значение, которое служит уникальным идентификатором данного необработанного контакта в аккаунте, +в котором он был создан. + + Когда адаптер синхронизации создает новый необработанный контакт, в этом столбце следует указать +уникальный идентификатор этого необработанного контакта на сервере. Когда же приложение Android создает +новый необработанный контакт, то приложению следует оставить этот столбец пустым. Это служит сигналом для +адаптера синхронизации, чтобы создать новый необработанный контакт на сервере и +получить значение {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}. +

+ В частности, идентификатор источника должен быть уникальным для каждого типа аккаунта +и неизменным при синхронизации. +

+
    +
  • + Уникальный: у каждого необработанного контакта для аккаунта должен быть свой собственный идентификатор источника. Если +не применить это требование, у вас обязательно возникнут проблемы с приложением «Контакты». + Обратите внимание, что два необработанных контакта одного и того же типа аккаунта могут +иметь одинаковый идентификатор источника. Например, необработанный контакт Thomas Higginson для +аккаунта {@code emily.dickinson@gmail.com} может иметь такой же +идентификатор источника, что и контакт Thomas Higginson для аккаунта +{@code emilyd@gmail.com}. +
  • +
  • + Стабильный: идентификаторы источников представляют собой неизменную часть данных онлайн-службы для +необработанного контакта. Например, если пользователь выполняет повторную синхронизацию после очистки хранилища контактов в +настройках приложения, идентификаторы источников восстановленных необработанных контактов должны быть такими же, +как и прежде. Если не применить это требование, перестанут +работать ярлыки. +
  • +
+
{@link android.provider.ContactsContract.Groups}{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}«0» — контакты, представленные в этой группе, не должны отображаться в интерфейсах пользователя приложений Android. + Этот столбец предназначен для сведений о совместимости с серверами, позволяющими пользователям скрывать контакты +в определенных группах. +
«1» — контакты, представленные в этой группе, могут отображаться в интерфейсах пользователя приложений.
{@link android.provider.ContactsContract.Settings} + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} + «0» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе, +не отображаются в интерфейсах пользователя приложений Android. + + По умолчанию контакты скрыты, если ни один из их необработанных контактов не принадлежит группе +(принадлежность необработанного контакта к группе указывается в одном или нескольких строках +{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} +в таблице {@link android.provider.ContactsContract.Data}). + Установив этот флаг в строке таблицы {@link android.provider.ContactsContract.Settings} +для типа аккаунта и имени аккаунта, вы можете принудительно сделать видимыми контакты, не принадлежащие какой-либо группе. + Один из вариантов использования этого флага — отображение контактов с серверов, на которых не используются группы. +
+ «1» — для этого аккаунта и аккаунтов этого типа контакты, не принадлежащие к группе, +отображаются в интерфейсах пользователя приложений Android. +
{@link android.provider.ContactsContract.SyncState}(все) + Эта таблица используется для хранения метаданных для вашего адаптера синхронизации. + + С помощью этой таблицы вы можете на постоянной основе хранить на устройстве сведения о состоянии синхронизации и другие связанные с +синхронизацией данные. +
+

Доступ к поставщику контактов

+

+ В этом разделе рассматриваются инструкции по получению доступа к данным из поставщика контактов, в частности, +следующие: +

+
    +
  • + запросы объектов; +
  • +
  • + пакетное изменение; +
  • +
  • + получение и изменение данных с помощью намерений; +
  • +
  • + целостность данных. +
  • +
+

+ Дополнительные сведения о внесении изменений с помощью адаптеров синхронизации представлены в разделе +Адаптеры синхронизации поставщика контактов. +

+

Запрос объектов

+

+ Таблицы поставщика контактов имеют иерархическую структуру, поэтому зачастую полезно +извлекать строку и все связанные с ней ее дочерние строки. Например, для отображения +всей информации о пользователе вы, возможно, захотите извлечь все строки +{@link android.provider.ContactsContract.RawContacts} для одной строки +{@link android.provider.ContactsContract.Contacts} или все строки +{@link android.provider.ContactsContract.CommonDataKinds.Email} для одной строки +{@link android.provider.ContactsContract.RawContacts}. Чтобы упростить этот процесс, в поставщике контактов имеются +объекты, которые выступают в роли соединителей базы данных между +таблицами. +

+

+ Объект представляет собой подобие таблицы, состоящей из выбранных столбцов родительской таблицы и ее дочерней таблицы. + При запросе объекта вы предоставляете проекцию и критерии поиска на основе доступных в +объекте столбцов. В результате вы получаете объект {@link android.database.Cursor}, в котором +содержится одна строка для каждой извлеченной строки дочерней таблицы. Например, если запросить объект +{@link android.provider.ContactsContract.Contacts.Entity} для имени контакта и все строки +{@link android.provider.ContactsContract.CommonDataKinds.Email} для всех необработанных контактов, +соответствующих этому имени, вы получите объект {@link android.database.Cursor}, содержащий по одной строке +для каждой строки {@link android.provider.ContactsContract.CommonDataKinds.Email}. +

+

+ Использование объектов упрощает запросы. С помощью объекта можно извлечь сразу все данные для +контакта или необработанного контакта, вместо того, чтобы сначала делать запрос к родительской таблице для получения +идентификатора и последующего запроса к дочерней таблице с использованием полученного идентификатора. Кроме того, поставщик контактов обрабатывает запрос +объекта за одну транзакцию, что гарантирует внутреннюю согласованность полученных +данных. +

+

+ Примечание. Объект обычно содержит не все столбцы +родительской и дочерней таблиц. Если вы попытаетесь изменить имя столбца, который отсутствует в списке +констант имени столбца для объекта, вы получите {@link java.lang.Exception}. +

+

+ Ниже представлен пример кода для получения всех строк необработанного контакта для контакта. Это фрагмент +более крупного приложения, в котором имеются две операции: «основная» и «для получения сведений». Основная операция +служит для получения списка строк контактов; при выборе пользователем контакта операция отправляет его идентификатор в операцию +для получения сведений. Операция для получения сведений использует объект {@link android.provider.ContactsContract.Contacts.Entity} +для отображения всех строк данных, полученных ото всех необработанных контактов, которые связаны с выбранным +контактом. +

+

+ Вот фрагмент кода операции «для получения сведений»: +

+
+...
+    /*
+     * Appends the entity path to the URI. In the case of the Contacts Provider, the
+     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
+     */
+    mContactUri = Uri.withAppendedPath(
+            mContactUri,
+            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
+
+    // Initializes the loader identified by LOADER_ID.
+    getLoaderManager().initLoader(
+            LOADER_ID,  // The identifier of the loader to initialize
+            null,       // Arguments for the loader (in this case, none)
+            this);      // The context of the activity
+
+    // Creates a new cursor adapter to attach to the list view
+    mCursorAdapter = new SimpleCursorAdapter(
+            this,                        // the context of the activity
+            R.layout.detail_list_item,   // the view item containing the detail widgets
+            mCursor,                     // the backing cursor
+            mFromColumns,                // the columns in the cursor that provide the data
+            mToViews,                    // the views in the view item that display the data
+            0);                          // flags
+
+    // Sets the ListView's backing adapter.
+    mRawContactList.setAdapter(mCursorAdapter);
+...
+@Override
+public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+
+    /*
+     * Sets the columns to retrieve.
+     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
+     * DATA1 contains the first column in the data row (usually the most important one).
+     * MIMETYPE indicates the type of data in the data row.
+     */
+    String[] projection =
+        {
+            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
+            ContactsContract.Contacts.Entity.DATA1,
+            ContactsContract.Contacts.Entity.MIMETYPE
+        };
+
+    /*
+     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
+     * contact collated together.
+     */
+    String sortOrder =
+            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
+            " ASC";
+
+    /*
+     * Returns a new CursorLoader. The arguments are similar to
+     * ContentResolver.query(), except for the Context argument, which supplies the location of
+     * the ContentResolver to use.
+     */
+    return new CursorLoader(
+            getApplicationContext(),  // The activity's context
+            mContactUri,              // The entity content URI for a single contact
+            projection,               // The columns to retrieve
+            null,                     // Retrieve all the raw contacts and their data rows.
+            null,                     //
+            sortOrder);               // Sort by the raw contact ID.
+}
+
+

+ По завершении загрузки {@link android.app.LoaderManager} выполняет обратный вызов метода +{@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) +onLoadFinished()}. Одним их входящих аргументов для этого метода является +{@link android.database.Cursor} с результатом запроса. В своем приложении вы можете +получить данные из этого объекта {@link android.database.Cursor} для его отображения или дальнейшей работы с ним. +

+

Пакетное изменение

+

+ При каждой возможности данные в поставщике контактов следует вставлять, обновлять и удалять в +«пакетном режиме» путем создания {@link java.util.ArrayList} из объектов +{@link android.content.ContentProviderOperation} и вызова метода +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Поскольку +поставщик контактов выполняет все операции в методе +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} за одну +транзакцию, ваши изменения всегда будут находиться в рамках репозитория контактов и +всегда будут согласованными. Пакетное изменение также упрощает вставку необработанного контакта одновременно с его подробными +данными. +

+

+ Примечание. Чтобы изменить отдельный необработанный контакт, рекомендуется отправить намерение в +приложение для работы с контактами на устройстве вместо обработки изменения в вашем приложении. +Более подробно эта операция описана в разделе +Получение и изменение данных с помощью намерений. +

+

Пределы

+

+ Пакетное изменение большого количества операций может заблокировать выполнение других процессов, +что может негативно сказаться на работе пользователя с приложением. Чтобы упорядочить все необходимые изменения +в рамках как можно меньшего количества отдельных списков и при этом предотвратить +блокирование работы системы, следует задать пределы для одной или нескольких операций. + Предел представляет собой объект {@link android.content.ContentProviderOperation}, в качестве значения параметра +{@link android.content.ContentProviderOperation#isYieldAllowed()} которого задано +true. При достижении поставщиком контактов предела он приостанавливает свою работу, +чтобы могли выполняться другие процессы, и закрывает текущую транзакцию. Когда поставщик запускается снова, он +выполняет следующую операцию в {@link java.util.ArrayList} и запускает +новую транзакцию. +

+

+ Использование пределов позволяет за один вызов метода +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} выполнять более одной транзакции. По этой причине +вам следует задать предел для последней операции с набором связанных строк. + Например, необходимо задать предел для последней операции в наборе, которая +добавляет строки необработанного контакта и связанные с ним строки данных, или для последней операции с набором строк, связанных +с одним контактом. +

+

+ Пределы также являются единицами атомарных операций. Все операции доступа в промежутке между двумя пределами завершаются либо +успехом, либо сбоем в рамках одной единицы. Если любой из пределов не задан, наименьшей +атомарной операцией считается весь пакет операций. Использование пределов позволяет предотвратить +снижение производительности системы и одновременно обеспечить выполнение набора +операций на атомарном уровне. +

+

Изменение обратных ссылок

+

+ При вставке новой строки необработанного контакта и связанных с ним рядов данных в виде набора объектов +{@link android.content.ContentProviderOperation} вам приходится связывать строки данных +со строкой необработанного контакта путем вставки значения +{@code android.provider.BaseColumns#_ID} необработанного контакта в виде значения +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Однако это значение +недоступно, когда вы создаете{@link android.content.ContentProviderOperation} +для строки данных, поскольку вы еще не применили +{@link android.content.ContentProviderOperation} для строки необработанного контакта. Чтобы обойти это ограничение, +в классе {@link android.content.ContentProviderOperation.Builder} предусмотрен метод +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}. + С помощью этого метода можно вставлять или изменять столбец +с результатом предыдущей операции. +

+

+ В методе {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +предусмотрено два аргумента: +

+
+
+ key +
+
+ Ключ для пары «ключ-значение». Значением этого аргумента должно быть имя столбца +в таблице, которую вы изменяете. +
+
+ previousResult +
+
+ Индекс значения, начинающийся с «0», в массиве объектов +{@link android.content.ContentProviderResult} из метода +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. По мере +выполнения пакетных операций результат каждой операции сохраняется в +промежуточном массиве результатов. Значением previousResult является индекс +одного из этих результатов, который извлекается и хранится со значением +key. Благодаря этому можно вставить новую запись необработанного контакта и получить обратно его значение +{@code android.provider.BaseColumns#_ID}, а затем создать «обратную ссылку» на +значение при добавлении строки {@link android.provider.ContactsContract.Data}. +

+ Целиком весь результат создается при первом вызове метода +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. +Размер результата равен размеру {@link java.util.ArrayList} предоставленных вами объектов +{@link android.content.ContentProviderOperation}. Однако для всех +элементов в массиве результатов присваивается значение null, и при попытке +воспользоваться обратной ссылкой на результат еще не выполненной операции метод +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +выдает {@link java.lang.Exception}. + +

+
+
+

+ Ниже представлены фрагменты кода для вставки нового необработанного контакта и его данных в пакетном режиме. Они включают +код, который задает предел и использует обратную ссылку. Эти фрагменты +представляют собой расширенную версию метода createContacEntry(), который входит в класс +ContactAdder в примере приложения + + Contact Manager. +

+

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

+
+// Creates a contact entry from the current UI values, using the currently-selected account.
+protected void createContactEntry() {
+    /*
+     * Gets values from the UI
+     */
+    String name = mContactNameEditText.getText().toString();
+    String phone = mContactPhoneEditText.getText().toString();
+    String email = mContactEmailEditText.getText().toString();
+
+    int phoneType = mContactPhoneTypes.get(
+            mContactPhoneTypeSpinner.getSelectedItemPosition());
+
+    int emailType = mContactEmailTypes.get(
+            mContactEmailTypeSpinner.getSelectedItemPosition());
+
+

+ В следующем фрагменте кода создается операция для вставки строки необработанного контакта в таблицу +{@link android.provider.ContactsContract.RawContacts}: +

+
+    /*
+     * Prepares the batch operation for inserting a new raw contact and its data. Even if
+     * the Contacts Provider does not have any data for this person, you can't add a Contact,
+     * only a raw contact. The Contacts Provider will then add a Contact automatically.
+     */
+
+     // Creates a new array of ContentProviderOperation objects.
+    ArrayList<ContentProviderOperation> ops =
+            new ArrayList<ContentProviderOperation>();
+
+    /*
+     * Creates a new raw contact with its account type (server type) and account name
+     * (user's account). Remember that the display name is not stored in this row, but in a
+     * StructuredName data row. No other data is required.
+     */
+    ContentProviderOperation.Builder op =
+            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
+            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+

+ Затем код создает строки данных для строк отображаемого имени, телефона и адреса эл. почты. +

+

+ Каждый объект компонента операции использует метод +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +для получения +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Ссылка возвращается +обратно к объекту {@link android.content.ContentProviderResult} из первой операции, +в результате чего добавляется строка необработанного контакта и возвращается его новое значение +{@code android.provider.BaseColumns#_ID}. После этого каждая строка данных автоматически связывается по своему +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} +с новой строкой {@link android.provider.ContactsContract.RawContacts}, которой она принадлежит. +

+

+ Объект {@link android.content.ContentProviderOperation.Builder}, который добавляет строку адреса эл. почты, +помечается флагом с помощью метода {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) +withYieldAllowed()}, который задает предел: +

+
+    // Creates the display name for the new raw contact, as a StructuredName data row.
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * withValueBackReference sets the value of the first argument to the value of
+             * the ContentProviderResult indexed by the second argument. In this particular
+             * call, the raw contact ID column of the StructuredName data row is set to the
+             * value of the result returned by the first operation, which is the one that
+             * actually adds the raw contact row.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to StructuredName
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+
+            // Sets the data row's display name to the name in the UI.
+            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+    // Inserts the specified phone number and type as a Phone data row
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * Sets the value of the raw contact id column to the new raw contact ID returned
+             * by the first operation in the batch.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to Phone
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+
+            // Sets the phone number and type
+            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
+            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+    // Inserts the specified email and type as a Phone data row
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * Sets the value of the raw contact id column to the new raw contact ID returned
+             * by the first operation in the batch.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to Email
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+
+            // Sets the email address and type
+            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
+            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
+
+    /*
+     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
+     * will yield priority to other threads. Use after every set of operations that affect a
+     * single contact, to avoid degrading performance.
+     */
+    op.withYieldAllowed(true);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+

+ В последнем фрагменте кода представлен вызов метода +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} +для вставки нового необработанного контакта и его строк данных. +

+
+    // Ask the Contacts Provider to create a new contact
+    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
+            mSelectedAccount.getType() + ")");
+    Log.d(TAG,"Creating contact: " + name);
+
+    /*
+     * Applies the array of ContentProviderOperation objects in batch. The results are
+     * discarded.
+     */
+    try {
+
+            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+    } catch (Exception e) {
+
+            // Display a warning
+            Context ctx = getApplicationContext();
+
+            CharSequence txt = getString(R.string.contactCreationFailure);
+            int duration = Toast.LENGTH_SHORT;
+            Toast toast = Toast.makeText(ctx, txt, duration);
+            toast.show();
+
+            // Log exception
+            Log.e(TAG, "Exception encountered while inserting contact: " + e);
+    }
+}
+
+

+ С помощью пакетных операций можно также реализоватьоптимистическое управление параллелизмом. +Это метод применения транзакций изменения без необходимости блокировать базовый репозиторий. + Чтобы воспользоваться этим методом, необходимо применить транзакцию и проверить наличие других изменений, +которые могли быть внесены в это же время. Если обнаружится, что имеется несогласованное изменение, +транзакцию можно откатить и выполнить повторно. +

+

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

+

+ Ниже представлен порядок использования оптимистического управления параллелизмом при обновлении одной строки +{@link android.provider.ContactsContract.RawContacts}. +

+
    +
  1. + Извлеките столбец {@link android.provider.ContactsContract.SyncColumns#VERSION} +необработанного контакта, а также другие данные, которые необходимо извлечь. +
  2. +
  3. + Создайте объект {@link android.content.ContentProviderOperation.Builder}, подходящий для +применения ограничения, с помощью метода +{@link android.content.ContentProviderOperation#newAssertQuery(Uri)}. Для URI контента +используйте {@link android.provider.ContactsContract.RawContacts#CONTENT_URI +RawContacts.CONTENT_URI}, +добавив к нему {@code android.provider.BaseColumns#_ID} необработанного контакта. +
  4. +
  5. + Для объекта {@link android.content.ContentProviderOperation.Builder} вызовите метод +{@link android.content.ContentProviderOperation.Builder#withValue(String, Object) +withValue()}, чтобы сравнить столбец {@link android.provider.ContactsContract.SyncColumns#VERSION} +с номером версии, которую вы только что получили. +
  6. +
  7. + Для того же объекта {@link android.content.ContentProviderOperation.Builder} вызовите метод +{@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) +withExpectedCount()}, чтобы убедиться в том, что проверочное утверждение проверяет только одну строка. +
  8. +
  9. + Вызовите метод {@link android.content.ContentProviderOperation.Builder#build()}, чтобы создать объект +{@link android.content.ContentProviderOperation}, затем добавьте этот объект в качестве первого объекта в +{@link java.util.ArrayList}, который вы передаете в метод +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. +
  10. +
  11. + Примените пакетную транзакцию. +
  12. +
+

+ Если в промежутке между считыванием строки и попыткой ее изменения строка необработанного контакта была обновлена другой +операцией, assert {@link android.content.ContentProviderOperation} +завершится сбоем и в выполнении всего пакета операций будет отказано. Можно выбрать повторное выполнение +пакета или выполнить другое действие. +

+

+ В примере кода ниже демонстрируется, как создать assert +{@link android.content.ContentProviderOperation} после запроса одного необработанного контакта с помощью +{@link android.content.CursorLoader}. +

+
+/*
+ * The application uses CursorLoader to query the raw contacts table. The system calls this method
+ * when the load is finished.
+ */
+public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+
+    // Gets the raw contact's _ID and VERSION values
+    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
+    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
+}
+
+...
+
+// Sets up a Uri for the assert operation
+Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
+
+// Creates a builder for the assert operation
+ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
+
+// Adds the assertions to the assert operation: checks the version and count of rows tested
+assertOp.withValue(SyncColumns.VERSION, mVersion);
+assertOp.withExpectedCount(1);
+
+// Creates an ArrayList to hold the ContentProviderOperation objects
+ArrayList ops = new ArrayList<ContentProviderOperationg>;
+
+ops.add(assertOp.build());
+
+// You would add the rest of your batch operations to "ops" here
+
+...
+
+// Applies the batch. If the assert fails, an Exception is thrown
+try
+    {
+        ContentProviderResult[] results =
+                getContentResolver().applyBatch(AUTHORITY, ops);
+
+    } catch (OperationApplicationException e) {
+
+        // Actions you want to take if the assert operation fails go here
+    }
+
+

Получение и изменение данных с помощью намерений

+

+ С помощью отправки намерения в приложение для работы с контактами, которое имеется на устройстве, можно в обход получить доступ +к поставщику контактов. Намерение запускает пользовательский интерфейс приложения на устройстве, посредством которого пользователь +может работать с контактами. Такой тип доступа позволяет пользователю выполнять следующие действия: +

    +
  • выбор контактов из списка и их возврат в приложение для дальнейшей работы;
  • +
  • изменение данных существующего контакта;
  • +
  • вставка нового необработанного контакта для любых других аккаунтов;
  • +
  • удаление контакта или его данных.
  • +
+

+ Если пользователь вставляет или обновляет данные , вы можете сначала собрать эти данные, а затем отправить их вместе +с намерением. +

+

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

+

+ Более подробно общий процесс отправки намерения для получения доступа к поставщику описан в руководстве +Основные сведения о поставщике контента +в разделе «Доступ к данным посредством намерений». В таблице 4 ниже представлены сводные сведения об операциях, +типе MIME и значениях данных, которые используются для доступных задач. Значения +дополнительных данных, которые можно использовать для +{@link android.content.Intent#putExtra(String, String) putExtra()}, указаны в справочной +документации по {@link android.provider.ContactsContract.Intents.Insert}. +

+

+ Таблица 4. Намерения в поставщике контактов. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ЗадачаДействиеДанныеТип MIMEПримечания
Выбор контакта из списка{@link android.content.Intent#ACTION_PICK} + Одно из следующего: +
    +
  • +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI} +(отображение списка контактов); +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI} +(отображение номеров телефонов для необработанного контакта); +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI} +(отображение списка почтовых адресов для необработанного контакта); +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI} +(отображение адресов эл. почты для необработанного контакта). +
  • +
+
+ Не используется + + Отображение списка необработанных контактов или списка данных необработанного контакта (в зависимости от +предоставленного URI контента). +

+ Вызовите метод +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}, +который возвращает URI контента для выбранной строки. URI представлен в форме +URI контента таблицы, к которому добавлен LOOKUP_ID строки. + Приложение для работы с контактами, установленное на устройстве, делегирует этому URI контента +разрешения на чтение и запись, которые действуют в течение всего жизненного цикла вашей операции. Дополнительные сведения представлены в статье +Основные +сведения о поставщике контента. +

+
Вставка нового необработанного контакта{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}Н/Д + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE +RawContacts.CONTENT_TYPE}, тип MIME для набора необработанных контактов. + + Отображение экрана Добавить контакт в приложении для работы с контактами, которое установлено на устройстве. Отображаются +значения дополнительных данных, добавленных вами в намерение. При отправке с помощью метода +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} +URI контента нового добавленного необработанного контакта передается обратно в метод обратного вызова +{@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()} +вашей операции в аргументе {@link android.content.Intent} в поле +data. Чтобы получить значение, вызовите метод {@link android.content.Intent#getData()}. +
Изменение контакта{@link android.content.Intent#ACTION_EDIT} + {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} +контакта. Операция редактора разрешит пользователю изменить любые данные, связанные с +этим контактом. + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE +Contacts.CONTENT_ITEM_TYPE}, один контакт. + Отображение экрана редактирования контакта в приложении для работы с контактами. Отображаются +значения дополнительных данных, добавленных вами в намерение. Когда пользователь нажимает на кнопку Готово для сохранения +внесенных изменений, ваша операция возвращается на передний план. +
Отображение средства выбора, в котором также можно добавлять данные{@link android.content.Intent#ACTION_INSERT_OR_EDIT} + Н/Д + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + + Это намерение также отображает экран средства выбора приложения для работы с контактами. Пользователь +может выбрать контакт для изменения или добавить новый контакт. В зависимости от выбора отображается экран редактирования или добавления контакта, +и отображаются дополнительные данные, +переданные вами в намерение. Если ваше приложение отображает такие данные контакта, как адрес эл. почты или номер телефона, +воспользуйтесь этим намерением, чтобы разрешить пользователю добавлять данные к существующему +контакту. +

+ Примечание. Вам не нужно отправлять значение имени в дополнительные данные этого намерения, +поскольку пользователь всегда выбирает существующее имя или добавляет новое. Кроме того, +если отправить имя, а пользователь решит внести изменения, приложение для работы с контактами +отобразит отправленное вами имя, перезаписав предыдущее значение. Если пользователь +не заметит этого и сохранит изменения, старое значение будет утеряно. +

+
+

+ Приложение для работы с контактами, имеющееся на устройстве, не разрешает вам удалять необработанные контакты или любые его данные с +помощью намерений. Вместо этого для обработки необработанного контакта используйте метод +{@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} +или {@link android.content.ContentProviderOperation#newDelete(Uri) +ContentProviderOperation.newDelete()}. +

+

+ Ниже представлен фрагмент кода для создания и отправки намерения, который вставляет новый +необработанный контакт и его данные. +

+
+// Gets values from the UI
+String name = mContactNameEditText.getText().toString();
+String phone = mContactPhoneEditText.getText().toString();
+String email = mContactEmailEditText.getText().toString();
+
+String company = mCompanyName.getText().toString();
+String jobtitle = mJobTitle.getText().toString();
+
+// Creates a new intent for sending to the device's contacts application
+Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
+
+// Sets the MIME type to the one expected by the insertion activity
+insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+
+// Sets the new contact name
+insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
+
+// Sets the new company and job title
+insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
+insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
+
+/*
+ * Demonstrates adding data rows as an array list associated with the DATA key
+ */
+
+// Defines an array list to contain the ContentValues objects for each row
+ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
+
+
+/*
+ * Defines the raw contact row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues rawContactRow = new ContentValues();
+
+// Adds the account type and name to the row
+rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
+rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
+
+// Adds the row to the array
+contactData.add(rawContactRow);
+
+/*
+ * Sets up the phone number data row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues phoneRow = new ContentValues();
+
+// Specifies the MIME type for this data row (all data rows must be marked by their type)
+phoneRow.put(
+        ContactsContract.Data.MIMETYPE,
+        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+);
+
+// Adds the phone number and its type to the row
+phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
+
+// Adds the row to the array
+contactData.add(phoneRow);
+
+/*
+ * Sets up the email data row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues emailRow = new ContentValues();
+
+// Specifies the MIME type for this data row (all data rows must be marked by their type)
+emailRow.put(
+        ContactsContract.Data.MIMETYPE,
+        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
+);
+
+// Adds the email address and its type to the row
+emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
+
+// Adds the row to the array
+contactData.add(emailRow);
+
+/*
+ * Adds the array to the intent's extras. It must be a parcelable object in order to
+ * travel between processes. The device's contacts app expects its key to be
+ * Intents.Insert.DATA
+ */
+insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
+
+// Send out the intent to start the device's contacts app in its add contact activity.
+startActivity(insertIntent);
+
+

Целостность данных

+

+ Поскольку в репозитории контактов содержится важная и конфиденциальная информация, которая, как ожидает пользователь, верна и +актуальна, в поставщике контактов предусмотрены четко определенные правила для обеспечения целостности данных. При изменении данных контактов вы обязаны +соблюдать эти правила. Вот наиболее +важные из этих правил: +

+
+
+ Всегда добавляйте строку {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} +к каждой добавляемой вами строке {@link android.provider.ContactsContract.RawContacts}. +
+
+ Если в таблице {@link android.provider.ContactsContract.Data} имеется строка {@link android.provider.ContactsContract.RawContacts} +без строки {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}, +то могут возникнуть +проблемы во время агрегирования. +
+
+ Всегда связывайте новые строки {@link android.provider.ContactsContract.Data} с их +родительскими строками {@link android.provider.ContactsContract.RawContacts}. +
+
+ Строка {@link android.provider.ContactsContract.Data}, которая не связана +с {@link android.provider.ContactsContract.RawContacts}, не будет отображаться в +приложении для работы с контактами, имеющемся на устройстве, а также может привести к проблемам с адаптерами синхронизации. +
+
+ Следует изменять данные только тех необработанных контактов, которыми вы владеете. +
+
+ Помните о том, что поставщик контактов обычно управляет данными из +нескольких аккаунтов различных типов или служб в Интернете. Необходимо убедиться в том, что ваше приложение изменяет +или удаляет данные только для тех строк, которые принадлежат вам, а также в том, что оно вставляет данные с использованием только тех типов и имени аккаунта, +которыми вы управляете. +
+
+ Всегда используйте +для центров, URI контента, путей URI, имен столбцов, типов MIME и значений +{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} только те константы, которые определены в классе {@link android.provider.ContactsContract} и его подклассах. +
+
+ Это позволит избежать ошибок. Если какая либо константа устарела, компилятор сообщит об этом +с помощью соответствующего предупреждения. +
+
+

Настраиваемые строки данных

+

+ Создав и используя собственные типы MIME, вы можете вставлять, изменять, удалять и извлекать +в таблице{@link android.provider.ContactsContract.Data} собственные строки данных. Ваши строки +ограничены использованием столбца, который определен в +{@link android.provider.ContactsContract.DataColumns}, однако вы можете сопоставить ваши собственные имена столбцов по типам строк +с именами столбцов по умолчанию. Данные для ваших строк отображаются в приложении для работы с контактами, которое имеется на устройстве, +однако их не удастся изменить или удалить, а также пользователи не смогут +добавить дополнительные данные. Чтобы разрешить пользователям изменять ваши настраиваемые строки данных, необходимо реализовать в вашем приложении операцию +редактора. +

+

+ Для отображения настраиваемых данных укажите файл contacts.xml, содержащий элемент +<ContactsAccountType> и один или несколько его +<ContactsDataKind> дочерних элементов. Дополнительные сведения об этом представлены в разделе +<ContactsDataKind> element. +

+

+ Дополнительные сведения о настраиваемых типах MIME представлены в руководстве +Создание +поставщика контента. +

+

Адаптеры синхронизации поставщика контактов

+

+ Поставщик контактов разработан специально для обработки операций синхронизации +данных контактов между устройством и службой в Интернете. Благодаря этому пользователи +могут загружать существующие данные на новое устройство и отправлять их в новый аккаунт. + Синхронизация также обеспечивает предоставление пользователю всегда актуальных сведений, независимо от +источника их добавления и внесенных в них изменений. Еще одно преимущество синхронизации заключается в том, +что данные контактов доступны даже в том случае, если устройство не подключено к сети. +

+

+ Несмотря на множество различных способов реализации синхронизации данных, в системе Android имеется +подключаемая платформа синхронизации, которая позволяет автоматизировать выполнение следующих задач: +

    + +
  • + проверка доступности сети; +
  • +
  • + планирование и выполнение синхронизации на основе предпочтений пользователя; +
  • +
  • + перезапуск остановленных процессов синхронизации. +
  • +
+

+ Для использования этой платформы вы предоставляете подключаемый модуль адаптера синхронизации. Каждый адаптер синхронизации является уникальным +для службы и поставщика контента, однако способен работать с несколькими аккаунтами в одной службе. В платформе +также предусмотрена возможность использовать несколько адаптеров синхронизации для одной и той же службы или поставщика. +

+

Классы и файлы адаптера синхронизации

+

+ Адаптер синхронизации реализуется как подкласс класса +{@link android.content.AbstractThreadedSyncAdapter} и устанавливается в составе приложения +Android. Система узнает о наличии адаптера синхронизации из элементов в манифесте +вашего приложения, а также из особого файла XML, на который имеется указание в манифесте. В файле XML определяются +тип аккаунта в онлайн-службе и центр поставщика контента, которые вместе служат уникальными +идентификаторами адаптера. Адаптер синхронизации находится в неактивном состоянии до тех пор, пока пользователь не добавит +тип аккаунта и не включит синхронизацию +с поставщиком контента. На этом этапе система вступает в управление адаптером +, при необходимости вызывая его для синхронизации данных между поставщиком контента и сервером. +

+

+ Примечание. Использование типа аккаунта для идентификации адаптера синхронизации +позволяет системе обнаруживать и группировать адаптеры синхронизации, которые обращаются к разным службам +из одной и той же организации. Например, у всех адаптеров синхронизации для онлайн-служб Google +один и тот же тип аккаунта — com.google. Когда пользователи добавляют на свои устройства аккаунт Google, все +установленные адаптеры синхронизации группируются вместе; каждый из адаптеров +синхронизируется только с отдельным поставщиком контента на устройстве. +

+

+ Поскольку в большинстве служб пользователям сначала необходимо подтвердить свою подлинность, прежде чем они смогут получить доступ к данным, +система Android предлагает платформу аутентификации, которая аналогична +платформе адаптера синхронизации и зачастую используется совместно с ней. Платформа аутентификации +использует подключаемые структуры проверки подлинности, которые представляют собой подклассы класса +{@link android.accounts.AbstractAccountAuthenticator}. Такая структура проверяет +подлинность пользователя следующим образом: +

    +
  1. + Сначала собираются сведения об имени пользователя и его пароле или аналогичная информация (учетные данные +пользователя). +
  2. +
  3. + Затем учетные данные оправляются в службу. +
  4. +
  5. + Наконец, изучается ответ, полученный от службы. +
  6. +
+

+ Если служба приняла учетные данные, структура проверки подлинности может +сохранить их для использования в дальнейшем. Благодаря использованию структур проверки подлинности +{@link android.accounts.AccountManager} может предоставить доступ к любым маркерам аутентификации, которые +поддерживает структура проверки подлинности и которые она решает предоставить, например, к маркерам аутентификации OAuth2. +

+

+ Несмотря на то что аутентификация не требуется, она используется большинством служб для работы с контактами. + Тем не менее, вам не обязательно использовать платформу аутентификации Android для проверки подлинности. +

+

Реализация адаптера синхронизации

+

+ Чтобы реализовать адаптер синхронизации для поставщика контактов, начните с создания +приложения Android, которое содержит следующие компоненты. +

+
+
+ Компонент {@link android.app.Service}, который отвечает на запросы системы +для привязки к адаптеру синхронизации. +
+
+ Когда системе требуется запустить синхронизацию, она вызывает метод +{@link android.app.Service#onBind(Intent) onBind()} службы, чтобы получить объект +{@link android.os.IBinder} для адаптера синхронизации. Благодаря этому система +может вызывать методы адаптера между процессами. +

+ В +примере адаптера синхронизации именем класса этой службы является +com.example.android.samplesync.syncadapter.SyncService. +

+
+
+ Адаптер синхронизации, фактически реализованный как конкретный подкласс +класса {@link android.content.AbstractThreadedSyncAdapter}. +
+
+ Этот класс не подходит для загрузки данных с сервера, отправки данных +с устройства и разрешения конфликтов. Основную свою работу адаптер +выполняет в методе {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( +Account, Bundle, String, ContentProviderClient, SyncResult) +onPerformSync()}. Этот класс допускает создание только одного экземпляра. +

+ В +примере адаптера синхронизации адаптер определяется в классе +com.example.android.samplesync.syncadapter.SyncAdapter. +

+
+
+ Подкласс класса {@link android.app.Application}. +
+
+ Этот класс выступает в роли фабрики для единственного экземпляра адаптера синхронизации. Воспользуйтесь методом +{@link android.app.Application#onCreate()}, чтобы создать экземпляр адаптера синхронизации, а затем +предоставьте статический метод get, чтобы возвратить единственный экземпляр в метод +{@link android.app.Service#onBind(Intent) onBind()} службы +адаптера синхронизации. +
+
+ Необязательно: компонент {@link android.app.Service}, который отвечает на запросы +системы для аутентификации пользователей. +
+
+ {@link android.accounts.AccountManager} запускает службу, чтобы начать процесс +аутентификации. Метод {@link android.app.Service#onCreate()} службы создает экземпляр +объекта структуры проверки подлинности. Когда системе требуется запустить аутентификацию аккаунта пользователя для +адаптера синхронизации приложения, она вызывает метод +{@link android.app.Service#onBind(Intent) onBind()} службы, чтобы получить объект +{@link android.os.IBinder} для структуры проверки подлинности. Благодаря этому система +может вызывать методы структуры проверки подлинности между процессами. +

+ В +примере адаптера синхронизации именем класса этой службы является +com.example.android.samplesync.authenticator.AuthenticationService. +

+
+
+ Необязательно: конкретный подкласс класса +{@link android.accounts.AbstractAccountAuthenticator}, который обрабатывает запросы на +аутентификацию. +
+
+ В этом классе имеются методы, которые {@link android.accounts.AccountManager} вызывает +для проверки подлинности учетных данных пользователя на сервере. Подробности процесса +аутентификации значительно различаются в зависимости от технологии, используемой на сервере. Чтобы узнать подробнее об аутентификации, +обратитесь к соответствующей документации к программному обеспечению используемого сервера. +

+ В +примере адаптера синхронизации структура проверки подлинности определяется в классе +com.example.android.samplesync.authenticator.Authenticator. +

+
+
+ Файлы XML, в которых определяются адаптер синхронизация и структура проверки подлинности для системы. +
+
+ Описанные ранее компоненты службы адаптера синхронизации и структуры проверки подлинности определяются +в элементах +<service> +в манифесте приложения. Эти элементы +включают дочерние элементы +<meta-data> +, в которых имеются определенные данные для +системы. +
    +
  • + Элемент +<meta-data> +для службы адаптера синхронизации указывает на файл +XML res/xml/syncadapter.xml. В свою очередь, в этом файле задается +URI веб-службы для синхронизации с поставщиком контактов, +а также тип аккаунта для этой веб-службы. +
  • +
  • + Необязательно: элемент +<meta-data> +для структуры проверки подлинности указывает на файл XML +res/xml/authenticator.xml. В свою очередь, в этом файле задается +тип аккаунта, который поддерживает структура проверки подлинности, а также ресурсы пользовательского интерфейса, +которые отображаются в процессе аутентификации. Тип аккаунта, указанный в этом +элементе, должен совпадать с типом аккаунта, который задан для +адаптера синхронизации. +
  • +
+
+
+

Потоки данных из социальных сетей

+

+ Для управления входящими данными из социальных сетей используются таблицы {@code android.provider.ContactsContract.StreamItems} +и +{@code android.provider.ContactsContract.StreamItemPhotos}. Можно создать адаптер синхронизации, который добавляет поток данных +из вашей собственной сети в эти таблицы, либо вы можете считывать поток данных из этих таблиц и отображать +их в собственном приложении. Можно также реализовать оба этих способа. С помощью этих функций вы можете интегрировать службы социальных сетей +в компоненты Android для работы с социальными сетями. +

+

Текст из потока данных из социальных сетей

+

+ Элементы потока всегда ассоциируются с необработанным контактом. Идентификатор +{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} связывается со значением +_ID необработанного контакта. Тип аккаунта и его имя для необработанного +контакта также хранятся в строке элемента потока. +

+

+ Данные из потока следует хранить в следующих столбцах: +

+
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} +
+
+ Обязательный. Тип аккаунта пользователя для необработанного контакта, связанного с +этим элементом потока. Не забудьте задать это значение при вставке элемента потока. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} +
+
+ Обязательный. Имя аккаунта пользователя для необработанного контакта, связанного с +этим элементом потока. Не забудьте задать это значение при вставке элемента потока. +
+
+ Столбцы идентификатора +
+
+ Обязательный. При вставке элемента потока необходимо вставить +указанные ниже столбцы идентификатора. +
    +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}: значение +{@code android.provider.BaseColumns#_ID} для контакта, с которым ассоциирован +этот элемент потока. +
  • +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}: значение +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} для контакта, с которым ассоциирован +этот элемент потока. +
  • +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}: значение +{@code android.provider.BaseColumns#_ID} для необработанного контакта, с которым ассоциирован +этот элемент потока. +
  • +
+
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} +
+
+ Необязательный. В нем хранится сводная информация, которую можно отобразить в начале элемента потока. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} +
+
+ Текст элемента потока: либо контент, опубликованный источником элемента, +либо описание некоторого действия, сгенерировавшего элемент потока. В этом столбце могут содержаться +любое форматирование и встроенные изображения ресурсов, рендеринг которых можно выполнить с помощью метода +{@link android.text.Html#fromHtml(String) fromHtml()}. Поставщик может обрезать слишком длинный контент +или заменить его часть многоточием, однако он попытается избежать нарушения тегов. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} +
+
+ Текстовая строка с информацией о времени вставки или обновления элемента в +миллисекундах от начала отсчета времени. Обслуживанием этого столбца занимаются приложения, которые вставляют или +обновляют элементы потока; поставщик контактов не выполняет +это автоматически. +
+
+

+ Для отображения идентификационной информации для элементов потока воспользуйтесь +{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}, +{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} и +{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} для связывания с ресурсами +в вашем приложении. +

+

+ В таблице {@code android.provider.ContactsContract.StreamItems} также имеются столбцы +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC1}—{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4}, +которые предназначены исключительно для +адаптеров синхронизации. +

+

Фотографии из потока данных из социальных сетей

+

+ Фотографии, связанные с элементом потока, хранятся в таблице +{@code android.provider.ContactsContract.StreamItemPhotos}. Столбец +{@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} +в этой таблице связан со столбцом {@code android.provider.BaseColumns#_ID} +в таблице {@code android.provider.ContactsContract.StreamItems}. Ссылки на фотографии хранятся в следующих столбцах +таблицы: +

+
+
+ Столбец {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} (объект BLOB). +
+
+ Представление фотографии в двоичном формате и с измененным поставщиком размером для ее хранения и отображения. + Этот столбец доступен для обеспечения обратной совместимости с предыдущими версиями поставщика +контактов, которые использовались для хранения фотографий. Однако в текущей версии +поставщика мы не рекомендуем использовать этот столбец для хранения фотографий. Вместо этого воспользуйтесь столбцом +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} или +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (или обоими +столбцами, как описано далее) для хранения фотографий в файле. В этом +столбце теперь хранятся миниатюры фотографий, доступных для чтения. +
+
+ {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} +
+
+ Числовой идентификатор фотографии для необработанного контакта. Добавьте это значение к константе +{@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI}, +чтобы получить URI контента для одного файла фотографии, а затем вызовите метод +{@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()}, чтобы получить средство обработки файла фотографии. +
+
+ {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} +
+
+ URI контента, указывающий на файл фотографии, для фотографии, которая представлена этой строкой. + Вызовите метод {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()}, передав в него этот URI, чтобы получить средство обработки файла фотографии. +
+
+

Использование таблиц из потока данных из социальных сетей

+

+ Эти таблицы работают аналогично другим основным таблицам в поставщике контактов, за исключением указанных ниже моментов. +

+
    +
  • + Для работы с этими таблицами требуются дополнительные разрешения на доступ. Для чтения данных из них вашему приложению +должно быть предоставлено разрешение {@code android.Manifest.permission#READ_SOCIAL_STREAM}. Для +изменения таблиц ваше приложение должно иметь разрешение +{@code android.Manifest.permission#WRITE_SOCIAL_STREAM}. +
  • +
  • + Для таблицы {@code android.provider.ContactsContract.StreamItems} существует ограничение на количество строк, +которое можно хранить для каждого необработанного контакта. При достижении этого ограничения +поставщик контактов освобождает место для новых строк элементов потока путем автоматического удаления +строк со самой старой меткой +{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP}. Чтобы получить это ограничение, +запросите URI контента +{@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}. Для всех аргументов, +отличных от URI контента, можно оставить значение null. Запрос возвращает +объект Cursor, в котором содержится одна строка с одним столбцом +{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}. +
  • +
+ +

+ Класс {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} определяет +дочернюю таблицу объектов {@code android.provider.ContactsContract.StreamItemPhotos}, в которой содержатся +строки для одного элемента потока. +

+

Взаимодействие с потоками данных из социальных сетей

+

+ Управление потоком данных из социальных сетей осуществляется поставщиком контактов совместно с приложением для управления контактами, имеющимся на устройстве. +Такой подход позволяет организовать эффективное использование данных из социальных сетей +с данными о существующих контактах. Доступны указанные ниже функции. +

+
    +
  • + Организовав синхронизацию данных из социальной службы с поставщиком контактов посредством +адаптера синхронизации, вы можете получать данные о недавней активности контактов пользователя и хранить такие данные в таблицах +,{@code android.provider.ContactsContract.StreamItems} +и {@code android.provider.ContactsContract.StreamItemPhotos} для использования в дальнейшем. +
  • +
  • + Помимо регулярной синхронизации, адаптер синхронизации можно настроить на получение +дополнительных данных при выборе пользователем контакта для просмотра. Благодаря этому ваш адаптер синхронизации +может получать фотографии высокого разрешения и самые актуальные элементы потока для контакта. +
  • +
  • + Регистрируя уведомление в приложении для работы с контактами и в поставщике +контактов, вы можетеполучать намерения при просмотре контакта и обновлять на этом этапе +данные о состоянии контакта из вашей службы. Такой подход может обеспечить большее быстродействие и меньший объем +использования полосы пропускания, чем выполнение полной синхронизации с помощью адаптера синхронизации. +
  • +
  • + Пользователи могут добавить контакт в вашу службу социальной сети, обратившись к контакту +в приложении для работы с контактами, которое имеется на устройстве. Это реализуется с помощью функции «пригласить контакт», +для включения которой используется сочетание операции, +которая добавляет существующий контакт в вашу сеть, и файла XML, в котором представлены сведения о вашем приложении для поставщика контактов и приложения для работы с +контактами. +
  • +
+

+ Регулярная синхронизация элементов потока с помощью поставщика контактов выполняется так же, +как и любая другая синхронизация. Дополнительные сведения о синхронизации представлены в разделе +Адаптеры синхронизации поставщика контактов. Регистрация уведомлений +и приглашение контактов рассматриваются в следующих двух разделах. +

+

Регистрация для обработки просмотров контактов в социальных сетях

+

+ Чтобы зарегистрировать адаптер синхронизации для получения уведомлений о просмотрах пользователями контакта, +управление которым осуществляется вашим адаптером синхронизации, выполните указанные ниже действия. +

+
    +
  1. + В каталоге res/xml/ своего проекта создайте файл +contacts.xml. Если у вас уже есть этот файл, переходите к следующему действию. +
  2. +
  3. + В этом файле добавьте элемент +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Если этот элемент уже существует, можете переходить к следующему действию. +
  4. +
  5. + Чтобы зарегистрировать службу, которой отправляется уведомление при открытии пользователем страницы со сведениями о контакте +в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут +viewContactNotifyService="serviceclass" к элементу, где +serviceclass — это полное имя класса службы, +которая должна получить намерение из приложения для работы с контактами. Для службы-уведомителя +используйте класс, который является расширением класса {@link android.app.IntentService}, чтобы разрешить службе +получать намерения. Данные во входящем намерении содержат URI контента необработанного +контакта, выбранного пользователем. В службе-уведомителе можно привязать адаптер синхронизации, а затем вызвать его +для обновления данных для необработанного контакта. +
  6. +
+

+ Чтобы зарегистрировать операцию, которую следует вызвать при выборе пользователем элемента потока или фотографии (или обоих элементов), выполните указанные ниже действия. +

+
    +
  1. + В каталоге res/xml/ своего проекта создайте файл +contacts.xml. Если у вас уже есть этот файл, переходите к следующему действию. +
  2. +
  3. + В этом файле добавьте элемент +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Если этот элемент уже существует, можете переходить к следующему действию. +
  4. +
  5. + Чтобы зарегистрировать одну из ваших операций для обработки выбора пользователем элемента потока +в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут +viewStreamItemActivity="activityclass" к элементу, где +activityclass — это полное имя класса операции, +которая должна получить намерение из приложения для работы с контактами. +
  6. +
  7. + Чтобы зарегистрировать одну из ваших операций для обработки выбора пользователем фотографии в потоке +в приложении для работы с контактами, которое имеется на устройстве, добавьте атрибут +viewStreamItemPhotoActivity="activityclass" к элементу, где +activityclass — это полное имя класса операции, +которая должна получить намерение из приложения для работы с контактами. +
  8. +
+

+ Дополнительные сведения об элементе <ContactsAccountType> представлены в разделе +элемент <ContactsAccountType>. +

+

+ Данные во входящем намерении содержат URI контента элемента или фотографии, выбранных пользователем. + Чтобы использовать разные операции для текстовых элементов и фотографий, используйте оба атрибута в одном файле. +

+

Взаимодействие со службой социальной сети

+

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

+
    +
  1. + В каталоге res/xml/ своего проекта создайте файл +contacts.xml. Если у вас уже есть этот файл, переходите к следующему действию. +
  2. +
  3. + В этом файле добавьте элемент +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Если этот элемент уже существует, можете переходить к следующему действию. +
  4. +
  5. + Добавьте следующие атрибуты: +
      +
    • inviteContactActivity="activityclass";
    • +
    • + inviteContactActionLabel="@string/invite_action_label". +
    • +
    + Значение activityclass представляет собой полное имя класса операции, +которая должна получить намерение. Значениеinvite_action_label +— это текстовая строка, которая отображается в меню Добавить подключение +в приложении для работы с контактами. +
  6. +
+

+ Примечание. ContactsSource — это устаревшее имя тега для +ContactsAccountType, которое больше не используется. +

+

Ссылка contacts.xml

+

+ В файле contacts.xml содержатся элементы XML, которые управляют взаимодействием вашего +адаптера синхронизации и вашего приложения с поставщиком контактов и приложением для работы с контактами. Эти +элементы описаны в следующих разделах. +

+

Элемент <ContactsAccountType>

+

+ Элемент <ContactsAccountType> управляет взаимодействием вашего +приложения с приложением для работы с контактами. Ниже представлен его синтаксис. +

+
+<ContactsAccountType
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        inviteContactActivity="activity_name"
+        inviteContactActionLabel="invite_command_text"
+        viewContactNotifyService="view_notify_service"
+        viewGroupActivity="group_view_activity"
+        viewGroupActionLabel="group_action_text"
+        viewStreamItemActivity="viewstream_activity_name"
+        viewStreamItemPhotoActivity="viewphotostream_activity_name">
+
+

+ находится в: +

+

+ res/xml/contacts.xml +

+

+ может содержать: +

+

+ <ContactsDataKind> +

+

+ Описание +

+

+ Этот элемент объявляет компоненты и элементы пользовательского интерфейса, с помощью которых пользователи могут приглашать свои контакты +в социальную сеть, уведомлять пользователей при обновлении одного из их потоков данных из социальных сетей и +др. +

+

+ Обратите внимание, что префикс атрибута android: необязательно использовать для атрибутов +<ContactsAccountType>. +

+

+ Атрибуты +

+
+
{@code inviteContactActivity}
+
+ Полное имя класса операции в вашем приложении, которую необходимо активировать +при выборе пользователем элемента Добавить подключение в приложении +для работы с контактами, которое имеется на устройстве. +
+
{@code inviteContactActionLabel}
+
+ Текстовая строка, которая отображается для операции, заданной в +{@code inviteContactActivity}, в меню Добавить подключение. + Например, можно указать фразу «Следите за новостями в моей сети». Для этого элемента можно использовать +идентификатор строкового ресурса. +
+
{@code viewContactNotifyService}
+
+ Полное имя класса службы в вашем приложении, которая должна получать +уведомления при просмотре контакта пользователем. Такое уведомление отправляется приложением для работы с контактами, +которое имеется на устройстве; благодаря этому ваше приложение может отложить выполнение операций, требующих обработки большого объема данных, до тех пор +, пока это не потребуется. Например, ваше приложение может реагировать на такое уведомление +путем считывания и отображения фотографии контакта в высоком разрешении и самых актуальных +элементов потока данных из социальной сети. Дополнительные сведения об этой функции представлены в разделе +Взаимодействие с потоками данных из социальных сетей. Пример +службы уведомлений представлен в файле +NotifierService.java в +образце приложенияSampleSyncAdapter. +
+
{@code viewGroupActivity}
+
+ Полное имя класса операции в вашем приложении, которая может отобразить +информацию о группе. При нажатии пользователем на метку группы в приложении для работы с данными, +которое имеется на устройстве, отображается пользовательский интерфейс для этой операции. +
+
{@code viewGroupActionLabel}
+
+ Метка, отображаемая приложением для работы с контактами для элемента пользовательского интерфейса, с помощью которой +пользователь может просмотреть группы в вашем приложении. +

+ Например, если вы установили приложение Google+ на ваше устройство и выполняете синхронизацию +данных в Google+ с приложением для работы с контактами, то круги Google+ будут обозначены в приложении для работы с контактами как +группы на вкладке Группы. При нажатии на на круг +Google+ участники крга отобразятся как группа контактов. В верхней части экрана +находится значок Google+; если нажать на него, управление перейдет в приложение +Google+. В приложении для управления контактами это реализовано с помощью +{@code viewGroupActivity}, в которой значок Google+ используется в качестве значения +{@code viewGroupActionLabel}. +

+

+ Для этого атрибута можно использовать идентификатор строкового ресурса. +

+
+
{@code viewStreamItemActivity}
+
+ Полное имя класса операции в вашем приложении, которую запускает +приложение для работы с контактами, когда пользователь выбирает элемент потока для необработанного контакта. +
+
{@code viewStreamItemPhotoActivity}
+
+ Полное имя класса операции в вашем приложении, которую запускает +приложение для работы с контактами, когда пользователь выбирает фотографию в элементе +потока для необработанного контакта. +
+
+

Элемент <ContactsDataKind>

+

+ Элемент <ContactsDataKind> управляет отображением настраиваемых строк данных вашего +приложения в интерфейсе приложения для работы с контактами, которое имеется на устройстве. Ниже представлен его синтаксис. +

+
+<ContactsDataKind
+        android:mimeType="MIMEtype"
+        android:icon="icon_resources"
+        android:summaryColumn="column_name"
+        android:detailColumn="column_name">
+
+

+ находится в: +

+<ContactsAccountType> +

+ Описание +

+

+ Используйте этот элемент для отображения содержимого настраиваемой строки данных в приложении для работы с контактами как части +сведений о необработанном контакте. Каждый дочерний элемент <ContactsDataKind> +элемента <ContactsAccountType> представляет собой тип настраиваемой строки данных, который +адаптер синхронизации добавляет в таблицу {@link android.provider.ContactsContract.Data}. Для каждого используемого вами настраиваемого типа MIME необходимо добавить один элемент +<ContactsDataKind>. Вам не нужно добавлять элемент +, если у вас имеется настраиваемая строка данных, для которой не требуется отображать данные. +

+

+ Атрибуты +

+
+
{@code android:mimeType}
+
+ Определенные вами настраиваемые типы MIME для одного из ваших типов настраиваемых строк данных в таблице +{@link android.provider.ContactsContract.Data}. Например, значение +vnd.android.cursor.item/vnd.example.locationstatus может быть настраиваемым +типом MIME для строки данных, в которой находятся записи о последнем известном местоположении контакта. +
+
{@code android:icon}
+
+ Графический ресурс +Android, +который приложение для работы с контактами отображает рядом с вашими данными. Используйте его для обозначения того, +что эти данные получены из вашей службы. +
+
{@code android:summaryColumn}
+
+ Имя столбца для первого из двух значений, полученных из строки данных. Значение +отображается в виде первой строки записи в этой строке данных. Первая строка +предназначена для использования в качестве сводных данных, однако она необязательна. См. также +android:detailColumn. +
+
{@code android:detailColumn}
+
+ Имя столбца для второго из двух значений, полученных из строки данных. Значение +отображается в виде второй строки записи в этой строке данных. См. также +{@code android:summaryColumn}. +
+
+

Дополнительные возможности поставщика контактов

+

+ Помимо основных функций, описанных разделах выше, в поставщике +контактов предусмотрены указанные ниже полезные функции для работы с данными контактов. +

+
    +
  • Группы контактов
  • +
  • Функции работы с фотографиями
  • +
+

Группы контактов

+

+ Поставщик контактов может дополнительно отметить коллекции связанных контактов с данными о +группе. Если серверу, который связан с учетной записью пользователя, +требуется сохранить группы, адаптеру синхронизации для типа этого аккаунта следует передать +данные о группах из поставщика контактов на сервер. При добавлении пользователем нового контакта на сервер +и последующем помещении этого контакта в новую группу адаптер синхронизации должен добавить эту новую группу в таблицу +{@link android.provider.ContactsContract.Groups}. Группа или группы, в которые входит необработанный контакт, +хранятся в таблице {@link android.provider.ContactsContract.Data} с использованием типа MIME +{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}. +

+

+ Если необходимо создать адаптер синхронизации, который будет добавлять данные необработанного контакта с сервера +в поставщик контактов, а вы не используете группы, то вам необходимо указать для поставщика, +чтобы он сделал ваши данные видимыми. В коде, который выполняется при добавлении пользователем +аккаунта на устройство, обновите строку {@link android.provider.ContactsContract.Settings}, +которую поставщик контактов добавляет для этого аккаунта. В этой строке укажите в столбце +{@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE +Settings.UNGROUPED_VISIBLE} значение «1». После этого поставщик контактов всегда будет +делать ваши данные видимыми, даже если вы не используете группы. +

+

Фотографии контактов

+

+ В таблице {@link android.provider.ContactsContract.Data} хранятся фотографии в виде строк +{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} типа MIME. Столбец +{@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} в строке связан со столбцом +{@code android.provider.BaseColumns#_ID} необработанного контакта, которому он принадлежит. + Класс {@link android.provider.ContactsContract.Contacts.Photo} определяет вложенную таблицу +{@link android.provider.ContactsContract.Contacts}, в которой содержится информация об основной фотографии +контакта (которая является основной фотографией основного необработанного контакта этого контакта). Аналогичным образом класс +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} определяет вложенную таблицу +{@link android.provider.ContactsContract.RawContacts}, в которой содержится информация об основной фотографии +необработанного контакта. +

+

+ В справочной документации по {@link android.provider.ContactsContract.Contacts.Photo} и +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} содержатся примеры +получения информации о фотографии. К сожалению, отсутствует класс для удобного извлечения миниатюры +основной фотографии необработанного контакта, однако вы можете отправить запрос в таблицу +{@link android.provider.ContactsContract.Data}, выбрать +{@code android.provider.BaseColumns#_ID} необработанного контакта, +{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} и столбец {@link android.provider.ContactsContract.Data#IS_PRIMARY}, +чтобы найти строку основной фотографии необработанного контакта. +

+

+ Потоки данных из социальных сетей также могут включать фотографии. Они находятся в таблице +{@code android.provider.ContactsContract.StreamItemPhotos}, дополнительные сведения о которой представлены в разделе +Фотографии из потока данных из социальных сетей. +

diff --git a/docs/html-intl/intl/ru/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/ru/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 0000000000000000000000000000000000000000..c912dbc037523bfda5cd1f845c9128dff93bd900 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=Основные сведения о поставщике контента +@jd:body + + + +

+ Поставщик контента управляет доступом к центральному репозиторию данных. Поставщик +является компонентом приложения Android, который зачастую имеет собственный пользовательский интерфейс для +работы с данными. Однако поставщики контента предназначены в первую очередь для использования другими приложениями, +которые получают доступ к поставщику посредством клиентского объекта поставщика. Вместе поставщики +и клиенты поставщиков обеспечивают согласованный, стандартный интерфейс к данным, который также обрабатывает +взаимодействие между процессами и обеспечивает защищенный доступ к данным. +

+

+ В этой статье рассматриваются основные сведения, касающиеся следующих тем: +

+
    +
  • принцип работы поставщика контента;
  • +
  • API, используемый для получения данных от поставщика контента;
  • +
  • API, используемый для вставки данных в поставщик контента и их обновления или удаления в нем;
  • +
  • другие функции API, которые упрощают работу с поставщиками.
  • +
+ + +

Обзор

+

+ Поставщик контента предоставляет данные внешним приложениям в виде одной или нескольких таблиц, +аналогичных таблицам в реляционной базе данных. Строка представляет собой экземпляр некоторого типа +собираемых поставщиком данных, а каждый столбец в этой строке — это отдельный элемент данных, +собранных для экземпляра. +

+

+ Примером встроенного поставщика в платформе Android может служить пользовательский словарь, +в котором хранятся данные о написании нестандартных слов, добавленных пользователем. В таблице 1 показано, +как данные могут выглядеть в этой таблице поставщика. +

+

+ Таблица 1. Пример таблицы пользовательского словаря. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
wordapp idfrequencylocale_ID
mapreduceuser1100en_US1
precompileruser14200fr_FR2
appletuser2225fr_CA3
constuser1255pt_BR4
intuser5100en_UK5
+

+ В каждой строке таблицы 1 представлен экземпляр слова, которое отсутствует +в стандартном словаре. В каждом ее столбце содержатся некоторые данные для слова, +например, данные о языке, на котором это слово было впервые использовано. Заголовки столбцов представляют собой имена столбцов, которые хранятся +в поставщике. Чтобы узнать язык строки, необходимо обратиться к столбцу locale. В этом +поставщике столбец _ID выступает в роли «основного ключа», +который поставщик автоматически сохраняет. +

+

+ Примечание. В поставщике необязательно должен быть основной ключ, а также ему необязательно +использовать_ID в качестве имени столбца основного ключа, если таковой имеется. Однако, если +необходимо привязать данные из поставщика к классу {@link android.widget.ListView}, +один из столбцов должен именоваться _ID. Дополнительные сведения об этом требовании представлены в разделе +Отображение результатов запроса. +

+

Доступ к поставщику

+

+ Для доступа приложения к данным из поставщика контента +используется клиентский объект {@link android.content.ContentResolver}. В этом объекте имеются методы, которые вызывают +идентичные методы в объекте поставщика, который представляет собой экземпляр одного из конкретных +подклассов класса {@link android.content.ContentProvider}. В этих методах +{@link android.content.ContentResolver} представлены основные функции + CRUD (аббревиатура create, retrieve, update, delete [создание, получение, обновление и удаление]) постоянного хранилища. +

+

+ Объект {@link android.content.ContentResolver} в процессе клиентского приложения +и объект {@link android.content.ContentProvider} в приложении, +которое владеет поставщиком, автоматически обрабатывают взаимодействие между процессами. +Объект {@link android.content.ContentProvider} также выступает в роли уровня абстракции между +репозиторием данных и внешним представлением данных в виде таблиц. +

+

+ Примечание. Для доступа к поставщику ваше приложение обычно должно запросить определенные разрешения +в своем файле манифеста. Дополнительные сведения об этом представлены в разделе +Разрешения поставщика контента. +

+

+ Например, чтобы получить из поставщика пользовательского словаря список слов и языков, на которых они представлены, +вызовите метод {@link android.content.ContentResolver#query ContentResolver.query()}. + В свою очередь, метод {@link android.content.ContentResolver#query query()} вызывает метод +{@link android.content.ContentProvider#query ContentProvider.query()}, определенный поставщиком +пользовательского словаря. В примере кода ниже показан вызов метода +{@link android.content.ContentResolver#query ContentResolver.query()}. +

+

+// Queries the user dictionary and returns results
+mCursor = getContentResolver().query(
+    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
+    mProjection,                        // The columns to return for each row
+    mSelectionClause                    // Selection criteria
+    mSelectionArgs,                     // Selection criteria
+    mSortOrder);                        // The sort order for the returned rows
+
+

+ В таблице 2 указано соответствие аргументов для метода +{@link android.content.ContentResolver#query +query(Uri,projection,selection,selectionArgs,sortOrder)} SQL-инструкции SELECT. +

+

+ Таблица 2. Сравнение метода query() и SQL-запроса. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Аргумент метода query()Параметр/ключевое слово SELECTПримечания
UriFROM table_nameUri соответствует таблице table_name в поставщике.
projectioncol,col,col,... + projection представляет собой массив столбцов, которые следует включить +в каждую полученную строку. +
selectionWHERE col = valueselection задает критерии для выбора строк.
selectionArgs + (Точный эквивалент отсутствует. В предложении выбора заполнители ? +заменяются аргументами выбора). +
sortOrderORDER BY col,col,... + sortOrder задает порядок отображения строк в возвращаемом объекте +{@link android.database.Cursor}. +
+

URI контента

+

+ URI контента представляет собой URI, который определяет данные в поставщике. URI контента +могут включать символическое имя всего поставщика (его центр) и +имя, которое указывает на таблицу (путь). При вызове +клиентского метода для доступа к таблице в поставщике URI контента этой таблицы выступает в роли одного +из аргументов этого метода. +

+

+ Константа +{@link android.provider.UserDictionary.Words#CONTENT_URI} в предыдущих строках кода содержит URI контента +таблицы words в пользовательском словаре. Объект{@link android.content.ContentResolver} +анализирует центр URI и использует его для «разрешения» поставщика +путем сравнения центра с системной таблицей известных поставщиков. {@link android.content.ContentResolver} +может отправить аргументы запроса в соответствующий +поставщик. +

+

+ {@link android.content.ContentProvider} использует часть URI контента, в которой указан путь, для выбора таблицы +для доступа. В поставщике обычно имеется путь для каждой предоставляемой им таблицы. +

+

+ В предыдущих строках кода полный URI для таблицы words выглядит следующим образом: +

+
+content://user_dictionary/words
+
+

+ Строка user_dictionary ֪– это центр поставщика, а строка +words — это путь к таблице. Строка +content:// (схема) присутствует всегда; +она определяет, что это URI контента. +

+

+ Многие поставщики предоставляют доступ к одной строке в таблице путем добавления идентификатора +в конец URI. Например, чтобы извлечь из пользовательского словаря строку, в столбце _ID которой +указано 4, можно воспользоваться следующим URI контента: +

+
+Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
+
+

+ Идентификаторы часто используются в случае, когда вы извлекли набор строк и хотите обновить или удалить +одну из них. +

+

+ Примечание. В классах {@link android.net.Uri} и {@link android.net.Uri.Builder} +имеются методы для удобного создания правильно оформленных объектов URI из строк. {@link android.content.ContentUris} +содержит методы для удобного добавления идентификаторов +к URI. В примере кода выше для добавления идентификатора к URI контента UserDictionary используется метод {@link android.content.ContentUris#withAppendedId +withAppendedId()}. +

+ + + +

Получение данных от поставщика

+

+ В это разделе рассматривается порядок получения данных от поставщика на примере +поставщика пользовательского словаря. +

+

+ Для полной ясности в примерах кода, приведенных в этом разделе, методы +{@link android.content.ContentResolver#query ContentResolver.query()} вызываются в потоке пользовательского интерфейса. В реальном +коде запросы следует выполнять асинхронно в отдельном потоке. Одним из способов реализовать +это является использование класса {@link android.content.CursorLoader}, который более подробно описан в +статье +Загрузчики. Кроме того, в этой статье представлены лишь фрагменты кода; они не представляют собой готовое +приложение. +

+

+ Чтобы получить данные из поставщика, выполните указанные ниже основные действия. +

+
    +
  1. + Запросите у поставщика разрешение на чтение. +
  2. +
  3. + Определите код, который отвечает за отправку запроса поставщику. +
  4. +
+

Запрос разрешения на чтение

+

+ Чтобы ваше приложение могло получать данные от поставщика, приложению требуется получить от поставщика разрешение +на чтение. Это разрешение невозможно получить во время выполнения; вместо этого вам необходимо указать, что вам требуется +такое разрешение, в манифесте приложения. Для этого воспользуйтесь элементом +<uses-permission> +и укажите точное название разрешения, +определенное поставщиком. Указав этот элемент в манифесте, вы тем самым запрашиваете +необходимое разрешение для вашего приложения. Когда пользователи устанавливают ваше приложение, они косвенно +получают разрешение по этому запросу. +

+

+ Чтобы узнать точное название разрешения на чтение в используемом поставщике, +а также названия других используемых в нем разрешений на чтение, обратитесь +к документации поставщика. +

+

+ Дополнительные сведения о роли разрешений в получении доступа к поставщику представлены в разделе +Разрешения поставщика контента. +

+

+ Поставщик пользовательского словаря задает разрешение +android.permission.READ_USER_DICTIONARY в своем файле манифеста, +поэтому приложению, которому требуется выполнить чтение данных из поставщика, необходимо запросить именно это разрешение. +

+ +

Создание запроса

+

+ Следующим этапом получения данных от поставщика является создание запроса. В следующем фрагменте кода +задаются некоторые переменные для доступа к поставщику пользовательского словаря: +

+
+
+// A "projection" defines the columns that will be returned for each row
+String[] mProjection =
+{
+    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
+    UserDictionary.Words.WORD,   // Contract class constant for the word column name
+    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
+};
+
+// Defines a string to contain the selection clause
+String mSelectionClause = null;
+
+// Initializes an array to contain selection arguments
+String[] mSelectionArgs = {""};
+
+
+

+ В следующем фрагменте кода демонстрируется порядок использования метода +{@link android.content.ContentResolver#query ContentResolver.query()} (в качестве примера выступает +поставщик пользовательского словаря): Клиентский запрос поставщика аналогичен SQL-запросу. В нем содержится +набор столбцов, которые возвращаются, набор критериев выборки и порядок сортировки. +

+

+ Набор столбцов, которые должен возвратить запрос, называется проекцией +(переменная mProjection). +

+

+ Выражение, которое задает строки для получения, состоит из предложения выбора +и аргументов выбора. Предложение выбора представляет собой сочетание логических выражений, +имен столбцов и значений (переменная mSelectionClause). Если вместо значения указать подставляемый параметр +?, метод запроса извлекает значение из массива аргументов выбора (переменная +mSelectionArgs). +

+

+ В следующем фрагменте кода, если пользователь не указал слово, то для предложения выбора задается значение +null, а запрос возвращает все слова, имеющиеся в поставщике. Если пользователь указал слово, то для предложения выбора задается значение +UserDictionary.Words.WORD + " = ?", +а для первого элемента в массиве аргументов выбора задается введенное пользователем слово. +

+
+/*
+ * This defines a one-element String array to contain the selection argument.
+ */
+String[] mSelectionArgs = {""};
+
+// Gets a word from the UI
+mSearchString = mSearchWord.getText().toString();
+
+// Remember to insert code here to check for invalid or malicious input.
+
+// If the word is the empty string, gets everything
+if (TextUtils.isEmpty(mSearchString)) {
+    // Setting the selection clause to null will return all words
+    mSelectionClause = null;
+    mSelectionArgs[0] = "";
+
+} else {
+    // Constructs a selection clause that matches the word that the user entered.
+    mSelectionClause = UserDictionary.Words.WORD + " = ?";
+
+    // Moves the user's input string to the selection arguments.
+    mSelectionArgs[0] = mSearchString;
+
+}
+
+// Does a query against the table and returns a Cursor object
+mCursor = getContentResolver().query(
+    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
+    mProjection,                       // The columns to return for each row
+    mSelectionClause                   // Either null, or the word the user entered
+    mSelectionArgs,                    // Either empty, or the string the user entered
+    mSortOrder);                       // The sort order for the returned rows
+
+// Some providers return null if an error occurs, others throw an exception
+if (null == mCursor) {
+    /*
+     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
+     * call android.util.Log.e() to log this error.
+     *
+     */
+// If the Cursor is empty, the provider found no matches
+} else if (mCursor.getCount() < 1) {
+
+    /*
+     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
+     * an error. You may want to offer the user the option to insert a new row, or re-type the
+     * search term.
+     */
+
+} else {
+    // Insert code here to do something with the results
+
+}
+
+

+ Этот запрос аналогичен следующей инструкции SQL: +

+
+SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
+
+

+ В этой инструкции SQL вместо констант класса-контракта используются фактические имена столбцов. +

+

Защита от ввода вредоносного кода

+

+ Если данные, которыми управляет поставщик контента, находятся в базе данных SQL, то включение в необработанные инструкции +SQL внешних ненадежных данных может привести к атаке путем внедрения кода SQL. +

+

+ Рассмотрим следующее предложение выбора: +

+
+// Constructs a selection clause by concatenating the user's input to the column name
+String mSelectionClause =  "var = " + mUserInput;
+
+

+ Если вы используете это предложение, вы разрешаете пользователю связать вашу инструкцию SQL с вредоносным кодом SQL. + Например, пользователь может ввести nothing; DROP TABLE *; для mUserInput, что +приведет к созданию следующего предложения выбора: var = nothing; DROP TABLE *;. Поскольку +предложение выбора выполняется в потоке как инструкция SQL, это может привести к тому, что поставщик удалит все +таблицы в соответствующей базе данных SQLite (если только в поставщик не настроен на отслеживание попыток +внедрить вредоносный код SQL). +

+

+ Чтобы избежать этого, воспользуйтесь предложением выбора, в котором ? выступает в качестве подставляемого +параметра, а также отдельным массивом аргументов выбора. После этого ввод пользователя +будет связан напрямую с запросом и не будет интерпретироваться как часть инструкции SQL. + Поскольку в этом случае введенный пользователем запрос не рассматривается как код SQL, то в него не удастся внедрить вредоносный код SQL. Вместо +объединения, которое следует включить в пользовательский ввод, используйте следующее предложение выбора: +

+
+// Constructs a selection clause with a replaceable parameter
+String mSelectionClause =  "var = ?";
+
+

+ Настройте массив аргументов выбора следующим образом: +

+
+// Defines an array to contain the selection arguments
+String[] selectionArgs = {""};
+
+

+ Укажите значение для массива аргументов выбора: +

+
+// Sets the selection argument to the user's input
+selectionArgs[0] = mUserInput;
+
+

+ Предложение выбора, в котором ? используется в качестве подстановочного параметра, и массив +аргументов выбора представляют собой предпочтительный способ указания выбора, даже если поставщик +не использует базу данных SQL. +

+ +

Отображение результатов запроса

+

+ Клиентский метод {@link android.content.ContentResolver#query ContentResolver.query()} всегда возвращает объект +{@link android.database.Cursor}, содержащий столбцы, указанные в проекции +запроса для строк, которые соответствуют критериям выборки в запросе. Объект +{@link android.database.Cursor} предоставляет прямой доступ на чтение содержащихся в нем строк и +столбцов. С помощью методов {@link android.database.Cursor} можно выполнить итерацию по строкам +в результатах, определить тип данных для каждого столбца, получить данные из столбца, а также проверить другие свойства +результатов. Некоторые реализации объекта {@link android.database.Cursor} автоматически обновляют +объект при изменении данных в поставщике или запускают выполнение методов в объекте-наблюдателе +при изменении объекта{@link android.database.Cursor}, либо выполняют и то, и другое. +

+

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

+

+ Если строки, соответствующие критериям выборки, отсутствуют, поставщик +возвращает объект{@link android.database.Cursor}, в котором для метода +{@link android.database.Cursor#getCount Cursor.getCount()} указано значение «0» (пустой объект cursor). +

+

+ При возникновении внутренней ошибки результаты запроса зависят от определенного поставщика. Поставщик может +возвратитьnull или выдать {@link java.lang.Exception}. +

+

+ Поскольку {@link android.database.Cursor} представляет собой «список» строк, то наилучшим способом отобразить содержимое объекта +{@link android.database.Cursor} будет связать его с {@link android.widget.ListView} +посредством {@link android.widget.SimpleCursorAdapter}. +

+

+ Следующий фрагмент кода является продолжением предыдущего фрагмента. Он создает объект +{@link android.widget.SimpleCursorAdapter}, содержащий объект{@link android.database.Cursor}, +который был получен в запросе, а затем определяет этот объект в качестве адаптера для +{@link android.widget.ListView}: +

+
+// Defines a list of columns to retrieve from the Cursor and load into an output row
+String[] mWordListColumns =
+{
+    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
+    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
+};
+
+// Defines a list of View IDs that will receive the Cursor columns for each row
+int[] mWordListItems = { R.id.dictWord, R.id.locale};
+
+// Creates a new SimpleCursorAdapter
+mCursorAdapter = new SimpleCursorAdapter(
+    getApplicationContext(),               // The application's Context object
+    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
+    mCursor,                               // The result from the query
+    mWordListColumns,                      // A string array of column names in the cursor
+    mWordListItems,                        // An integer array of view IDs in the row layout
+    0);                                    // Flags (usually none are needed)
+
+// Sets the adapter for the ListView
+mWordList.setAdapter(mCursorAdapter);
+
+

+ Примечание. Чтобы вернуть {@link android.widget.ListView} с объектом +{@link android.database.Cursor}, объект cursor должен содержать столбец с именем _ID. + Поэтому показанный ранее запрос извлекает столбец_ID для таблицы +words, даже если {@link android.widget.ListView} не отображает ее. + Данное ограничение также объясняет, почему в каждой таблице поставщика имеется столбец +_ID. +

+ + +

Получение данных из результатов запроса

+

+ Вместо того, чтобы просто отобразить результаты запроса, вы можете использовать их для выполнения других задач. Например, +можно получить написание слов из пользовательского словаря, а затем выполнить их поиск в +других поставщиках. Для этого выполните итерацию по строкам в объекте {@link android.database.Cursor}: +

+
+
+// Determine the column index of the column named "word"
+int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
+
+/*
+ * Only executes if the cursor is valid. The User Dictionary Provider returns null if
+ * an internal error occurs. Other providers may throw an Exception instead of returning null.
+ */
+
+if (mCursor != null) {
+    /*
+     * Moves to the next row in the cursor. Before the first movement in the cursor, the
+     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
+     * exception.
+     */
+    while (mCursor.moveToNext()) {
+
+        // Gets the value from the column.
+        newWord = mCursor.getString(index);
+
+        // Insert code here to process the retrieved word.
+
+        ...
+
+        // end of while loop
+    }
+} else {
+
+    // Insert code here to report an error if the cursor is null or the provider threw an exception.
+}
+
+

+ Реализации объекта {@link android.database.Cursor} содержат несколько методов get для +получения из объекта различных типов данных. Например, в следующем фрагменте кода используется метод +{@link android.database.Cursor#getString getString()}. В них также имеется метод +{@link android.database.Cursor#getType getType()}, который возвращает значение, указывающее на тип +данных в столбце. +

+ + + +

Разрешения поставщика контента

+

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

+

+ Если приложение поставщика не задает никаких разрешений, другие приложения не получают доступ к +данным поставщика. Однако компонентам приложения поставщика +всегда предоставлен полный доступ на чтение и запись, независимо от заданных разрешений. +

+

+ Как уже было отмечено ранее, для получения данных из поставщика пользовательского словаря требуется разрешение +android.permission.READ_USER_DICTIONARY. + В поставщике предусмотрено отдельное разрешениеandroid.permission.WRITE_USER_DICTIONARY +для вставки, обновления или удаления данных. +

+

+ Чтобы получить разрешения, необходимые для доступа к поставщику, приложение запрашивает их с помощью элемента +<uses-permission> +в файле манифеста. При установке менеджером пакетов Android приложения пользователю необходимо +утвердить все разрешения, запрашиваемые приложением. В случае утверждения всех разрешений +менеджер пакетов продолжает установку; если же пользователь отклоняет их, менеджер +пакетов отменяет установку. +

+

+ Для запроса доступа на чтение данных в поставщике пользовательского словаря используется +следующий элемент +<uses-permission>: +

+
+    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
+
+

+ Дополнительные сведения о влиянии разрешений на доступ к поставщику представлены в статье +Безопасность и разрешения. +

+ + + +

Вставка, обновление и удаление данных

+

+ Подобно тому, как вы получаете данные от поставщика, вы также можете можете использовать возможности взаимодействия между клиентом поставщика и объектом +{@link android.content.ContentProvider} поставщика для изменения данных. + Можно вызвать метод объекта {@link android.content.ContentResolver}, указав аргументы, +которые были переданы в соответствующий метод объекта {@link android.content.ContentProvider}. Поставщик и клиент поставщика +автоматически обрабатывают взаимодействие между процессами и обеспечивают безопасность. +

+

Вставка данных

+

+ Для вставки данных в поставщик вызовите +метод +{@link android.content.ContentResolver#insert ContentResolver.insert()}. Этот метод вставляет новую строку в поставщик и возвращает URI контента для этой строки. + В следующем фрагменте кода демонстрируется порядок вставки нового слова в поставщик пользовательского словаря: +

+
+// Defines a new Uri object that receives the result of the insertion
+Uri mNewUri;
+
+...
+
+// Defines an object to contain the new values to insert
+ContentValues mNewValues = new ContentValues();
+
+/*
+ * Sets the values of each column and inserts the word. The arguments to the "put"
+ * method are "column name" and "value"
+ */
+mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
+mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
+mNewValues.put(UserDictionary.Words.WORD, "insert");
+mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
+
+mNewUri = getContentResolver().insert(
+    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
+    mNewValues                          // the values to insert
+);
+
+

+ Данные для новой строки поступают в один объект {@link android.content.ContentValues}, +который аналогичен объекту cursor с одной строкой. Столбцы в этом объекте необязательно +должны содержать данные такого же типа, и если вы вообще не собираетесь указывать значение, вы можете задать для столбца значение +null с помощью метода {@link android.content.ContentValues#putNull ContentValues.putNull()}. +

+

+ Код в представленном фрагменте не добавляет столбец _ID, поскольку этот столбец сохраняется +автоматически. Поставщик присваивает уникальное значение _ID каждой +добавляемой строке. Обычно поставщики используют это значение в качестве основного ключа таблицы. +

+

+ URI контента, возвращенный в элементе newUri, служит для идентификации новой добавленной строки +в следующем формате: +

+
+content://user_dictionary/words/<id_value>
+
+

+ <id_value> — это содержимое столбца _ID для новой строки. + Большинство поставщиков автоматически определяют эту форму URI контента, а затем +выполняют запрошенную операцию с требуемой строкой. +

+

+ Чтобы получить значение _ID из возвращенного объекта {@link android.net.Uri}, вызовите метод +{@link android.content.ContentUris#parseId ContentUris.parseId()}. +

+

Обновление данных

+

+ Чтобы обновить строку, используйте объект {@link android.content.ContentValues} с обновленными +значениями (точно так же, как вы это делаете при вставке) и критериями выборки (так же, как и с запросом). + Используемый вами клиентский метод называется +{@link android.content.ContentResolver#update ContentResolver.update()}. Вам не нужно добавлять значения в объект +{@link android.content.ContentValues} для обновляемых столбцов. Чтобы очистить содержимое столбца, задайте значение +null. +

+

+ Следующий фрагмент кода служит для изменения языка во всех строках, где в качестве языка указано en, на +null. Возвращаемое значение представляет собой количество строк, которые были обновлены: +

+
+// Defines an object to contain the updated values
+ContentValues mUpdateValues = new ContentValues();
+
+// Defines selection criteria for the rows you want to update
+String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
+String[] mSelectionArgs = {"en_%"};
+
+// Defines a variable to contain the number of updated rows
+int mRowsUpdated = 0;
+
+...
+
+/*
+ * Sets the updated value and updates the selected words.
+ */
+mUpdateValues.putNull(UserDictionary.Words.LOCALE);
+
+mRowsUpdated = getContentResolver().update(
+    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
+    mUpdateValues                       // the columns to update
+    mSelectionClause                    // the column to select on
+    mSelectionArgs                      // the value to compare to
+);
+
+

+ Также следует проверить пользовательский ввод при вызове метода +{@link android.content.ContentResolver#update ContentResolver.update()}. Дополнительные сведения об этом +представлены в разделе Защита от ввода вредоносного кода. +

+

Удаление данных

+

+ Удаление данных аналогично получению данных строки: необходимо указать критерии выборки для строк, +которые требуется удалить, после чего клиентский метод возвратит количество удаленных строк. + Ниже представлен фрагмент кода для удаления строк с идентификатором appid user. Метод возвращает +количество удаленных строк. +

+
+
+// Defines selection criteria for the rows you want to delete
+String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
+String[] mSelectionArgs = {"user"};
+
+// Defines a variable to contain the number of rows deleted
+int mRowsDeleted = 0;
+
+...
+
+// Deletes the words that match the selection criteria
+mRowsDeleted = getContentResolver().delete(
+    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
+    mSelectionClause                    // the column to select on
+    mSelectionArgs                      // the value to compare to
+);
+
+

+ Также следует проверить пользовательский ввод при вызове метода +{@link android.content.ContentResolver#delete ContentResolver.delete()}. Дополнительные сведения об этом +представлены в разделе Защита от ввода вредоносного кода. +

+ +

Типы поставщиков данных

+

+ Поставщики контента могут предоставлять различные тип данных. Поставщик пользовательского словаря предоставляет только +текст, но также может предоставлять следующие форматы: +

+
    +
  • + целое число; +
  • +
  • + длинное целое число (long); +
  • +
  • + число с плавающей запятой; +
  • +
  • + длинное число с плавающей запятой (double). +
  • +
+

+ Другим типом данных, предлагаемых поставщиком, является большой двоичный объект (BLOB), реализованный как +64-разрядный массив. Чтобы просмотреть доступные типы данных, обратитесь к методам get класса +{@link android.database.Cursor}. +

+

+ Тип данных для каждого столбца в поставщике обычно указывается в документации к поставщику. + Типы данных для поставщика пользовательского словаря указаны в справочной документации +для класса-контракта {@link android.provider.UserDictionary.Words} (дополнительные сведения о классах-контрактах представлены в разделе +Классы-контракты). + Также определить тип данных можно путем вызова метода {@link android.database.Cursor#getType +Cursor.getType()}. +

+

+ Поставщики также хранят информацию о типе данных MIME для каждого определяемого ими URI контента. Эту информацию +можно использовать для определения того, может ли ваше приложение обрабатывать предлагаемые +поставщиком данные, а также для выбора типа обработки на основе типа MIME. Информация о типе +MIME обычно требуется при работе с поставщиком, который содержит +сложные структуры данных или файлы. Например, в таблице{@link android.provider.ContactsContract.Data} +в поставщике контактов используются типы MIME для отметки типа данных контакта, которые хранятся в каждой +строке. Чтобы получить тип MIME, соответствующий URI контента, вызовите метод +{@link android.content.ContentResolver#getType ContentResolver.getType()}. +

+

+ Синтаксис стандартных и настраиваемых типов MIME описан в +справке по типам MIME. +

+ + + +

Альтернативные формы доступа к поставщику

+

+ При разработке приложения следует учитывать три альтернативных формы доступа к поставщику: +

+
    +
  • + Пакетный доступ: можно создать пакет вызовов доступа с использованием методов в классе +{@link android.content.ContentProviderOperation}, а затем применить их с помощью метода +{@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. +
  • +
  • + Асинхронные запросы: Запросы следует выполнять в отдельном потоке. Одним из способов реализовать это является использование объекта +{@link android.content.CursorLoader}. Примеры этого представлены в +статье +Загрузчики. +
  • +
  • + Доступ к данным с помощью намерений: Несмотря на то, что намерение +невозможно отправить напрямую в поставщик, вы можете отправить запрос в приложение поставщика, в котором обычно +имеется больше возможностей для изменения данных поставщика. +
  • +
+

+ Пакетный доступ и изменение с помощью намерений описаны в следующих разделах. +

+

Пакетный доступ

+

+ Пакетный доступ к поставщику полезно использовать в случаях, когда необходимо вставить большое количество строк, или для вставки +строк в несколько таблиц в рамках одного вызова метода, а также в общих случаях для выполнения ряда +операций на границах процессов в виде транзакции (атомарной операции). +

+

+ Для доступа к поставщику в «пакетном режиме» необходимо создать массив объектов +{@link android.content.ContentProviderOperation}, а затем +отправить их в поставщик контента с помощью метода +{@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. В этот метод необходимо передать +центр поставщика контента, а не определенный URI контента. +Это позволит каждому объекту {@link android.content.ContentProviderOperation} в массиве взаимодействовать +с разными таблицами. Метод {@link android.content.ContentResolver#applyBatch +ContentResolver.applyBatch()} возвращает массив результатов. +

+

+ В описании класса-контракта {@link android.provider.ContactsContract.RawContacts} +также представлен фрагмент кода, в котором демонстрируется вставка в пакетном режиме. В исходном файле ContactAdder.java примера приложения +Диспетчер контактов +имеется пример пакетного +доступа. +

+ +

Доступ к данным с помощью намерений

+

+ Намерения позволяют в обход получать доступ к поставщику контента. Вы можете разрешить пользователям доступ к +данным в поставщике даже в том случае, если у приложения отсутствуют разрешения на доступ, либо путем +получения результирующего намерения от приложения, у которого имеются необходимые разрешения, либо путем активации +приложения, у которого имеются разрешения и которое разрешает пользователю работать с ним. +

+

Получение доступа с временными разрешениями

+

+ Вы можете получить доступ к данным в поставщике контента даже тогда, когда у вас нет необходимых разрешений на доступ +, путем отправки намерения в приложение, у которого есть такие разрешения, и получения +результирующего намерения, которое содержит разрешения URI. + Эти разрешения для определенного URI контента действуют до тех пор, пока не будет завершена операция, получившая +их. Приложение, у которой имеются бессрочные разрешения, предоставляет временные +разрешения путем задания соответствующего флага в результирующем намерении: +

+
    +
  • + Разрешение на чтение: +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} +
  • +
  • + Разрешение на запись: +{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} +
  • +
+

+ Примечание. Эти флаги не предоставляют общий доступ на чтение или запись поставщику, +центр которого указан в URI контента. Доступ предоставляется только самому URI. +

+

+ Поставщик определяет разрешения URI для URI контента в своем манифесте с помощью атрибута +android:grantUriPermission +элемента +<provider>, +а также +с помощью дочернего элемента +<grant-uri-permission> +элемента +<provider>. Дополнительные сведения о механизме разрешений URI представлены в статье +Безопасность и разрешения в разделе +Разрешения URI. +

+

+ Например, можно получить данные о контакте из поставщика контактов, даже если у вас нет разрешения +{@link android.Manifest.permission#READ_CONTACTS}. Возможно, это потребуется реализовать +в приложении, которое отправляет электронные поздравления контакту в день его рождения. Вместо запроса +{@link android.Manifest.permission#READ_CONTACTS}, когда вы получаете доступ ко всем контактам пользователя +и всей информации о них, можно предоставить пользователю возможность указать, +какие контакты используются вашим приложением. Для этого воспользуйтесь указанным ниже процессом. +

+
    +
  1. + Ваше приложение отправляет намерение, содержащее действие +{@link android.content.Intent#ACTION_PICK} и тип MIME +{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} контактов, используя для этого метод +{@link android.app.Activity#startActivityForResult +startActivityForResult()}. +
  2. +
  3. + Поскольку это намерение соответствует условиям отбора намерений для операции выбора +приложения «Контакты», эта операция переходит на передний план. +
  4. +
  5. + В операции выбора пользователь выбирает +контакт для обновления. Когда это происходит, операция выбора вызывает метод +{@link android.app.Activity#setResult setResult(resultcode, intent)} +для создания намерения, которое будет передано обратно в ваше приложение. Намерение содержит URI контента +выбранного пользователем контакта, а также флаги +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} дополнительных данных. Эти флаги предоставляют вашему приложению разрешение URI +на чтение данных контакта, на который указывает +URI контента. Затем операция выбора вызывает метод{@link android.app.Activity#finish()}, +чтобы вернуть управление вашему приложению. +
  6. +
  7. + Ваша операция возвращается на передний план, а система вызывает метод +{@link android.app.Activity#onActivityResult onActivityResult()} +вашей операции. Этот метод получает результирующее намерение, созданное операцией выбора в +приложении «Контакты». +
  8. +
  9. + С помощью URI контента из результирующего намерения можно выполнить чтение данных контакта +из поставщика контактов, даже если вы не запрашивали у поставщика постоянный доступ на чтение +в своем манифесте. Можно получить информацию о дне рождения контакта +или сведения о его адресе эл. почты, а затем отправить контакту электронное поздравление. +
  10. +
+

Использование другого приложения

+

+ Простой способ разрешить пользователю изменять данные, на доступ к которым у вас нет доступа +— это активировать приложение, у которого есть такие разрешения, а затем предоставить пользователю возможность выполнять необходимые действия в этом приложении. +

+

+ Например, приложение «Календарь» принимает намерения +{@link android.content.Intent#ACTION_INSERT}, с помощью которого можно активировать +пользовательский интерфейс приложения для вставки. Вы можете передать в это намерение дополнительные данные, которые приложение +использует для заполнения полей в пользовательском интерфейсе. Поскольку синтаксис повторяющихся событий довольно сложный, то события предпочтительно +вставлять в поставщик календаря путем активации приложения «Календарь» с помощью действия +{@link android.content.Intent#ACTION_INSERT} и последующего предоставления пользователю возможности самому вставить событие в этом приложении. +

+ +

Классы-контракты

+

+ Класс-контракт определяет константы, которые обеспечивают для приложений возможность работать с URI контента, именами +столбцов, операциями намерения и другими функциями поставщика контента. Классы-контракты не +включены в поставщик; разработчику поставщика следует определить их и сделать +их доступными для других разработчиков. Многие из поставщиков, включенные в платформу Android, +содержат соответствующие классы-контракты в пакете {@link android.provider}. +

+

+ Например, в поставщике пользовательского календаря имеется класс-контракт +{@link android.provider.UserDictionary}, содержащий константы URI контента и имен столбцов. URI +контента для таблицы words определен в константе +{@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. + В классе {@link android.provider.UserDictionary.Words} также имеются константы имен столбцов, +которые используются в фрагментах кода примера приложения, представленных в этой статье. Например, проекцию запроса +можно определить следующим образом: +

+
+String[] mProjection =
+{
+    UserDictionary.Words._ID,
+    UserDictionary.Words.WORD,
+    UserDictionary.Words.LOCALE
+};
+
+

+ Другим классом-контрактом является класс {@link android.provider.ContactsContract} для поставщика контактов. + В справочной документации к этому классу представлены фрагменты кода примера приложения. Один из его подклассов, +{@link android.provider.ContactsContract.Intents.Insert}, представляет собой класс-контракт, +который содержит константы для намерений и их данных. +

+ + + +

Справка по типам MIME

+

+ Поставщики контента могут возвращать как стандартные типы мультимедиа MIME, так и строки с настраиваемым типом MIME, либо оба этих типа. +

+

+ Типы MIME имеют следующий формат: +

+
+type/subtype
+
+

+ Например, хорошо известный тип MIME text/html имеет тип text и подтип +html. Если поставщик возвращает этот тип URI, это означает, что +строка запроса, в которой используется этот URI, возвратит текста с тегами HTML. +

+

+ Строки с настраиваемым типом MIME, которые также называются типами MIME поставщика, имеют более сложные значения +типов и подтипов. Значение типа всегда следующее: +

+
+vnd.android.cursor.dir
+
+

+ для нескольких строк, или +

+
+vnd.android.cursor.item
+
+

+ для одной строки. +

+

+ Подтип зависит от поставщика. Встроенные поставщики Android обычно содержат простой +подтип. Например, когда приложение «Контакты» создает строку для номера телефона, +оно задает следующий тип MIME в этой строке: +

+
+vnd.android.cursor.item/phone_v2
+
+

+ Обратите внимание, что значение подтипа просто phone_v2. +

+

+ Разработчики поставщиков могут создавать свои собственные шаблоны подтипов на основе +центра и названий таблиц поставщика. Например, рассмотрим поставщик, который содержит расписание движения поездов. + Центром поставщика является com.example.trains, в котором содержатся таблицы +Line1, Line2 и Line3. В ответ на следующий URI контента +

+

+

+content://com.example.trains/Line1
+
+

+ для таблицы Line1 поставщик возвращает следующий тип MIME +

+
+vnd.android.cursor.dir/vnd.example.line1
+
+

+ В ответ на следующий URI контента +

+
+content://com.example.trains/Line2/5
+
+

+ для строки 5 в таблице Line2 поставщик возвращает следующий тип MIME +

+
+vnd.android.cursor.item/vnd.example.line2
+
+

+ В большинстве поставщиков контента определены константы класса-контракта для используемых в них типов MIME. Например, класс-контракт +{@link android.provider.ContactsContract.RawContacts} +поставщика контактов определяет константу +{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} для типа MIME одной +строки необработанного контакта. +

+

+ URI контента для единичных строк описываются в разделе +URI контента. +

diff --git a/docs/html-intl/intl/ru/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/ru/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..d8f787393eaad757fe1d1d7c5083042b997014d3 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=Создание поставщика контента +@jd:body + + + +

+ Поставщик контента управляет доступом к центральному репозиторию данных. Реализация +поставщика включает один или несколько классов в приложении Android, а также элементы +в файле манифеста. Один из классов реализует подкласс +{@link android.content.ContentProvider}, который выступает в роли интерфейса между вашим поставщиком и +другими приложениями. Несмотря на то, что поставщики контента изначально предназначены для предоставления доступа к данным +другим приложениям, в вашем приложении, несомненно, могут содержаться операции, которые разрешают пользователю +запрашивать и изменять данные, управляемые вашим поставщиком. +

+

+ В данной статье представлены базовые инструкции по созданию поставщика контента и список необходимых для этого +API-интерфейсов. +

+ + + +

Подготовка к созданию поставщика

+

+ Прежде чем приступить к созданию поставщика, выполните указанные ниже действия. +

+
    +
  1. + Решите, нужен ли вообще вам поставщик контента. Поставщик +контента требуется в случаях, если вы хотите реализовать в своем приложении одну или несколько следующих функций: +
      +
    • предоставление сложных данных или файлов другим приложениям;
    • +
    • предоставление пользователям возможности копировать сложные данные из вашего приложения в другие приложения;
    • +
    • предоставление настраиваемых поисковых подсказок с помощью платформы поиска.
    • +
    +

    + Вам не нужен поставщик для работы с базой данных SQLite, если ее планируется использовать +исключительно в вашем приложении. +

    +
  2. +
  3. + Если вы еще не приняли окончательное решение, ознакомьтесь со +статьей +Основные сведения о поставщике контента, чтобы узнать подробнее о поставщиках контента. +
  4. +
+

+ После этого можно приступать к созданию поставщика. Для этого выполните указанные ниже действия. +

+
    +
  1. + Спроектируйте базовое хранилище для своих данных. Поставщик контента предоставляет данные двумя способами: +
    +
    + Данные для файлов +
    +
    + Данные, которые обычно поступают в файлы, такие как +фотографии, аудио- или видеоданные. Файлы следует хранить в закрытом +пространстве вашего приложения. В ответ на запрос файла из другого приложения +ваш поставщик может предложить дескриптор для файла. +
    +
    + Структурированные данные +
    +
    + Данные, которые обычно поступают в базу данных, массив или аналогичную структуру. + Данные следует хранить в той форме, которая совместима с таблицами из строк и столбцов. Строка +представляет собой объект, например, пользователя, позицию или учетную единицу. Столбец +представляет собой некоторые данные об объекте, например, имя пользователя или стоимость единицы. Обычно +данные такого типа хранятся в базе данных SQLite, однако вы можете использовать постоянное хранилище +любого типа. Дополнительные сведения о типах хранилищ, доступных в системе +Android, представлены в разделе +Проектирование хранилища данных. +
    +
    +
  2. +
  3. + Определите конкретную реализацию класса {@link android.content.ContentProvider} +и его необходимые методы. Этот класс выступает в роли интерфейса между вашими данными и остальной частью системы +Android. Дополнительные сведения об этом классе представлены в разделе +Реализация класса ContentProvider. +
  4. +
  5. + Определите строку центра поставщика, его URI контента и имена столбцов. Если необходимо, +чтобы приложение поставщика обрабатывало намерения, также необходимо определить действия намерений, дополнительные данные +и флаги. Кроме того, необходимо определить разрешения, которые будут необходимы приложениям для доступа к вашим +данным. Все эти значения следует определить как константы в отдельном +классе-контракте; в дальнейшем этот класс можно предоставить другим разработчикам. Дополнительные сведения о + URI контента представлены в разделе +Проектирование URI контента. + Дополнительные сведения о намерениях представлены в разделе +Намерения и доступ к данным. +
  6. +
  7. + Добавьте другие дополнительные компоненты, например, демонстрационные данные или реализация адаптера +{@link android.content.AbstractThreadedSyncAdapter}, который служит для синхронизации данных между +поставщиком и облаком. +
  8. +
+ + + +

Проектирование хранилища данных

+

+ Поставщик контента представляет собой интерфейс для передачи данных, сохраненных в структурированном формате. Прежде чем создавать +интерфейс, определите способ хранения данных. Данные можно хранить +в любой форме, а затем спроектировать интерфейс для чтения и записи данных при необходимости. +

+

+ В Android имеются некоторые технологии хранения данных: +

+
    +
  • + В системе Android имеется API базы данных SQLite, который используется собственными поставщиками Android для +хранения табличных данных. С помощью класса +{@link android.database.sqlite.SQLiteOpenHelper} можно создавать базы данных, а класс +{@link android.database.sqlite.SQLiteDatabase} представляет собой базовый класс для доступа +к базам данных. +

    + Обратите внимание, что вам не обязательно использовать базу данных для реализации своего репозитория. Поставщик представляет собой +внешний набор таблиц, как в случае с реляционной базой данных, однако это +не является требованием к внутренней реализации поставщика. +

    +
  • +
  • + Для хранения данных файлов в Android предусмотрены различные API-интерфейсы для работы с файлами. + Дополнительные сведения о хранилище файлов представлены в статье +Хранилище данных. Если вы +проектируете поставщик, который предлагает мультимедийные данные, такие как музыка или видео, можно создать поставщик, +объединяющий табличные данные и файлы. +
  • +
  • + Для работы с сетевыми данными используйте классы в {@link java.net} и +{@link android.net}. Вы также можете синхронизировать сетевые данные с локальным +хранилищем данных (например, с базой данных), а затем предоставить такие данные в виде таблиц или файлов. + Такой тип синхронизации демонстрируется в +примере приложения адаптера синхронизации. +
  • +
+

+ Рекомендации по проектированию данных +

+

+ Вот несколько советов и рекомендаций, касающихся проектирования структуры данных поставщика: +

+
    +
  • + В табличных данных всегда должен быть столбец для «основного ключа», который поставщик хранит +в виде уникального числового значения для каждой строки. Вы можете использовать это значение для связывания строки +со строками в других таблицах (используя его в качестве «внешнего ключа»). Несмотря на то, что вы можете использовать любое имя +для этого столбца, рекомендуется указать имя {@link android.provider.BaseColumns#_ID BaseColumns._ID}, +поскольку для связывания результатов запроса поставщика с +{@link android.widget.ListView} необходимо, чтобы один из получаемых столбцов назывался +_ID. +
  • +
  • + Если вы планируете предоставлять растровые изображения или очень большие фрагменты данных для файлов, то данные +следует хранить в файлах, а затем предоставлять их косвенно вместо хранения таких данных прямо в +таблице. В таком случае вам необходимо сообщить пользователям вашего поставщика о том, что для доступа к данным им потребуется воспользоваться методом +{@link android.content.ContentResolver}. +
  • +
  • + Для хранения данных разного размера или с разной структурой используйте тип +BLOB. Например, столбец BLOB можно использовать для хранения +буфера протокола или +структуры JSON. +

    + BLOB также можно использовать для реализации таблицы, не зависящей от схемы. В таблице +такого типа определяются столбец основного ключа, столбец типа MIME и один +или несколько общих столбцов BLOB. На смысл данных в столбцах BLOB +указывает значение в столбце типа MIME. Благодаря этому в одной и той же таблице можно хранить строки +разных типов. Примером таблицы, не зависящей от схемы, может служить таблица с данными поставщика +контента +{@link android.provider.ContactsContract.Data}. +

    +
  • +
+ +

Проектирование URI контента

+

+ URI контента представляет собой URI, который определяет данные в поставщике. URI контента +могут включать символическое имя всего поставщика (его центр) и +имя, которое указывает на таблицу или файл (путь). Дополнительная часть URI с идентификатором +указывает на отдельную строку в таблице. У каждого метода доступа к данным в классе +{@link android.content.ContentProvider} имеется URI контента (в виде аргумента); благодаря этому вы можете +определить таблицу, строку или файл для доступа. +

+

+ Базовые сведения о URI контента представлены в +статье +Основные сведения о поставщике контента. +

+

Проектирование центра поставщика

+

+ У поставщика обычно имеется только один центр, который выступает в качестве его внутреннего имени в системе Android. Во +избежание конфликтов с другими поставщиками в качестве основы центра поставщика должны выступать сведения о владении доменом в Интернете +(в обратном порядке). Поскольку эта рекомендация также применяется и к названиям пакетов Android, +вы можете определить центр своего поставщика в виде расширения названия +пакета, в котором содержится поставщик. Например, если пакет Android называется +com.example.<appname>, то центром +вашего поставщика должен быть com.example.<appname>.provider. +

+

Проектирование структуры пути

+

+ Обычно разработчики создают URI контента на основе центра поставщика, добавляя к нему путь, который указывает +на отдельные таблицы. Например, если имеется две таблицы, table1 и +table2, центр поставщика из предыдущего примера следует объединить для формирования +следующих URI контента: +com.example.<appname>.provider/table1 и +com.example.<appname>.provider/table2. Пути не ограничены +одним сегментом, и не на каждом уровне пути имеется таблица. +

+

Обработка идентификаторов URI контента

+

+ Обычно поставщики предоставляют доступ к одной строке в таблице путем принятия URI контента, +в конце которого указано значение идентификатора строки. Также поставщики обычно проверяют совпадение +значения идентификатора по столбцу _ID в таблице и предоставляют запрашиваемый доступ к +соответствующей строке. +

+

+ Это упрощает создание общего метода проектирования для приложений, получающих доступ к поставщику. Приложение +отправляет запрос поставщику и отображает полученный в результате такого запроса объект {@link android.database.Cursor} +в объекте {@link android.widget.ListView} с помощью {@link android.widget.CursorAdapter}. + Для определения {@link android.widget.CursorAdapter} необходимо, чтобы один из столбцов в объекте +{@link android.database.Cursor} назывался _ID +

+

+ Затем пользователь выбирает в пользовательском интерфейсе одну из отображаемых строк, чтобы просмотреть данные +или изменить их. Приложение получает соответствующую строку из объекта{@link android.database.Cursor} в базовом объекте +{@link android.widget.ListView}, получает значение _ID для этой строки, добавляет его к +URI контента, а затем отправляет поставщику запрос на доступ. Затем поставщик может +запросить или изменить строку, выбранную пользователем. +

+

Шаблоны URI контента

+

+ Чтобы помочь вам в выборе действия для выполнения со входящим URI контента, в API поставщика +имеется класс {@link android.content.UriMatcher}, который сопоставляет шаблоны URI контента с +целочисленными значениями. Такие целочисленные значения можно использовать в операторе switch, +который выбирает подходящее действие для URI контента, которые соответствуют определенному шаблону. +

+

+ Для определения совпадения URI контента с шаблоном используются подстановочные символы: +

+
    +
  • + *: соответствие строке любой длины с любыми допустимыми символами; +
  • +
  • + #: соответствие строке любой длины с цифрами. +
  • +
+

+ В качестве примера для проектирования и написания кода для обработки URI контента +рекомендуется использовать центр поставщикаcom.example.app.provider, который распознает +следующие URI контента, указывающие на таблицы: +

+
    +
  • + content://com.example.app.provider/table1: таблица table1; +
  • +
  • + content://com.example.app.provider/table2/dataset1: таблица +dataset1; +
  • +
  • + content://com.example.app.provider/table2/dataset2: таблица +dataset2; +
  • +
  • + content://com.example.app.provider/table3: таблица table3. +
  • +
+

+ Поставщик также распознает следующие URI контента, если к ним добавлен идентификатор строки (например, +content://com.example.app.provider/table3/1 для строки с +идентификатором1 в таблице table3. +

+

+ Возможно использование следующих шаблонов URI контента: +

+
+
+ content://com.example.app.provider/* +
+
+ Совпадает с любым URI контента в поставщике. +
+
+ content://com.example.app.provider/table2/*: +
+
+ Совпадает с URI контента в таблицах dataset1dataset2, однако не совпадает с URI контента в таблице table1 или +table3. +
+
+ content://com.example.app.provider/table3/#: Совпадает с URI контента +для отдельных строк в таблице table3, такими как +content://com.example.app.provider/table3/6 для строки с идентификатором +6. +
+
+

+ Во фрагменте кода ниже показано, как работают методы в классе {@link android.content.UriMatcher}. + Этот код обрабатывает URI для всей таблицы иначе, чем для URI +для отдельной строки, используя шаблон URI контента +content://<authority>/<path> для таблиц и шаблон +content://<authority>/<path>/<id> — для отдельных строк. +

+

+ Метод {@link android.content.UriMatcher#addURI(String, String, int) addURI()} сопоставляет +центр поставщика и его путь с целочисленным значением. Метод {@link android.content.UriMatcher#match(Uri) +match()} возвращает целочисленное значение для URI. Оператор switch +выбирает, следует ли ему выполнить запрос всей таблицы или только отдельной записи: +

+
+public class ExampleProvider extends ContentProvider {
+...
+    // Creates a UriMatcher object.
+    private static final UriMatcher sUriMatcher;
+...
+    /*
+     * The calls to addURI() go here, for all of the content URI patterns that the provider
+     * should recognize. 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
+     */
+    sUriMatcher.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.
+     */
+    sUriMatcher.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 (sUriMatcher.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 is not recognized, you should do some error handling here.
+        }
+        // call the code to actually do the query
+    }
+
+

+ Другой класс, {@link android.content.ContentUris}, предоставляет удобные методы для работы с частью +id URI контента. Классы {@link android.net.Uri} и +{@link android.net.Uri.Builder} содержат удобные методы для синтаксического анализа существующих объектов +{@link android.net.Uri} и создания новых. +

+ + +

Реализация класса ContentProvider

+

+ Экземпляр класса {@link android.content.ContentProvider} управляет доступом к структурированному набору данных + путем обработки запросов от других приложений. В конечном счете, при всех формах доступа +вызывается метод {@link android.content.ContentResolver}, который затем вызывает конкретный метод +{@link android.content.ContentProvider} для получения доступа. +

+

Необходимые методы

+

+ В абстрактном классе {@link android.content.ContentProvider} определены шесть абстрактных методов, +которые необходимо реализовать в рамках вашего собственного конкретного подкласса. Все указанные ниже методы, кроме +{@link android.content.ContentProvider#onCreate() onCreate()}, вызываются клиентским приложением, +которое пытается получить доступ к вашему поставщику контента. +

+
+
+ {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) +query()} +
+
+ Получение данных от поставщика. Использует аргументы для выбора таблицы для запроса, +строк и столбцов, которые необходимо возвратить, и указания порядка сортировки результатов. + Возвращает данные в виде объекта {@link android.database.Cursor}. +
+
+ {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} +
+
+ Вставка строки в ваш поставщик. Использует аргументы для выбора +конечной таблицы и получения значений столбца, которые следует использовать. Возвращает URI контента +для новой вставленной строки. +
+
+ {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) +update()} +
+
+ Обновление существующих строк в поставщике. Использует аргументы для выбора +таблицы и строк для обновления, а также для получения обновленных значений столбца. Возвращает количество обновленных строк. +
+
+ {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +
+
+ Удаление строк из поставщика. Использует аргументы для выбора +таблицы и строк для удаления. Возвращает количество удаленных строк. +
+
+ {@link android.content.ContentProvider#getType(Uri) getType()} +
+
+ Возвращение типа MIME, соответствующего URI контента. Дополнительные сведения об этом методе представлены в разделе +Реализация типов MIME поставщика контента. +
+
+ {@link android.content.ContentProvider#onCreate() onCreate()} +
+
+ Инициализация поставщика. Система Android вызывает этот метод сразу после +создания вашего поставщика. Обратите внимание, что поставщик не будет создан до тех пор, пока объект +{@link android.content.ContentResolver} не прекратит попытки получить доступ к нему. +
+
+

+ Подпись этих методов аналогична подписи для идентичных методов в объекте +{@link android.content.ContentResolver}. +

+

+ При реализации этих методов следует учитывать указанные ниже моменты. +

+
    +
  • + Все эти методы, кроме {@link android.content.ContentProvider#onCreate() onCreate()}, +можно вызвать сразу из нескольких потоков, поэтому они должны быть реализованы с сохранением потокобезопасности. Дополнительные сведения об +использовании нескольких потоков представлены в +статье +Процессы и потоки. +
  • +
  • + Избегайте слишком длинных операций в методе {@link android.content.ContentProvider#onCreate() +onCreate()}. Отложите выполнение задач инициализации до тех пор, пока они не потребуются. + Дополнительные сведения об этом представлены в разделе +Реализация метода onCreate. +
  • +
  • + Несмотря на то, что вы должны реализовать эти методы, ваш код необязательно должен выполнять какие-либо другие действия, кроме +возврата ожидаемого типа данных. Например, может потребоваться, чтобы другие приложения не имели возможности +вставлять данные в некоторые таблицы. Для этого можно игнорировать вызов метода +{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} и возвратить +0. +
  • +
+

Реализация метода query()

+

+ Метод +{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) +ContentProvider.query()} должен возвращать объект {@link android.database.Cursor}, а при сбое выдавать +{@link java.lang.Exception}. Если в качестве хранилища данных используется база данных SQLite, +можно просто возвратить объект {@link android.database.Cursor}, который был возвращен одним из методов +query() класса {@link android.database.sqlite.SQLiteDatabase}. + Если запрос не соответствует ни одной строке, следует возвратить экземпляр объекта{@link android.database.Cursor}, метод +{@link android.database.Cursor#getCount()} которого возвращает 0. + null следует возвращать только в том случае, если во время обработки запроса произошла внутренняя ошибка. +

+

+ Если вы не используете базу данных SQLite в качестве хранилища данных, обратитесь к одному из конкретных подклассов объекта +{@link android.database.Cursor}. Например, класс {@link android.database.MatrixCursor} +реализует объект cursor, в котором каждая строка представляет собой массив класса {@link java.lang.Object}. С помощью этого класса воспользуйтесь методом +{@link android.database.MatrixCursor#addRow(Object[]) addRow()}, чтобы добавить новую строку. +

+

+ Следует помнить, что система Android должна иметь возможность взаимодействовать с {@link java.lang.Exception} +в пределах процесса. Система Android позволяет это делать для указанных ниже исключений, которые могут быть полезны при обработке +ошибок запросов. +

+
    +
  • + {@link java.lang.IllegalArgumentException} (это исключение можно выдать в случае, +если поставщик получает недопустимый URI контента); +
  • +
  • + {@link java.lang.NullPointerException}. +
  • +
+

Реализация метода insert()

+

+ Метод {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} добавляет новую +строку в соответствующую строку, используя значения в аргументе +{@link android.content.ContentValues}. Если в аргументе {@link android.content.ContentValues} отсутствует имя столбца, +возможно, потребуется указать для него значение по умолчанию (либо в коде поставщика, либо в +схеме базы данных). +

+

+ Этот метод должен возвращать URI контента для новой строки. Для этого добавьте значение +_ID новой строки (или иной основной ключ) к URI контента таблицы, используя метод +{@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}. +

+

Реализация метода delete()

+

+ Методу {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +необязательно фактически удалять строки из вашего хранилища данных. Если для работы с поставщиком используется адаптер синхронизации, +рассмотрите возможность отметки удаленной строки флагом +delete вместо окончательного удаления строки. Адаптер синхронизации может +проверить наличие удаленных строк с флагом delete и удалить их с сервера перед удалением их из поставщика. +

+

Реализация метода update()

+

+ Метод {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) +update()} принимает тот же аргумент {@link android.content.ContentValues}, который используется методом +{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}, и те же аргументы +selection и selectionArgs, которые используются методами +{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} и +{@link android.content.ContentProvider#query(Uri, String[], String, String[], String) +ContentProvider.query()}. Благодаря этому код можно повторно использовать между данными методами. +

+

Реализация метода onCreate()

+

+ Система Android вызывает метод {@link android.content.ContentProvider#onCreate() +onCreate()} при запуске поставщика. В этом методе следует выполнять только быстро выполняющиеся задачи +инициализации, а создание базы данных и загрузку данных отложить до момента фактического получения +поставщиком запроса на данные. Слишком длинные операции в методе +{@link android.content.ContentProvider#onCreate() onCreate()} приводят к увеличению времени +запуска поставщика. В свою очередь, это увеличивает время отклика поставщика на запросы от других +приложений. +

+

+ Например, если вы используете базу данных SQLite, +в методе +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} можно создать новый объект +{@link android.database.sqlite.SQLiteOpenHelper}, а затем создать таблицы SQL при первом открытии базы данных. Чтобы упростить это, при первом вызове метода +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase +getWritableDatabase()} он автоматически вызывает метод +{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()}. +

+

+ В двух указанных ниже фрагментах кода иллюстрируется взаимодействие между методом +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} и методом +{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()}. В первом фрагменте кода представлена реализация метода +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}: +

+
+public class ExampleProvider extends ContentProvider
+
+    /*
+     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
+     * in a following snippet.
+     */
+    private MainDatabaseHelper mOpenHelper;
+
+    // Defines the database name
+    private static final String DBNAME = "mydb";
+
+    // Holds the database object
+    private SQLiteDatabase db;
+
+    public boolean onCreate() {
+
+        /*
+         * Creates a new helper object. This method always returns quickly.
+         * Notice that the database itself isn't created or opened
+         * until SQLiteOpenHelper.getWritableDatabase is called
+         */
+        mOpenHelper = new MainDatabaseHelper(
+            getContext(),        // the application context
+            DBNAME,              // the name of the database)
+            null,                // uses the default SQLite cursor
+            1                    // the version number
+        );
+
+        return true;
+    }
+
+    ...
+
+    // Implements the provider's insert method
+    public Cursor insert(Uri uri, ContentValues values) {
+        // Insert code here to determine which table to open, handle error-checking, and so forth
+
+        ...
+
+        /*
+         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
+         *
+         */
+        db = mOpenHelper.getWritableDatabase();
+    }
+}
+
+

+ В следующем фрагменте кода представлена реализация метода +{@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()}, включая вспомогательный класс: +

+
+...
+// A string that defines the SQL statement for creating a table
+private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
+    "main " +                       // Table's name
+    "(" +                           // The columns in the table
+    " _ID INTEGER PRIMARY KEY, " +
+    " WORD TEXT"
+    " FREQUENCY INTEGER " +
+    " LOCALE TEXT )";
+...
+/**
+ * Helper class that actually creates and manages the provider's underlying data repository.
+ */
+protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
+
+    /*
+     * Instantiates an open helper for the provider's SQLite data repository
+     * Do not do database creation and upgrade here.
+     */
+    MainDatabaseHelper(Context context) {
+        super(context, DBNAME, null, 1);
+    }
+
+    /*
+     * Creates the data repository. This is called when the provider attempts to open the
+     * repository and SQLite reports that it doesn't exist.
+     */
+    public void onCreate(SQLiteDatabase db) {
+
+        // Creates the main table
+        db.execSQL(SQL_CREATE_MAIN);
+    }
+}
+
+ + + +

Реализация типов MIME для класса ContentProvider

+

+ В классе {@link android.content.ContentProvider} предусмотрены два метода для возврата типов MIME: +

+
+
+ {@link android.content.ContentProvider#getType(Uri) getType()} +
+
+ Один из необходимых методов, который требуется реализовать для каждого поставщика. +
+
+ {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +
+
+ Метод, который нужно реализовать в случае, если поставщик предоставляет файлы. +
+
+

Типы MIME для таблиц

+

+ Метод {@link android.content.ContentProvider#getType(Uri) getType()} возвращает объект +{@link java.lang.String} в формате MIME, который описывает тип данных, возвращаемых аргументом +URI контента. Аргумент {@link android.net.Uri} может выступать в качестве шаблона, а не в виде определенного URI; +в этом случае необходимо возвратить тип данных, связанный с URI контента, который соответствует +шаблону. +

+

+ Для общих типов данных, таких как текст, HTML или JPEG, метод +{@link android.content.ContentProvider#getType(Uri) getType()} должен возвращать стандартный тип +MIME. Полный список стандартных типов представлен на +веб-сайте +IANA MIME Media Types. +

+

+ Для URI контента, которые указывают на одну или несколько строк табличных данных, метод +{@link android.content.ContentProvider#getType(Uri) getType()} должен возвращать +тип MIME в формате MIME поставщика, который имеется в системе Android: +

+
    +
  • + Часть типа: vnd +
  • +
  • + Часть подтипа: +
      +
    • + Если шаблон URI предназначен для одной строки: android.cursor.item/ +
    • +
    • + Если шаблон URI предназначен для нескольких строк: android.cursor.dir/ +
    • +
    +
  • +
  • + Часть поставщика: vnd.<name>.<type> +

    + Вы указываете <name> и <type>. + Значение <name> должно быть уникальным глобально, +а значение <type> должно быть уникальным для соответствующего шаблона +URI. В качестве <name> рекомендуется использовать название вашей компании +или часть названия пакета Android вашего приложения. В качестве +<type> рекомендуется использовать строку, которая определяет связанную с +URI таблицу. +

    + +
  • +
+

+ Например, если центром поставщика является +com.example.app.provider, который предоставляет таблицу +table1, то тип MIME для нескольких строк в таблице table1 будет следующим: +

+
+vnd.android.cursor.dir/vnd.com.example.provider.table1
+
+

+ Для одной строки в таблице table1 тип MIME будет следующим: +

+
+vnd.android.cursor.item/vnd.com.example.provider.table1
+
+

Типы MIME для файлов

+

+ Если поставщик предоставляет файлы, необходимо реализовать метод +{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}. + Этот метод возвращает массив {@link java.lang.String} с типами MIME для файлов, +возвращаемых поставщиком для заданного URI контента. Предлагаемые поставщиком типы следует сортировать с помощью аргумента фильтра типов MIME, +чтобы возвращались только те типы MIME, которые необходимо обработать клиенту. +

+

+ Например, рассмотрим поставщик, который предоставляет фотографии в виде файлов в форматах .jpg, +.png и .gif. + Если приложение вызывает метод {@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()} со строкой фильтра image/* (нечто вроде «изображения»), +то метод {@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} +должен возвращать следующий массив: +

+
+{ "image/jpeg", "image/png", "image/gif"}
+
+

+ Если же приложению требуются только файлы .jpg, то оно вызывает метод +{@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()} со строкой фильтра *\/jpeg; метод +{@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} при этом должен возвращать следующее: +

+{"image/jpeg"}
+
+

+ Если поставщик не предоставляет ни один из типов MIME, запрошенных в строке фильтра, то метод +{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +должен возвращать null. +

+ + + +

Реализация класса-контракта

+

+ Класс-контракт представляет собой класс public final, в котором содержатся определения констант для +URI, имен столбцов, типов MIME и других метаданных поставщика. Класс +устанавливает контрактные отношения между поставщиком и другими приложениями путем обеспечения +прямого доступа к поставщику даже в случае изменения фактических значений URI, имен столбцов и +т. д. +

+

+ Класс-контракт также полезен для разработчиков тем, что в нем содержатся мнемонические имена для его констант, +благодаря чему снижается риск того, что разработчики воспользуются неправильными значениями для имен столбцов или URI. Поскольку это класс, +он может содержать документацию Javadoc. Интегрированные среды разработки, такие как +Eclipse, могут автоматически заполнять имена констант из класса-контракта и отображать Javadoc для +констант. +

+

+ У разработчиков нет доступа к файлу класса-контракта из вашего приложения, однако они могут +статически скомпилировать класс-контракт в свое приложение из предоставленного вами файла .jar. +

+

+ Примом класса-контракта может служить класс +{@link android.provider.ContactsContract} и его вложенные классы. +

+

Реализация разрешений поставщика контента

+

+ Разрешения и доступ ко всем компонентам системы Android подробно описаны в статье +Безопасность и разрешения. + Кроме того, в статье Хранилище данных +представлены сведения о безопасности и разрешениях, применяемых к различным типам хранилища. + Ниже приведен краткий обзор основных моментов. +

+
    +
  • + По умолчанию файлы с данными хранятся во внутреннем хранилище устройства и доступны только +вашему приложению и поставщику. +
  • +
  • + Создаваемые вами базы данных {@link android.database.sqlite.SQLiteDatabase} также доступны только вашему +приложению и поставщику. +
  • +
  • + Файлы с данными, которые вы сохраняете во внешнем хранилище, по умолчанию являются общедоступными, которые +может считать любой пользователь. Вам не удастся использовать поставщик контента для ограничения доступа к файлам, +которые хранятся во внешнем хранилище, поскольку приложения могут использовать другие вызовы API для их чтения или записи. +
  • +
  • + Вызовы метода для открытия или создания файлов или баз данных SQLite, находящихся во внутреннем хранилище на вашем устройстве, +потенциально могут предоставить всем другим приложениям доступ как на запись, так и на чтение данных. Если вы используете +внутренний файл или базу данных в качестве репозитория поставщика, выполнить чтение или запись данных в котором может +любой пользователь, то разрешений, заданных в манифесте поставщика, будет явно недостаточно для +защиты ваших данных. По умолчанию доступ к файлам и базам данных +во внутреннем хранилище является закрытым, и вам не следует изменять параметры доступа к репозиторию вашего поставщика. +
  • +
+

+ При необходимости использовать разрешения поставщика контента для управления доступом к данным, данные +следует хранить во внутренних файлах, в базах данных SQLite или в облаке (например, +на удаленном сервере), а доступ к файлам и базам данных должен быть предоставлен только вашему приложению. +

+

Реализация разрешений

+

+ Любое приложение может выполнять чтение данных в поставщике или записывать их, даже если соответствующие данные +являются закрытыми, поскольку по умолчанию для поставщика не заданы разрешения. Чтобы изменить эти настройки, +задайте разрешения для поставщика в файле манифеста с помощью атрибутов элемента + + <provider> или его дочерних элементов. Можно задать разрешения, которые применяются ко всему поставщику +или только к определенным таблицам, либо даже только к определенным записям или всему дереву. +

+

+ Для задания разрешений используется один или несколько +элементов + <permission> в файле манифеста. Чтобы разрешения +были уникальными для поставщика, используйте области, аналогичные Java, для атрибута + + android:name. Например, присвойте разрешению на чтение имя +com.example.app.provider.permission.READ_PROVIDER. + +

+

+ Ниже перечислены области разрешений для поставщика, начиная с разрешений, +которые применяются ко всему поставщику, и заканчивая более подробными разрешениями. + Более подробные разрешения имеют преимущество над разрешениями с более широкими областями. +

+
+
+ Единичное разрешение на чтение/запись на уровне поставщика +
+
+ Единичное разрешение, которое управляет доступом как на чтение, так и на запись для всего поставщика, которое задается с помощью атрибута + + android:permission элемента + + <provider>. +
+
+ Отдельное разрешение на чтение/запись на уровне поставщика +
+
+ Разрешение на чтение и запись для всего поставщика. Такие разрешения задаются с помощью атрибутов + + android:readPermission и + + android:writePermission элемента + + <provider>. Они имеют преимущественную силу над разрешением, заданным с помощью атрибута + + android:permission. +
+
+ Разрешение на уровне пути +
+
+ Разрешение на чтение, запись или чтение/запись для URI контента в поставщике. Каждый +URI, которым необходимо управлять, задается с помощью дочернего элемента + + <path-permission> элемента + + <provider>. Для каждого указываемого URI контента можно задать разрешение +на чтение/запись, только чтение или только запись, либо все три разрешения. Разрешения на чтение и +запись имеют преимущественную силу над разрешением на чтение/запись. Кроме того, разрешения на уровне пути +имеют преимущественную силу над разрешениями на уровне поставщика. +
+
+ Временное разрешение +
+
+ Разрешения этого уровня предоставляют приложению временный доступ, даже если у приложения +нет разрешений, которые обычно требуются. Функция временного доступа +ограничивает набор разрешений, которые приложению необходимо запросить в своем +манифесте. Если включены временные разрешения, единственными приложениями, +которым требуются «постоянные» разрешения на работу с поставщиком, являются те, которые непрерывно получают доступ ко всем вашим +данным. +

+ Рассмотрим пример с разрешениями, которые необходимо реализовать для поставщика электронной почты и приложения, когда вам +необходимо разрешить внешнему приложению для просмотра изображений отображать вложенные в письма фотографии +из поставщика. Чтобы предоставить средству просмотра изображений требуемый доступ без запроса разрешений, +задайте временные разрешения для URI контента фотографий. Спроектируйте ваше приложение для работы с электронной почтой таким образом, +чтобы в случаях, когда пользователь желает отобразить фотографию, приложение отправляло намерение, в котором содержится +URI контента фотографии и флаги разрешения для средства просмотра изображений. Затем средство просмотра изображений +может отправить поставщику эл. почты запрос на получение фотографии, даже если у средства просмотра отсутствует обычное +разрешение на чтение данных из поставщика. +

+

+ Чтобы включить временные разрешения, задайте атрибут + + android:grantUriPermissions для элемента + + <provider>, либо добавьте один или несколько дочерних элементов + + <grant-uri-permission>в ваш элемент + + <provider>. Если вы используете временные разрешения, вам необходимо вызывать метод +{@link android.content.Context#revokeUriPermission(Uri, int) +Context.revokeUriPermission()} каждый раз, когда вы осуществляете удаленную поддержку URI контента +из поставщика, а URI контента связан с временным разрешением. +

+

+ Значение атрибута определяет, какая часть поставщика доступна. + Если для атрибута задано значение true, система предоставит временные +разрешения для всего поставщика, отменяя тем самым любые другие разрешения, которые требуются +на уровне поставщика или на уровне пути. +

+

+ Если для флага задано значение false, вам необходимо добавить дочерние элементы + + <grant-uri-permission> в свой элемент + + <provider>. Каждый дочерний элемент задает URI контента, +для которых предоставляется временное разрешение. +

+

+ Чтобы делегировать приложению временный доступ, в намерении должны быть указаны флаги +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} или +{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} , либо оба флага. Эти флаги задаются с помощью метода +{@link android.content.Intent#setFlags(int) setFlags()}. +

+

+ Если атрибут + android:grantUriPermissions отсутствует, предполагается, что его значение +false. +

+
+
+ + + + +

Элемент <provider>

+

+ Как и компоненты {@link android.app.Activity} и {@link android.app.Service}, +подкласс класса {@link android.content.ContentProvider} +должен быть определен в файле манифеста приложения с помощью элемента + + <provider>. Ниже указана информация, которую система Android получает из +этого элемента. +

+
+ Центр поставщика +({@code +android:authorities}) +
+
+ Символические имена, которые идентифицируют весь поставщик в системе. Дополнительные сведения об этом атрибуте представлены в +разделе +Проектирование URI контента. +
+
+ Название класса поставщика +( +android:name + ) +
+
+ Класс, который реализует класс {@link android.content.ContentProvider}. Дополнительные сведения об этом классе представлены в +разделе +Реализация класса ContentProvider. +
+
+ Разрешения +
+
+ Атрибуты, которые определяют разрешения, необходимые другим приложениям для +доступа к данным в поставщике: + +

+ Дополнительные сведения о разрешениях и соответствующих атрибутах представлены в +разделе +Реализация разрешений поставщика контента. +

+
+
+ Атрибуты запуска и управления +
+
+ Следующие атрибуты определяют порядок и время запуска поставщика системой Android, характеристики +процесса поставщика, а также другие параметры выполнения: +
    +
  • + + android:enabled: флаг, позволяющий системе запускать поставщик; +
  • +
  • + + android:exported: флаг, позволяющий другим приложениям использовать этот поставщик; +
  • +
  • + + android:initOrder: порядок запуска поставщика +(относительно других поставщиков в одном и том же процессе); +
  • +
  • + + android:multiProcess: флаг, позволяющий системе запускать поставщик +в том же процессе, что и вызывающий клиент; +
  • +
  • + + android:process: название процесса, в котором +запускается поставщик; +
  • +
  • + + android:syncable: флаг, указывающий на то, что данные в поставщике +следует синхронизировать с данными на сервере. +
  • +
+

+ Полная документация по атрибутам представлена в статье, посвященной +элементу + + <provider>. +

+
+
+ Информационные атрибуты +
+
+ Дополнительный значок и метка для поставщика: +
    +
  • + + android:icon: графический ресурс, содержащий значок для поставщика. + Значок отображается рядом с меткой поставщика в списке приложений в разделе +Настройки > Приложения > Все. +
  • +
  • + + android:label: информационная метка с описанием поставщика или его +данных, либо обоих описаний. Метка отображается в списке приложений в разделе +Настройки > Приложения > Все. +
  • +
+

+ Полная документация по атрибутам представлена в статье, посвященной +элементу + <provider>. +

+
+
+ + +

Намерения и доступ к данным

+

+ Приложения могут получать доступ к поставщику контента в обход с помощью объектов {@link android.content.Intent}. + Приложение при этом не вызывает какие-либо методы классов {@link android.content.ContentResolver} или +{@link android.content.ContentProvider}. Вместо этого оно отправляет намерение, запускающе операцию, +которая обычно является частью собственного приложения поставщика. Получение и отображение данных в своем пользовательском интерфейсе +выполняет конечная операция. В зависимости от действия, указанного в намерении, +конечная операция также может предложить пользователю внести изменения в данные поставщика. + В намерении также могут содержаться дополнительные данные, которые конечная операция отображает в +пользовательском интерфейсе; затем пользователю предлагается возможность изменить эти данные, прежде чем использовать их для изменения +данных в поставщике. +

+

+ +

+

+ Возможно, доступ с помощью намерения потребуется использовать для обеспечения целостности данных. Для вставки, +обновления и удаления данных в поставщике может существовать строго определенный программный код, реализующий его функциональные возможности. В +этом случае предоставление другим приложениям прямого доступа для изменения данных +может привести к тому, что данные станут недействительными. Если вы хотите предоставить разработчикам возможность доступа посредством намерений, вам следует тщательно задокументировать такую функцию. + Объясните им, почему доступ посредством намерений через пользовательский интерфейс вашего приложения намного лучше изменения +данных посредством их кода. +

+

+ Обработка входящего намерения для изменения данных поставщика ничем не отличается +от обработки других намерений. Дополнительные сведения об использовании намерений представлены в статье +Объекты Intent и фильтры объектов Intent. +

diff --git a/docs/html-intl/intl/ru/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/ru/guide/topics/providers/content-providers.jd new file mode 100644 index 0000000000000000000000000000000000000000..3d7b6515c8a5240e568a0eab926a0b9243c27a72 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=Поставщики контента +@jd:body + +

+ Поставщики контента управляют доступом к структурированному набору данных. Они инкапсулируют данные +и предоставляют механизмы обеспечения их безопасности. Поставщики контента представляют собой стандартный +интерфейс для объединения данных в одном процессе с кодом, который выполняется в другом процессе. +

+

+ Когда вам требуется доступ к данным в поставщике контента, используйте объект +{@link android.content.ContentResolver} в интерфейсе +{@link android.content.Context} вашего приложения, чтобы подключиться к поставщику как клиент. + Объект {@link android.content.ContentResolver} взаимодействует с объектом поставщика, который представляет собой экземпляр класса, реализующий объект +{@link android.content.ContentProvider}. Объект +поставщика получает от клиентов запросы данных, выполняет запрашиваемые действия и +возвращает результаты. +

+

+ Вам не нужно разрабатывать собственный поставщик, если вы не планируете предоставлять доступ к своим данным +другим приложениям. Однако вам потребуется собственный поставщик для предоставления настраиваемых +поисковых подсказок в вашем собственном приложении. Вам также потребуется собственный поставщик, если вы хотите копировать и вставлять сложные данные или файлы из своего приложения +в другие приложения. +

+

+ В состав системы Android входят поставщики контента, которые управляют такими данными, как аудио, видео, изображения и +личная контактная информация. Некоторые из поставщиков указаны в справочной документации для +пакета +android.provider + . Работать с этими поставщиками может любое приложение Android +(однако с некоторыми ограничениями). +

+ Ниже перечислены статьи, в которых представлено более подробное описание поставщиков контента. +

+
+
+ +Основные сведения о поставщике контента +
+
+ Сведения о доступе к данным в поставщике контента, которые представлены в таблицах. +
+
+ +Создание поставщика контента +
+
+ Сведения о создании своего собственного поставщика контента. +
+
+ +Поставщик календаря +
+
+ Сведения о доступе к поставщику календаря, который входит в состав платформы Android. +
+
+ +Поставщик контактов +
+
+ Сведения о доступе к поставщику контактов, который входит в состав платформы Android. +
+
diff --git a/docs/html-intl/intl/ru/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/ru/guide/topics/providers/document-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..c594968490d4bd9db62f851df077e99dc7afa034 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=Платформа доступа к хранилищу (Storage Access Framework) +@jd:body + + + +

Платформа доступа к хранилищу (Storage Access Framework, SAF) впервые появилась в Android версии 4.4 (API уровня 19). Платформа SAF + облегчает пользователям поиск и открытие документов, изображений и других файлов +в хранилищах всех поставщиков, с которыми они работают. Стандартный удобный интерфейс +позволяет пользователям применять единый для всех приложений и поставщиков способ поиска файлов и доступа к последним добавленным файлам.

+ +

Облачные или локальные службы хранения могут присоединиться к этой экосистеме, реализовав +класс {@link android.provider.DocumentsProvider}, инкапсулирующий их услуги. Клиентские +приложения, которым требуется доступ к документам поставщика, могут интегрироваться с SAF с помощью всего нескольких +строчек кода.

+ +

Платформа SAF включает в себя следующие компоненты:

+ +
    +
  • Поставщик документов—поставщик контента, позволяющий +службе хранения (например, Диск Google) показывать файлы, которыми он управляет. Поставщик документов +реализуется как подкласс класса{@link android.provider.DocumentsProvider}. +Его схема основана на традиционной файловой иерархии, +однако физический способ хранения данных в поставщике документов остается на усмотрении разработчика. + Платформа Android включает в себя несколько встроенных поставщиков документов, таких как +Загрузки, Изображения и Видео.
  • + +
  • Клиентское приложение—пользовательское приложение, вызывающее намерение +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} и/или +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} и принимающее +файлы, возвращаемые поставщиками документов.
  • + +
  • Элемент выбора—системный пользовательский интерфейс, обеспечивающий пользователям доступ к документам у всех +поставщиков документов, которые удовлетворяют критериям поиска, заданным в клиентском приложении.
  • +
+ +

Платформа SAF в числе прочих предоставляет следующие функции:

+
    +
  • позволяет пользователям искать контент у всех поставщиков документов, а не только у одного приложения;
  • +
  • обеспечивает приложению возможность долговременного, постоянного доступа к + документам, принадлежащим поставщику документов. Благодаря такому доступу пользователи могут добавлять, редактировать, + сохранять и удалять файлы, хранящиеся у поставщика;
  • +
  • поддерживает несколько учетных записей и временные корневые каталоги, например, поставщики +на USB-накопителях, которые появляются, только когда накопитель вставлен в порт.
  • +
+ +

Обзор

+ +

В центре платформы SAF находится поставщик контента, являющийся +подклассом класса {@link android.provider.DocumentsProvider}. Внутри поставщика документовданные имеют +структуру традиционной файловой иерархии:

+

data model

+

Рисунок 1. Модель данных поставщика документов. На рисунке Root (Корневой каталог) указывает на один объект Document (Документ), +который затем разветвляется в целое дерево.

+ +

Обратите внимание на следующее.

+
    + +
  • Каждый поставщик документов предоставляет один или несколько +«корневых каталогов», являющихся отправными точками при обходе дерева документов. +Каждый корневой каталог имеет уникальный идентификатор {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID} +и указывает на документ (каталог), +представляющий содержимое на уровне ниже корневого. +Корневые каталоги динамичны по своей конструкции, чтобы обеспечивать поддержку таким вариантам использования, как несколько учетных записей, +временные хранилища на USB-нкопителях и возможность для пользователя войти в систему и выйти из нее.
  • + +
  • В каждом корневом каталоге находится один документ. Этот документ указывает на количество документов N +каждый из которых, в свою очередь, может указывать на один или N документов.
  • + +
  • Каждый сервер хранилища показывает +отдельные файлы и каталоги, ссылаясь на них с помощью уникального +идентификатора {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID}. +Идентификаторы документов должны быть уникальными и не меняться после присвоения, поскольку они используются для выдачи постоянных +URI, не зависящих от перезагрузки устройства.
  • + + +
  • Документ — это или открываемый файл (имеющий конкретный MIME-тип), или +каталог, содержащий другие документы (с +MIME-типом {@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR}).
  • + +
  • Каждый документ может иметь различные свойства, описываемые флагами +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}, +такими как{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}, +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE} и +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}. +Документ с одним и тем же идентификатором {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} может находиться +в нескольких каталогах.
  • +
+ +

Поток управления

+

Как было сказано выше, модель данных поставщика документов основана на традиционной +файловой иерархии. Однако физический способ хранения данных остается на усмотрение разработчика, при +условии, что к ним можно обращаться через API-интерфейс {@link android.provider.DocumentsProvider}. Например, можно +использовать для данных облачное хранилище на основе тегов.

+ +

На рисунке 2 показан пример того, как приложение для обработки фотографий может использовать SAF +для доступа к сохраненным данным:

+

app

+ +

Рисунок 2. Поток управления Storage Access Framework

+ +

Обратите внимание на следующее.

+
    + +
  • На платформе SAF поставщики и клиенты не взаимодействуют +напрямую. Клиент запрашивает разрешение на взаимодействие +с файлами (то есть, на чтение, редактирование, создание или удаление файлов).
  • + +
  • Взаимодействие начинается, когда приложение (в нашем примере обрабатывающее фотографии) активизирует намерение +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} или {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. Намерение может включать в себя фильтры +для уточнения критериев, например, «предоставить открываемые файлы +с MIME-типом image».
  • + +
  • Когда намерение срабатывает, системный элемент выбора переходит к каждому зарегистрированному поставщику +и показывает пользователю корневые каталоги с контентом, соответствующим запросу.
  • + +
  • Элемент выбора предоставляет пользователю стандартный интерфейс, даже +если поставщики документов значительно различаются. В качестве примера на рисунке 2 +изображены Диск Google, поставщик на USB-накопителе и облачный поставщик.
  • +
+ +

На рисунке 3 показан элемент выбора, в котором пользователь для поиска изображений выбрал учетную запись +Диск Google:

+ +

picker

+ +

Рисунок 3. Элемент выбора

+ +

Когда пользователь выбирает Диск Google, изображения отображаются, как показано на +рисунке 4. С этого момента пользователь может взаимодействовать с ними любыми способами, + которые поддерживаются поставщиком и клиентским приложением. + +

picker

+ +

Рисунок 4. Изображения

+ +

Создание клиентского приложения

+ +

В Android версии 4.3 и ниже для того, чтобы приложение могло получать файл от другого +приложения, оно должно активизировать намерение, например, {@link android.content.Intent#ACTION_PICK} +или {@link android.content.Intent#ACTION_GET_CONTENT}. После этого пользователь должен выбрать +какое-либо одно приложение, чтобы получить файл, а оно должно предоставить пользователю +интерфейс, с помощью которого он сможет выбирать и получать файлы.

+ +

Начиная с Android 4.4 и выше, у разработчика имеется дополнительная возможность — намерение +{@link android.content.Intent#ACTION_OPEN_DOCUMENT}, +которое отображает пользовательский интерфейс элемента выбора, управляемого системой. Этот элемент предоставляет пользователю +обзор всех файлов, доступных в других приложениях. Благодаря этому единому интерфейсу, +пользователь может выбрать файл в любом из поддерживаемых приложений.

+ +

Намерение {@link android.content.Intent#ACTION_OPEN_DOCUMENT} не +является заменой для намерения {@link android.content.Intent#ACTION_GET_CONTENT}. + Разработчику следует использовать то, которое лучше соответствует потребностям приложения:

+ +
    +
  • используйте {@link android.content.Intent#ACTION_GET_CONTENT}, если приложению нужно просто +прочитать или импортировать данные. При таком подходе приложение импортирует копию данных, + например, файл с изображением.
  • + +
  • используйте {@link android.content.Intent#ACTION_OPEN_DOCUMENT}, если +приложению нужна возможность долговременного, постоянного доступа к документам, принадлежащим поставщику +документов. В качестве примера можно назвать редактор фотографий, позволяющий пользователям обрабатывать +изображения, хранящиеся в поставщике документов.
  • + +
+ + +

В этом разделе показано, как написать клиентское приложение, использующее намерения +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} и +{@link android.content.Intent#ACTION_CREATE_DOCUMENT}.

+ + + + +

+В следующем фрагменте кода намерение {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +используется для поиска поставщиков документов, +содержащих файлы изображений:

+ +
private static final int READ_REQUEST_CODE = 42;
+...
+/**
+ * Fires an intent to spin up the "file chooser" UI and select an image.
+ */
+public void performFileSearch() {
+
+    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
+    // browser.
+    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as a
+    // file (as opposed to a list of contacts or timezones)
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Filter to show only images, using the image MIME data type.
+    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
+    // To search for all documents available via installed storage providers,
+    // it would be "*/*".
+    intent.setType("image/*");
+
+    startActivityForResult(intent, READ_REQUEST_CODE);
+}
+ +

Обратите внимание на следующее.

+
    +
  • Когда приложение активизирует намерение {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +, оно запускает элемент выбора, отображающий всех поставщиков документов, соответствующих заданным критериям.
  • + +
  • Добавление категории {@link android.content.Intent#CATEGORY_OPENABLE} в +фильтры намерения приводит к отображению только тех документов, которые можно открыть, например, файлов с изображениями.
  • + +
  • Оператор {@code intent.setType("image/*")} выполняет дальнейшую фильтрацию, чтобы +отображались только документы с MIME-типом image.
  • +
+ +

Обработка результатов

+ +

Когда пользователь выбирает документ в элементе выбора, +вызывается метод {@link android.app.Activity#onActivityResult onActivityResult()}. +Идентификатор URI, указывающий на выбранный документ, содержится в параметре{@code resultData}. + Чтобы извлечь URI, следует вызвать {@link android.content.Intent#getData getData()}. +Этот URI можно использовать для получения документа, нужного пользователю. Например: +

+ +
@Override
+public void onActivityResult(int requestCode, int resultCode,
+        Intent resultData) {
+
+    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
+    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
+    // response to some other intent, and the code below shouldn't run at all.
+
+    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+        // The document selected by the user won't be returned in the intent.
+        // Instead, a URI to that document will be contained in the return intent
+        // provided to this method as a parameter.
+        // Pull that URI using resultData.getData().
+        Uri uri = null;
+        if (resultData != null) {
+            uri = resultData.getData();
+            Log.i(TAG, "Uri: " + uri.toString());
+            showImage(uri);
+        }
+    }
+}
+
+ +

Изучение метаданных документа

+ +

Имея в своем распоряжении URI документа, разработчик получает доступ к его метаданным. В следующем +фрагменте кода метаданные документа, определяемого идентификатором URI, считываются и записываются в журнал:

+ +
public void dumpImageMetaData(Uri uri) {
+
+    // The query, since it only applies to a single document, will only return
+    // one row. There's no need to filter, sort, or select fields, since we want
+    // all fields for one document.
+    Cursor cursor = getActivity().getContentResolver()
+            .query(uri, null, null, null, null, null);
+
+    try {
+    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
+    // "if there's anything to look at, look at it" conditionals.
+        if (cursor != null && cursor.moveToFirst()) {
+
+            // Note it's called "Display Name".  This is
+            // provider-specific, and might not necessarily be the file name.
+            String displayName = cursor.getString(
+                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+            Log.i(TAG, "Display Name: " + displayName);
+
+            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
+            // If the size is unknown, the value stored is null.  But since an
+            // int can't be null in Java, the behavior is implementation-specific,
+            // which is just a fancy term for "unpredictable".  So as
+            // a rule, check if it's null before assigning to an int.  This will
+            // happen often:  The storage API allows for remote files, whose
+            // size might not be locally known.
+            String size = null;
+            if (!cursor.isNull(sizeIndex)) {
+                // Technically the column stores an int, but cursor.getString()
+                // will do the conversion automatically.
+                size = cursor.getString(sizeIndex);
+            } else {
+                size = "Unknown";
+            }
+            Log.i(TAG, "Size: " + size);
+        }
+    } finally {
+        cursor.close();
+    }
+}
+
+ +

Открытие документа

+ +

Получив URI документа, разработчик может открывать его и в целом +делать с ним всё, что угодно.

+ +

Объект растровых изображений

+ +

Приведем пример кода для открытия объекта {@link android.graphics.Bitmap}:

+ +
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
+    ParcelFileDescriptor parcelFileDescriptor =
+            getContentResolver().openFileDescriptor(uri, "r");
+    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
+    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
+    parcelFileDescriptor.close();
+    return image;
+}
+
+ +

Обратите внимание, что не следует производить эту операцию в потоке пользовательского интерфейса. Ее нужно выполнять +в фоне, с помощью {@link android.os.AsyncTask}. Когда файл с растровым изображением откроется, его +можно отобразить в виджете {@link android.widget.ImageView}. +

+ +

Получение объекта InputStream

+ +

Далее приведен пример того, как можно получить объект {@link java.io.InputStream} по идентификатору URI. В этом +фрагменте кода строчки файла считываются в объект строкового типа:

+ +
private String readTextFromUri(Uri uri) throws IOException {
+    InputStream inputStream = getContentResolver().openInputStream(uri);
+    BufferedReader reader = new BufferedReader(new InputStreamReader(
+            inputStream));
+    StringBuilder stringBuilder = new StringBuilder();
+    String line;
+    while ((line = reader.readLine()) != null) {
+        stringBuilder.append(line);
+    }
+    fileInputStream.close();
+    parcelFileDescriptor.close();
+    return stringBuilder.toString();
+}
+
+ +

Создание нового документа

+ +

Приложение может создать новый документ в поставщике документов, используя намерение +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} +. Чтобы создать файл, нужно указать в намерении MIME-тип и имя файла, а затем +запустить его с уникальным кодом запроса. Об остальном позаботится платформа:

+ + +
+// Here are some examples of how you might call this method.
+// The first parameter is the MIME type, and the second parameter is the name
+// of the file you are creating:
+//
+// createFile("text/plain", "foobar.txt");
+// createFile("image/png", "mypicture.png");
+
+// Unique request code.
+private static final int WRITE_REQUEST_CODE = 43;
+...
+private void createFile(String mimeType, String fileName) {
+    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as
+    // a file (as opposed to a list of contacts or timezones).
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Create a file with the requested MIME type.
+    intent.setType(mimeType);
+    intent.putExtra(Intent.EXTRA_TITLE, fileName);
+    startActivityForResult(intent, WRITE_REQUEST_CODE);
+}
+
+ +

После создания нового документа можно получить его URI с помощью +метода {@link android.app.Activity#onActivityResult onActivityResult()}, чтобы иметь возможность +записывать в него данные.

+ +

Удаление документа

+ +

Если у разработчика имеется URI документа, а объект +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} +этого документа содержит флаг +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE}, +то документ можно удалить. Например:

+ +
+DocumentsContract.deleteDocument(getContentResolver(), uri);
+
+ +

Редактирование документа

+ +

Платформа SAF позволяет редактировать текстовые документы на месте. +В следующем фрагменте кода активизируется +намерение {@link android.content.Intent#ACTION_OPEN_DOCUMENT}, а +категория {@link android.content.Intent#CATEGORY_OPENABLE} используется, чтобы отображались только +документы, которые можно открыть. Затем производится дальнейшая фильтрация, чтобы отображались только текстовые файлы:

+ +
+private static final int EDIT_REQUEST_CODE = 44;
+/**
+ * Open a file for writing and append some text to it.
+ */
+ private void editDocument() {
+    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
+    // file browser.
+    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as a
+    // file (as opposed to a list of contacts or timezones).
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Filter to show only text files.
+    intent.setType("text/plain");
+
+    startActivityForResult(intent, EDIT_REQUEST_CODE);
+}
+
+ +

Далее, из метода {@link android.app.Activity#onActivityResult onActivityResult()} +(см. Обработка результатов) можно вызвать код для выполнения редактирования. +В следующем фрагменте кода объект {@link java.io.FileOutputStream} +получен с помощью объекта класса {@link android.content.ContentResolver}. По умолчанию используется режим записи. +Рекомендуется запрашивать минимально необходимые права доступа, поэтому не следует запрашивать +чтение/запись, если приложению требуется только записать файл:

+ +
private void alterDocument(Uri uri) {
+    try {
+        ParcelFileDescriptor pfd = getActivity().getContentResolver().
+                openFileDescriptor(uri, "w");
+        FileOutputStream fileOutputStream =
+                new FileOutputStream(pfd.getFileDescriptor());
+        fileOutputStream.write(("Overwritten by MyCloud at " +
+                System.currentTimeMillis() + "\n").getBytes());
+        // Let the document provider know you're done by closing the stream.
+        fileOutputStream.close();
+        pfd.close();
+    } catch (FileNotFoundException e) {
+        e.printStackTrace();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}
+ +

Удержание прав доступа

+ +

Когда приложение открывает файл для чтения или записи, система предоставляет +ему URI-разрешение на этот файл. Разрешение действует вплоть до перезагрузки устройства. +Предположим, что в графическом редакторе требуется, чтобы у пользователя была возможность +открыть непосредственно в этом приложении последние пять изображений, которые он редактировал. Если он +перезапустил устройство, возникает необходимость снова отсылать его к системному элементу выбора для поиска +файлов. Очевидно, это далеко не идеальный вариант.

+ +

Чтобы избежать такой ситуации, разработчик может удержать права доступа, предоставленные системой +его приложению. Приложение фактически принимает постоянное URI-разрешение, +предлагаемое системой. В результате пользователь получает непрерывный доступ к файлам +из приложения, независимо от перезагрузки устройства:

+ + +
final int takeFlags = intent.getFlags()
+            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
+            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+// Check for the freshest data.
+getContentResolver().takePersistableUriPermission(uri, takeFlags);
+ +

Остается один заключительный шаг. Можно сохранить последние +URI-идентификаторы, с которыми работало приложение. Однако не исключено, что они потеряют актуальность, поскольку другое приложение +может удалить или модифицировать документ. Поэтому следует всегда вызывать +{@code getContentResolver().takePersistableUriPermission()}, чтобы получать +актуальные данные.

+ +

Создание собственного поставщика документов

+ +

+При разработке приложения, оказывающего услуги по хранению файлов (например, +службы хранения в облаке), можно предоставить доступ к файлам при помощи +SAF, написав собственный поставщик документов. В этом разделе показано, +как это сделать.

+ + +

Манифест

+ +

Чтобы реализовать собственный поставщик документов, необходимо добавить в манифест приложения +следующую информацию:

+
    + +
  • Целевой API-интерфейс уровня 19 или выше.
  • + +
  • Элемент <provider>, в котором объявляется нестандартный поставщик + хранилища.
  • + +
  • Имя поставщика, т. е., имя его класса с именем пакета. +Например: com.example.android.storageprovider.MyCloudProvider.
  • + +
  • Имя центра поставщика, т. е. имя пакета (в этом примере — +com.example.android.storageprovider) с типом поставщика контента +(documents). Например,{@code com.example.android.storageprovider.documents}.
  • + +
  • Атрибут android:exported, установленный в значение "true". + Необходимо экспортировать поставщик, чтобы он был виден другим приложениям.
  • + +
  • Атрибут android:grantUriPermissions, установленный в значение +"true". Этот параметр позволяет системе предоставлять другим приложениям доступ +к контенту поставщика. Обсуждение того, как следует удерживать права доступа +к конкретному документу см. в разделе Удержание прав доступа.
  • + +
  • Разрешение {@code MANAGE_DOCUMENTS}. По умолчанию поставщик доступен +всем. Добавление этого разрешения в манифест делает поставщик доступным только системе. +Это важно для обеспечения безопасности.
  • + +
  • Атрибут {@code android:enabled}, имеющий логическое значение, определенное в файле +ресурсов. Этот атрибут предназначен для отключения поставщика на устройствах под управлением Android версии 4.3 и ниже. + Например: {@code android:enabled="@bool/atLeastKitKat"}. Помимо +включения этого атрибута в манифест, необходимо сделать следующее: +
      +
    • В файл ресурсов {@code bool.xml}, расположенный в каталоге {@code res/values/}, добавить +строчку
      <bool name="atLeastKitKat">false</bool>
    • + +
    • В файл ресурсов {@code bool.xml}, расположенный в каталоге {@code res/values-v19/}, добавить +строчку
      <bool name="atLeastKitKat">true</bool>
    • +
  • + +
  • Фильтр намерения с действием +{@code android.content.action.DOCUMENTS_PROVIDER}, чтобы поставщик +появлялся в элементе выбора, когда система будет искать поставщиков.
  • + +
+

Ниже приведены отрывки из образца манифеста, включающего в себя поставщик:

+ +
<manifest... >
+    ...
+    <uses-sdk
+        android:minSdkVersion="19"
+        android:targetSdkVersion="19" />
+        ....
+        <provider
+            android:name="com.example.android.storageprovider.MyCloudProvider"
+            android:authorities="com.example.android.storageprovider.documents"
+            android:grantUriPermissions="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DOCUMENTS"
+            android:enabled="@bool/atLeastKitKat">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+        </provider>
+    </application>
+
+</manifest>
+ +

Поддержка устройств под управлением Android версии 4.3 и ниже

+ +

Намерение +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} доступно только +на устройствах с Android версии 4.4 и выше. +Если приложение должно поддерживать {@link android.content.Intent#ACTION_GET_CONTENT}, +чтобы обслуживать устройства, работающие под управлением Android 4.3 и ниже, необходимо +отключить фильтр намерения {@link android.content.Intent#ACTION_GET_CONTENT} в +манифесте для устройств с Android версии 4.4 и выше. Поставщик +документов и намерение {@link android.content.Intent#ACTION_GET_CONTENT} следует считать +взаимоисключающими. Если приложение поддерживает их одновременно, оно +будет появляться в пользовательском интерфейсе системного элемента выбора дважды, предлагая два различных способа доступа +к сохраненным данным. Это запутает пользователей.

+ +

Отключать фильтр намерения +{@link android.content.Intent#ACTION_GET_CONTENT} на устройствах +с Android версии 4.4 и выше рекомендуется следующим образом:

+ +
    +
  1. В файл ресурсов {@code bool.xml}, расположенный в каталоге {@code res/values/}, добавить +следующую строку:
    <bool name="atMostJellyBeanMR2">true</bool>
  2. + +
  3. В файл ресурсов {@code bool.xml}, расположенный в каталоге {@code res/values-v19/}, добавить +следующую строку:
    <bool name="atMostJellyBeanMR2">false</bool>
  4. + +
  5. Добавить +псевдоним +операции, чтобы отключить фильтр намерения {@link android.content.Intent#ACTION_GET_CONTENT} +для версий 4.4 (API уровня 19) и выше. Например: + +
    +<!-- This activity alias is added so that GET_CONTENT intent-filter
    +     can be disabled for builds on API level 19 and higher. -->
    +<activity-alias android:name="com.android.example.app.MyPicker"
    +        android:targetActivity="com.android.example.app.MyActivity"
    +        ...
    +        android:enabled="@bool/atMostJellyBeanMR2">
    +    <intent-filter>
    +        <action android:name="android.intent.action.GET_CONTENT" />
    +        <category android:name="android.intent.category.OPENABLE" />
    +        <category android:name="android.intent.category.DEFAULT" />
    +        <data android:mimeType="image/*" />
    +        <data android:mimeType="video/*" />
    +    </intent-filter>
    +</activity-alias>
    +
    +
  6. +
+

Контракты

+ +

Как правило, при создании нестандартного поставщика контента одной из задач +является реализация классов-контрактов, описанная в руководстве для разработчиков + +Поставщики контента. Класс-контракт представляет собой класс {@code public final}, +в котором содержатся определения констант для URI, имен столбцов, типов MIME и +других метаданных поставщика. Платформа SAF +предоставляет разработчику следующие классы-контракты, так что ему не нужно писать +собственные:

+ +
    +
  • {@link android.provider.DocumentsContract.Document}
  • +
  • {@link android.provider.DocumentsContract.Root}
  • +
+ +

Например, когда +к поставщику документов приходит запрос на документы или корневой каталог, можно возвращать в курсоре следующие столбцы:

+ +
private static final String[] DEFAULT_ROOT_PROJECTION =
+        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
+        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
+        Root.COLUMN_AVAILABLE_BYTES,};
+private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
+        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
+        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
+        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
+
+ +

Создание подкласса класса DocumentsProvider

+ +

Следующим шагом в разработке собственного поставщика документов является создание подкласса +абстрактного класса {@link android.provider.DocumentsProvider}. Как минимум, необходимо +реализовать следующие методы:

+ +
    +
  • {@link android.provider.DocumentsProvider#queryRoots queryRoots()}
  • + +
  • {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
  • + +
  • {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
  • + +
  • {@link android.provider.DocumentsProvider#openDocument openDocument()}
  • +
+ +

Это единственные методы, реализация которых строго обязательна, однако существует +намного больше методов, которые, возможно, тоже придется реализовать. Подробности приводятся в описании класса{@link android.provider.DocumentsProvider} +.

+ +

Реализация метода queryRoots

+ +

Реализация метода {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} должна возвращать объект {@link android.database.Cursor}, указывающий на все +корневые каталоги поставщиков документов, используя столбцы, определенные в +{@link android.provider.DocumentsContract.Root}.

+ +

В следующем фрагменте кода параметр {@code projection} представляет +конкретные поля, нужные вызывающему объекту. В этом коде создается курсор, +и к нему добавляется одна строка, соответствующая одному корневому каталогу (каталогу верхнего уровня), например, +Загрузки или Изображения. Большинство поставщиков имеет только один корневой каталог. Однако ничто не мешает иметь несколько корневых каталогов, +например, при наличии нескольких учетных записей. В этом случае достаточно добавить в +курсор еще одну строку.

+ +
+@Override
+public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+
+    // Create a cursor with either the requested fields, or the default
+    // projection if "projection" is null.
+    final MatrixCursor result =
+            new MatrixCursor(resolveRootProjection(projection));
+
+    // If user is not logged in, return an empty root cursor.  This removes our
+    // provider from the list entirely.
+    if (!isUserLoggedIn()) {
+        return result;
+    }
+
+    // It's possible to have multiple roots (e.g. for multiple accounts in the
+    // same app) -- just add multiple cursor rows.
+    // Construct one row for a root called "MyCloud".
+    final MatrixCursor.RowBuilder row = result.newRow();
+    row.add(Root.COLUMN_ROOT_ID, ROOT);
+    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
+
+    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
+    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
+    // recently used documents will show up in the "Recents" category.
+    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
+    // shares.
+    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
+            Root.FLAG_SUPPORTS_RECENTS |
+            Root.FLAG_SUPPORTS_SEARCH);
+
+    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
+    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
+
+    // This document id cannot change once it's shared.
+    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
+
+    // The child MIME types are used to filter the roots and only present to the
+    //  user roots that contain the desired type somewhere in their file hierarchy.
+    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
+    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
+    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
+
+    return result;
+}
+ +

Реализация метода queryChildDocuments

+ +

Реализация метода +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} +должна возвращать объект{@link android.database.Cursor}, указывающий на все файлы в +заданном каталоге, используя столбцы, определенные в +{@link android.provider.DocumentsContract.Document}.

+ +

Этот метод вызывается, когда в интерфейсе элемента выбора пользователь выбирает корневой каталог приложения. +Метод получает документы-потомки каталога на уровне ниже корневого. Его можно вызывать на любом уровне +файловой иерархии, а не только в корневом каталоге. В следующем фрагменте кода +создается курсор с запрошенными столбцами. Затем в него заносится информация о +каждом ближайшем потомке родительского каталога. +Потомком может быть изображение, еще один каталог, в общем, любой файл:

+ +
@Override
+public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+                              String sortOrder) throws FileNotFoundException {
+
+    final MatrixCursor result = new
+            MatrixCursor(resolveDocumentProjection(projection));
+    final File parent = getFileForDocId(parentDocumentId);
+    for (File file : parent.listFiles()) {
+        // Adds the file's display name, MIME type, size, and so on.
+        includeFile(result, null, file);
+    }
+    return result;
+}
+
+ +

Реализация метода queryDocument

+ +

Реализация метода +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} +должна возвращать объект{@link android.database.Cursor}, указывающий на заданный файл, +используя столбцы, определенные в{@link android.provider.DocumentsContract.Document}. +

+ +

Метод {@link android.provider.DocumentsProvider#queryDocument queryDocument()} +возвращает ту же информацию, которую возвращал +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}, +но для конкретного файла:

+ + +
@Override
+public Cursor queryDocument(String documentId, String[] projection) throws
+        FileNotFoundException {
+
+    // Create a cursor with the requested projection, or the default projection.
+    final MatrixCursor result = new
+            MatrixCursor(resolveDocumentProjection(projection));
+    includeFile(result, documentId, null);
+    return result;
+}
+
+ +

Реализация метода openDocument

+ +

Необходимо реализовать метод {@link android.provider.DocumentsProvider#openDocument +openDocument()}, который возвращает объект {@link android.os.ParcelFileDescriptor}, представляющий +указанный файл. Другие приложения смогут воспользоваться возращенным объектом {@link android.os.ParcelFileDescriptor} +для организации потока данных. Система вызывает этот метод, когда пользователь выбирает файл, +и клиентское приложение запрашивает доступ нему, вызывая +метод {@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}. +Например:

+ +
@Override
+public ParcelFileDescriptor openDocument(final String documentId,
+                                         final String mode,
+                                         CancellationSignal signal) throws
+        FileNotFoundException {
+    Log.v(TAG, "openDocument, mode: " + mode);
+    // It's OK to do network operations in this method to download the document,
+    // as long as you periodically check the CancellationSignal. If you have an
+    // extremely large file to transfer from the network, a better solution may
+    // be pipes or sockets (see ParcelFileDescriptor for helper methods).
+
+    final File file = getFileForDocId(documentId);
+
+    final boolean isWrite = (mode.indexOf('w') != -1);
+    if(isWrite) {
+        // Attach a close listener if the document is opened in write mode.
+        try {
+            Handler handler = new Handler(getContext().getMainLooper());
+            return ParcelFileDescriptor.open(file, accessMode, handler,
+                        new ParcelFileDescriptor.OnCloseListener() {
+                @Override
+                public void onClose(IOException e) {
+
+                    // Update the file with the cloud server. The client is done
+                    // writing.
+                    Log.i(TAG, "A file with id " +
+                    documentId + " has been closed!
+                    Time to " +
+                    "update the server.");
+                }
+
+            });
+        } catch (IOException e) {
+            throw new FileNotFoundException("Failed to open document with id "
+            + documentId + " and mode " + mode);
+        }
+    } else {
+        return ParcelFileDescriptor.open(file, accessMode);
+    }
+}
+
+ +

Безопасность

+ +

Предположим, что поставщик документов представляет собой защищенную паролем службу хранения в облаке, +а приложение должно убедиться, что пользователь вошел в систему, прежде чем оно предоставит ему доступ к файлам. +Что должно предпринять приложение, если пользователь не выполнил вход? Решение состоит в том, чтобы +реализация метода {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} не возвращала корневых каталогов. Иными словами, это должен быть пустой корневой курсор:

+ +
+public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+...
+    // If user is not logged in, return an empty root cursor.  This removes our
+    // provider from the list entirely.
+    if (!isUserLoggedIn()) {
+        return result;
+}
+
+ +

Следующий шаг состоит в вызове метода {@code getContentResolver().notifyChange()}. +Помните объект {@link android.provider.DocumentsContract}? Воспользуемся им для создания +соответствующего URI. В следующем фрагменте кода система извещается о необходимости опрашивать корневые каталоги +поставщика документов, когда меняется статус входа пользователя в систему. Если пользователь не +выполнил вход, метод {@link android.provider.DocumentsProvider#queryRoots queryRoots()} возвратит +пустой курсор, как показано выше. Это гарантирует, что документы поставщика будут +доступны только пользователям, вошедшим в поставщик.

+ +
private void onLoginButtonClick() {
+    loginOrLogout();
+    getContentResolver().notifyChange(DocumentsContract
+            .buildRootsUri(AUTHORITY), null);
+}
+
\ No newline at end of file diff --git a/docs/html-intl/intl/ru/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/ru/guide/topics/resources/accessing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..3d59ceb292a2464a2bc16bd88d3426ae00707a9e --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=Доступ к ресурсам +parent.title=Ресурсы приложения +parent.link=index.html +@jd:body + +
+
+

Краткое описание

+
    +
  • В коде для создания ссылок на ресурсы можно использовать целочисленные переменные из {@code R.java}, такие как +{@code R.drawable.myimage}
  • +
  • Для создания ссылок на ресурсы из самих ресурсов можно использовать особый синтаксис XML, например {@code +@drawable/myimage}
  • +
  • Также для доступа к ресурсам приложения используются методы класса +{@link android.content.res.Resources}
  • +
+ +

Ключевые классы

+
    +
  1. {@link android.content.res.Resources}
  2. +
+ +

Содержание документа

+
    +
  1. Доступ к ресурсам из кода
  2. +
  3. Доступ к ресурсам из XML +
      +
    1. Ссылка на атрибуты стиля
    2. +
    +
  4. +
  5. Доступ к ресурсам платформы
  6. +
+ +

См. также:

+
    +
  1. Предоставление ресурсов
  2. +
  3. Типы ресурсов
  4. +
+
+
+ + + + +

После того, как вы предоставили ресурс в вашем приложении (см. раздел Предоставление ресурсов), этот ресурс можно применить. Для этого необходимо создать +ссылку на идентификатор ресурса. Для задания всех таких идентификаторов в вашем проекте используется класс {@code R}, который автоматически создается инструментом +{@code aapt}.

+ +

Во время компиляции приложения инструмент {@code aapt} создает класс {@code R}, в котором находятся +идентификаторы для всех ресурсов в каталоге {@code +res/}. Для каждого типа ресурсов предусмотрен подкласс {@code R} (например, +класс {@code R.drawable} для элементов дизайна), а для каждого ресурса указанного типа существует статическая +целочисленная переменная (например, {@code R.drawable.icon}). Эта переменная как раз и служит идентификатором ресурса, которую можно +использовать для его получения.

+ +

Несмотря на то, что в классе {@code R} находятся идентификаторы ресурсов, никогда не обращайтесь к нему +для поиска идентификатора ресурса. Последний состоит из следующих компонентов:

+
    +
  • Тип ресурса: ресурсы объединены по типам, таким как {@code +string}, {@code drawable} и {@code layout}. Дополнительные сведения о различных типах представлены в разделе Типы ресурсов. +
  • +
  • Имя ресурса, в качестве которого выступает либо имя файла +(без расширения), либо значение атрибута XML {@code android:name} (если +ресурс представляет собой простое значение, например строку).
  • +
+ +

Существует два способа доступа к ресурсу.

+
    +
  • Из кода: с помощью статической целочисленной переменной из подкласса вашего класса {@code R} +, например: +
    R.string.hello
    +

    {@code string} — это тип ресурса, а {@code hello} — это его имя. Существует множество API-интерфейсов +Android, которые могут получать доступ к ресурсам, идентификаторы которых указаны в этом формате. См. раздел +Доступ к ресурсам из кода.

    +
  • +
  • Из XML: с помощью особого синтаксиса XML, который также соответствует +идентификатору ресурса, заданному в классе {@code R}, например: +
    @string/hello
    +

    {@code string} — это тип ресурса, а {@code hello} — это его имя. Этот синтаксис можно +использовать в любом фрагменте ресурса XML, где ожидается значение, указанное вами в ресурсе. См. раздел Доступ к ресурсам из XML

    +
  • +
+ + + +

Доступ к ресурсам из кода

+ +

Чтобы использовать ресурс в коде, можно передать идентификатор ресурса в виде параметра метода. Например, с +помощью метода {@link android.widget.ImageView#setImageResource(int) setImageResource()} можно указать использование виджетом {@link android.widget.ImageView} ресурса {@code res/drawable/myimage.png}: +

+
+ImageView imageView = (ImageView) findViewById(R.id.myimageview);
+imageView.setImageResource(R.drawable.myimage);
+
+ +

Отдельные ресурсы также можно получать с помощью методов в классе {@link +android.content.res.Resources}, экземпляр которого можно получить с помощью +метода {@link android.content.Context#getResources()}.

+ + + + +

Синтаксис

+ +

Ниже представлен синтаксис ссылки на ресурс из кода.

+ +
+[<package_name>.]R.<resource_type>.<resource_name>
+
+ +
    +
  • {@code <package_name>} — это имя пакета, в котором находится ресурс (не +требуется при создании ссылок на ресурсы из вашего собственного пакета).
  • +
  • {@code <resource_type>} — это подкласс {@code R} для типа ресурса.
  • +
  • {@code <resource_name>} — это либо имя файла +ресурса (без расширения), либо значение атрибута {@code android:name} в элементе XML (для простых +значений).
  • +
+

Дополнительные сведения о каждом типе +ресурсов и порядке создания ссылок на них см. в разделе Типы ресурсов.

+ + +

Примеры использования

+ +

Существует множество методов, которые могут принимать идентификатор ресурса в виде параметра. Для получения ресурсов можно использовать методы, +представленные в классе {@link android.content.res.Resources}. Можно получить экземпляр {@link +android.content.res.Resources} с помощью {@link android.content.Context#getResources +Context.getResources()}.

+ + +

Вот некоторые примеры доступа к ресурсам из кода:

+ +
+// Load a background for the current screen from a drawable resource
+{@link android.app.Activity#getWindow()}.{@link
+android.view.Window#setBackgroundDrawableResource(int)
+setBackgroundDrawableResource}(R.drawable.my_background_image) ;
+
+// Set the Activity title by getting a string from the Resources object, because
+//  this method requires a CharSequence rather than a resource ID
+{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence)
+setTitle}(getResources().{@link android.content.res.Resources#getText(int)
+getText}(R.string.main_title));
+
+// Load a custom layout for the current screen
+{@link android.app.Activity#setContentView(int)
+setContentView}(R.layout.main_screen);
+
+// Set a slide in animation by getting an Animation from the Resources object
+mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation)
+setInAnimation}(AnimationUtils.loadAnimation(this,
+        R.anim.hyperspace_in));
+
+// Set the text on a TextView object using a resource ID
+TextView msgTextView = (TextView) findViewById(R.id.msg);
+msgTextView.{@link android.widget.TextView#setText(int)
+setText}(R.string.hello_message);
+
+ + +

Внимание! Никогда не изменяйте файл {@code +R.java} вручную — этот файл создается инструментом {@code aapt} во время компиляции вашего +проекта. Любые внесенные в него изменения перезаписываются после следующей компиляции.

+ + + +

Доступ к ресурсам из XML

+ +

Для задания значений для некоторых атрибутов и элементов XML можно использовать +ссылку на существующий ресурс. Это зачастую требуется при создании файлов макета при +указании строк и изображений для виджетов.

+ +

Например, при добавлении в макет элемента {@link android.widget.Button} необходимо использовать +строковый ресурс для надписи на кнопке:

+ +
+<Button
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="@string/submit" />
+
+ + +

Синтаксис

+ +

Ниже представлен синтаксис ссылки на ресурс из ресурса XML.

+ +
+@[<package_name>:]<resource_type>/<resource_name>
+
+ +
    +
  • {@code <package_name>} — это имя пакета, в котором находится ресурс (не +требуется при создании ссылок на ресурсы из одного и того же пакета).
  • +
  • {@code <resource_type>} — это подкласс +{@code R} для типа ресурса.
  • +
  • {@code <resource_name>} — это либо имя файла +ресурса (без расширения), либо значение атрибута {@code android:name} в элементе XML (для простых +значений).
  • +
+ +

Дополнительные сведения о каждом типе +ресурсов и порядке создания ссылок на них см. в разделе Типы ресурсов.

+ + +

Примеры использования

+ +

В некоторых случаях ресурс необходимо использовать в качестве значения в элементе XML (например, чтобы применить графическое изображение к +виджету), однако вы также можете использовать ресурс в любом фрагменте XML, где ожидается простое значение. Например, +если имеется следующий файл ресурса, включающий цветовой ресурс и строковый ресурс:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+   <color name="opaque_red">#f00</color>
+   <string name="hello">Hello!</string>
+</resources>
+
+ +

Эти ресурсы можно использовать в следующем файле макета для задания цвета текста и +надписи:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:textColor="@color/opaque_red"
+    android:text="@string/hello" />
+
+ +

В этом случае в ссылке на ресурс не нужно указывать имя пакета, поскольку +ресурсы находятся в вашем собственном пакете. Однако для создания +ссылки на системный ресурс вам потребуется указать имя пакета. Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:textColor="@android:color/secondary_text_dark"
+    android:text="@string/hello" />
+
+ +

Примечание. Всегда используйте строковые ресурсы, +поскольку ваше приложение может потребоваться перевести на другие языки. +Сведения о создании альтернативных +ресурсов (например, локализованных строк) представлены в разделе Предоставление альтернативных +ресурсов. В разделе Локализация приведено полное руководство по локализации +приложения.

+ +

Вы даже можете использовать ресурсы в XML для создания псевдонимов. Например, можно создать +элемент дизайна, который будет служить псевдонимом для другого элемента дизайна:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/other_drawable" />
+
+ +

Это может показаться излишним, однако такой подход очень полезен при использовании альтернативных ресурсов. Узнайте подробнее о +создании псевдонимов ресурсов.

+ + + +

Ссылка на атрибуты стиля

+ +

С помощью ресурса атрибута стиля можно создать ссылку на значение +атрибута в текущей примененной теме. Таким образом можно +настраивать внешний вид элементов пользовательского интерфейса, подстраивая их под стандартные варианты элементов оформления +текущей темы, вместо указания жестко заданных значений. Создание ссылки на атрибут стиля +представляет собой своеобразную инструкцию «использовать стиль, заданный этим атрибутом в текущей теме».

+ +

Синтаксис для создания ссылки на атрибут стиля практически идентичен обычному формату +ресурса, только в этом случае вместо символа{@code @} необходимо указать вопросительный знак ({@code ?}), а тип ресурса +вообще необязательно указывать. Например:

+ +
+?[<package_name>:][<resource_type>/]<resource_name>
+
+ +

Ниже представлен пример создания ссылки на атрибут для задания цвета текста в соответствии с +«основным» цветом текста системной темы оформления:

+ +
+<EditText id="text"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:textColor="?android:textColorSecondary"
+    android:text="@string/hello_world" />
+
+ +

Здесь атрибут {@code android:textColor} служит для задания имени атрибута стиля +в текущей теме. Теперь в Android используется значение, примененное к атрибуту стиля{@code android:textColorSecondary} +в качестве значения для {@code android:textColor} в этом виджете. Поскольку инструменту для работы с системными +ресурсами известно, что в этом контексте ожидается ресурс атрибута, +вам не нужно явно указывать его тип (который должен быть +?android:attr/textColorSecondary) — тип {@code attr} можно исключить.

+ + + + +

Доступ к ресурсам платформы

+ +

В Android предусмотрен ряд стандартных ресурсов, например, стилей, тем и макетов. Для +доступа к этим ресурсам укажите в ссылке на ресурс имя пакета +android. Например, в Android имеется ресурс макета, который можно использовать для +элементов списка в виджете {@link android.widget.ListAdapter}:

+ +
+{@link android.app.ListActivity#setListAdapter(ListAdapter)
+setListAdapter}(new {@link
+android.widget.ArrayAdapter}<String>(this, android.R.layout.simple_list_item_1, myarray));
+
+ +

В этом примере {@link android.R.layout#simple_list_item_1} представляет собой ресурс макета, определенный +платформой для элементов в виджете {@link android.widget.ListView}. Вы можете использовать его вместо создания +собственных макетов для элементов списка. Дополнительные сведения представлены в руководстве для разработчиков, посвященном +представлению в виде списка.

+ diff --git a/docs/html-intl/intl/ru/guide/topics/resources/overview.jd b/docs/html-intl/intl/ru/guide/topics/resources/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..c809c85b784b92d527549cb514509a6173af6d9d --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=Обзор ресурсов +@jd:body + + + + +

Необходимо обязательно экспортировать ресурсы, такие как изображения и строки, из кода +приложения, чтобы можно было обрабатывать их независимо. Кроме того, экспорт +ресурсов позволяет предоставлять альтернативные ресурсы для поддержки конфигураций +конкретных устройств, например, различные языки или размеры экранов. Значение этого возрастает по мере того, как +появляется все больше устройств Android с разными конфигурациями. Чтобы обеспечить +совместимость с различными конфигурациями, необходимо организовать ресурсы +в каталоге {@code res/} проекта с использованием различных подкаталогов для группирования ресурсов по типу и +конфигурации.

+ +
+ +

+Рисунок 1. Два разных устройства, каждое из которых использует макет по умолчанию +(приложение не предоставляет альтернативных макетов).

+
+ +
+ +

+Рисунок 2. Два разных устройства, каждое из которых использует свой макет, разработанный для +экранов разных размеров.

+
+ +

Для ресурсов любого типа можно указать ресурс по умолчанию и несколько +альтернативных ресурсов для приложения:

+
    +
  • Ресурсы по умолчанию должны использоваться независимо от +конфигурации устройства или в том случае, когда отсутствуют альтернативные ресурсы, соответствующие +текущей конфигурации.
  • +
  • Альтернативные ресурсы предназначены для работы с определенными +конфигурациями. Чтобы указать, что группа ресурсов предназначена для определенной конфигурации, +добавьте соответствующий квалификатор к имени каталога.
  • +
+ +

Например, несмотря на то, что макет пользовательского интерфейса по умолчанию +сохранен в каталоге {@code res/layout/}, можно указать другой макет для +использования на экране с альбомной ориентацией, сохранив его в каталоге {@code res/layout-land/} +. Android автоматически применяет соответствующие ресурсы, сопоставляя текущую конфигурацию +устройства с именами каталогов ресурсов.

+ +

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

+ +

В следующих документах содержится полное руководство по организации ресурсов приложения, +указания альтернативных ресурсов, доступа к ним из приложения и т. д.:

+ +
+
Предоставление ресурсов
+
Типы ресурсов, которые можно предоставлять в приложении, место их сохранения и способы создания +альтернативных ресурсов для определенных конфигураций устройств.
+
Доступ к ресурсам
+
Способ использования предоставленных ресурсов: путем ссылки на них из кода приложения +или из других ресурсов XML.
+
Обработка изменений в режиме выполнения
+
Управление изменениями конфигурации во время выполнения операции.
+
Локализация
+
Руководство по локализации приложения «снизу вверх» с помощью альтернативных ресурсов. Хотя это лишь +один из примеров использования альтернативных ресурсов, он очень важен для охвата более широкой аудитории +пользователей.
+
Типы ресурсов
+
Ссылка на различные типы ресурсов, которые вы можете предоставлять, с описанием элементов XML, +атрибутов и синтаксиса. Например, эта ссылка показывает, как создать ресурс для меню +, рисунков, анимаций приложения и т. д.
+
+ + diff --git a/docs/html-intl/intl/ru/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/ru/guide/topics/resources/providing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..be0af952896e45dca6dc2a26af70e8ff279a77fe --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=Предоставление ресурсов +parent.title=Ресурсы приложения +parent.link=index.html +@jd:body + +
+
+

Краткое описание

+
    +
  • Разные типы ресурсов находятся в разных подкаталогах каталога {@code res/}
  • +
  • Альтернативные ресурсы представляют собой файлы ресурсов для определенных конфигураций
  • +
  • Обязательно включайте ресурсы по умолчанию, чтобы приложение не зависело от конфигураций конкретного +устройства
  • +
+

Содержание документа

+
    +
  1. Группирование типов ресурсов
  2. +
  3. Предоставление альтернативных ресурсов +
      +
    1. Правила квалификатора имени
    2. +
    3. Создание псевдонимов ресурсов
    4. +
    +
  4. +
  5. Обеспечение оптимальной совместимости устройства с ресурсами
  6. +
  7. Как Android находит наиболее подходящий ресурс
  8. +
+ +

См. также:

+
    +
  1. Доступ к ресурсам
  2. +
  3. Типы ресурсов
  4. +
  5. Поддержка +нескольких экранов
  6. +
+
+
+ +

Обязательно необходимо экспортировать из кода ресурсы приложения, такие как изображения и строки, +для последующей их независимой обработки. Следует также обеспечить альтернативные ресурсы для +определенных конфигураций устройств, группируя их в каталогах ресурсов со специальными именами. В +режиме выполнения Android использует соответствующие ресурсы с учетом текущей конфигурации. Например, +можно предоставлять другой макет пользовательского интерфейса в зависимости от размера экрана или различные +строки в зависимости от настройки языка.

+ +

После выполнения экспорта ресурсов приложения можно обращаться к ним +с помощью идентификаторов ресурсов, которые генерируются в классе {@code R} вашего проекта. Использование +ресурсов в приложении рассмотрено в разделе Доступ +к ресурсам. В этом документе показано, как группировать ресурсы в проекте Android и +предоставлять альтернативные ресурсы для определенных конфигураций устройств.

+ + +

Группирование типов ресурсов

+ +

Следует поместить ресурсы каждого типа в определенный подкаталог каталога +{@code res/} вашего проекта. В качестве примера приведена иерархия файлов для простого проекта:

+ +
+MyProject/
+    src/  
+        MyActivity.java  
+    res/
+        drawable/  
+            graphic.png  
+        layout/  
+            main.xml
+            info.xml
+        mipmap/  
+            icon.png 
+        values/  
+            strings.xml  
+
+ +

Как видно в этом примере, каталог {@code res/} содержит все ресурсы (в +подкаталогах): ресурс-изображение, два ресурса-макета, каталоги {@code mipmap/} для значков +запуска и файл строк. Имена каталогов +ресурсов очень важны и описаны в таблице 1.

+ +

Примечание. Подробные сведения об использовании папок множественного отображения см. в разделе +Обзор управления проектами.

+ +

Таблица 1. Каталоги ресурсов, +поддерживаемые в каталоге {@code res/} проекта.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КаталогТип ресурсов
animator/Файлы XML, которые определяют анимации +свойств.
anim/Файлы XML, которые определяют анимации +преобразований. (Анимации свойств также можно сохранять в этом каталоге, но +для анимаций свойств предпочтительнее использовать каталог {@code animator/}, чтобы различать эти два +типа).
color/Файлы XML, которые определяют список состояний цветов. См. раздел Ресурс списка +состояний цветов
drawable/

Файлы растровых изображений ({@code .png}, {@code .9.png}, {@code .jpg}, {@code .gif}) или файлы XML, которые +составляют следующие подтипы графических ресурсов:

+
    +
  • Файлы растровых изображений
  • +
  • Файлы из девяти фрагментов (растровые изображения с возможностью изменения размера)
  • +
  • Списки состояний
  • +
  • Формы
  • +
  • Графические анимации
  • +
  • Другие графические элементы
  • +
+

См. раздел Графические ресурсы.

+
mipmap/Графические файлы для значков запуска с различной плотностью. Подробные сведения об управлении +значками запуска с помощью папок {@code mipmap/} см. в разделе + Обзор управления проектами.
layout/Файлы XML, которые определяют макет пользовательского интерфейса. + См. раздел Ресурсы макетов.
menu/Файлы XML, которые определяют меню приложения, такие как меню параметров, контекстные меню или вложенные +меню. См. раздел Ресурсы меню.
raw/

Произвольные файлы для сохранения в исходной форме. Чтобы открыть эти ресурсы с помощью +{@link java.io.InputStream}, вызовите {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()} с идентификатором ресурса, который имеет вид {@code R.raw.filename}.

+

Однако, если требуется получить доступ к исходным именам файлов и иерархии файлов, можно +сохранять некоторые ресурсы в каталоге {@code +assets/} (вместо каталога {@code res/raw/}). Файлы в каталоге {@code assets/} не получают +идентификатора ресурса, поэтому их чтение возможно только с помощью {@link android.content.res.AssetManager}.

values/

Файлы XML, которые содержат простые значения, такие как строки, целые числа и цвета.

+

Тогда как XML-файлы ресурсов в других подкаталогах каталога {@code res/} определяют отдельные ресурсы +на базе имени файла XML, файлы в каталоге {@code values/} описывают несколько ресурсов. +Для файла в этом каталоге каждый дочерний элемент элемента {@code <resources>} определяет один +ресурс. Например, элемент {@code <string>} создает ресурс +{@code R.string}, а элемент {@code <color>} создает ресурс {@code R.color} +.

+

Так как каждый ресурс определяется с помощью своего собственного элемента XML, можно назначать имя файла +по своему усмотрению и помещать ресурсы разных типов в один файл. Тем не мене, может +появиться необходимость поместить ресурсы отдельных типов в разные файлы. Например, ниже приведены соглашения для имен файлов +ресурсов, которые можно создать в этом каталоге:

+ +

См. разделы Строковые ресурсы, + Ресурсы стиля и + Дополнительные типы ресурсов.

+
xml/Произвольные XML-файлы, которые можно читать в режиме выполнения вызовом метода {@link +android.content.res.Resources#getXml(int) Resources.getXML()}. Здесь должны сохраняться различные файлы конфигурации XML, +например, конфигурация с возможностью поиска. +
+ +

Предупреждение! Не сохраняйте файлы ресурсов непосредственно в +каталоге {@code res/}, так как это вызывает ошибку компилятора.

+ +

Дополнительную информацию об определенных типах ресурсов см. в документации Типы ресурсов.

+ +

Ресурсы, сохраненные в подкаталогах, которые описаны в таблице 1, являются ресурсами +«по умолчанию». Таким образом, эти ресурсы определяют дизайн и содержимое приложения по умолчанию. +Однако различные типы устройств Android могут вызывать различные типы ресурсов. +Например, если устройство оснащено экраном больше нормального, следует предоставить +другие ресурсы макета, которые будут использовать преимущества дополнительного места на экране. Или, если устройство +содержит различные языковые настройки, следует предоставить другие строковые ресурсы, содержащие перевод +текста пользовательского интерфейса. Чтобы предоставить разные ресурсы для разных конфигураций устройств, +необходимо предоставить альтернативные ресурсы в дополнение к ресурсам +по умолчанию.

+ + +

Предоставление альтернативных ресурсов

+ + +
+ +

+Рисунок 1. Два разных устройства, которые используют разные ресурсы макета.

+
+ +

Почти каждое приложение должно предоставлять альтернативные ресурсы, чтобы поддерживать определенные конфигурации +устройств. Например, необходимо включить альтернативные графические ресурсы для экранов с +разной плотностью растра и альтернативные ресурсы для разных языков. В режиме выполнения Android +определяет конфигурацию устройства и загружает соответствующие +ресурсы для приложения.

+ +

Чтобы указать альтернативы для конкретных конфигураций набора ресурсов, выполните следующие действия:

+
    +
  1. Создайте новый каталог в каталоге {@code res/} с именем следующего вида {@code +<имя_ресурса>-<квалификатор_конфигурации>}. +
      +
    • {@code <resources_name>} – имя каталога соответствующих ресурсов +по умолчанию (определено в таблице 1).
    • +
    • {@code <qualifier>} – имя, которое указывает определенную конфигурацию, +для которой должны использоваться эти ресурсы (определено в таблице 2).
    • +
    +

    Можно добавлять несколько квалификаторов {@code <qualifier>}. Разделяйте их +знаком дефиса.

    +

    Предупреждение! При добавлении нескольких квалификаторов необходимо +располагать их в том же порядке, в котором они перечислены в таблице 2. Если порядок квалификаторов нарушен, + ресурсы игнорируются.

    +
  2. +
  3. Сохраните соответствующие альтернативные ресурсы в этом новом каталоге. Файлы ресурсов должны +иметь имена, точно совпадающие с именами файлов ресурсов по умолчанию.
  4. +
+ +

В качестве примера здесь приведено несколько ресурсов по умолчанию и альтернативных ресурсов:

+ +
+res/
+    drawable/   
+        icon.png
+        background.png    
+    drawable-hdpi/  
+        icon.png
+        background.png  
+
+ +

Квалификатор {@code hdpi} указывает, что ресурсы в этом каталоге предназначены для устройств, оснащенных экраном +высокой плотности. Изображения в каждом из этих каталогов для графических объектов имеют размер для определенной плотности +экрана, но имена файлов полностью +совпадают. Таким образом, идентификатор ресурса, который указывает на изображение {@code icon.png} или {@code +background.png}, всегда одинаков, но Android выбирает +версию каждого ресурса, которая оптимально соответствует текущему устройству, сравнивая информацию о конфигурации устройства +с квалификаторами в имени каталога ресурсов.

+ +

Android поддерживает несколько квалификаторов конфигурации, позволяя +добавлять несколько квалификаторов к одному имени каталога, разделяя квалификаторы дефисом. В таблице 2 +перечислены допустимые квалификаторы конфигурации в порядке приоритета — если используется несколько +квалификаторов для каталога ресурсов, необходимо добавлять их к имени каталога в том порядке, в котором +они перечислены в таблице.

+ + +

Таблица 2. Имена квалификаторов +конфигурации.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
КонфигурацияЗначения квалификатораОписание
MCC и MNCПримеры:
+ mcc310
+ mcc310-mnc004
+ mcc208-mnc00
+ и т. д. +
+

Код страны для мобильной связи (MCC), за которым может следовать код сети мобильной связи (MNC) + из SIM-карты устройства. Например, mcc310 – код США для любого поставщика услуг, + mcc310-mnc004 – код США для Verizon и mcc208-mnc00 – код Франции + для Orange.

+

Если в устройстве используется радиосвязь (телефон GSM), значения MCC и MNC добываются + из SIM-карты.

+

Можно также использовать только код MCC (например, для включения в приложении разрешенных в стране +ресурсов). Если требуется указать только язык, используйте квалификатор +язык и регион (обсуждается ниже). Если принято решение использовать квалификатор MCC и +MNC, следует делать это с осторожностью и проверить корректность его работы.

+

См. также поля конфигурации {@link +android.content.res.Configuration#mcc} и {@link +android.content.res.Configuration#mnc}, которые указывают текущий код страны при мобильной связи +и код сети мобильной связи соответственно.

+
Язык и регионПримеры:
+ en
+ fr
+ en-rUS
+ fr-rFR
+ fr-rCA
+ и т. д. +

Язык задается двухбуквенным кодом языка ISO + 639-1, к которому можно добавить двухбуквенный код региона + ISO + 3166-1-alpha-2 (которому предшествует строчная буква "{@code r}"). +

+ Коды не зависят от регистра; префикс {@code r} служит для +обозначения кода региона. + Нельзя указывать только код региона.

+

Он может измениться за время работы +приложения, если пользователь изменяет свой язык в системных настройках. В разделе Обработка изменений в режиме выполнения содержится информация +о воздействии таких изменений на приложение во время выполнения.

+

В разделе Локализация приведено полное руководство по локализации +приложения для других языков.

+

См. также поле конфигурации {@link android.content.res.Configuration#locale}, которое +указывает текущий язык.

+
Направление макетаldrtl
+ ldltr
+

Направление макета для приложения. Квалификатор {@code ldrtl} означает «направление макета справа налево». +Квалификатор {@code ldltr} означает «направление макета слева направо» и используется по умолчанию. +

+

Эти квалификаторы можно применять к любым ресурсам, таким как макеты, графические элементы или значения. +

+

Например, если требуется предоставить специальный макет для арабского языка и +общий макет для других языков, использующих написание «справа налево» (таких как фарси или иврит), используйте следующий код: +

+
+res/
+    layout/   
+        main.xml  (Default layout)
+    layout-ar/  
+        main.xml  (Specific layout for Arabic)
+    layout-ldrtl/  
+        main.xml  (Any "right-to-left" language, except
+                  for Arabic, because the "ar" language qualifier
+                  has a higher precedence.)
+
+

Примечание. Чтобы включить в приложение функцию макета «справа налево», +необходимо установить для параметра {@code + supportsRtl} значение {@code "true"} и для параметра {@code targetSdkVersion} значение 17 или больше.

+

Добавлено в API уровня 17.

+
smallestWidthsw<N>dp

+ Примеры:
+ sw320dp
+ sw600dp
+ sw720dp
+ и т. д. +
+

Основной размер экрана, указывающий минимальный размер доступной +области экрана. Точнее говоря, минимальная ширина устройства – это наименьший из двух размеров экрана: +высоты и ширины (можно также называть ее «меньшей стороной» экрана). Этот квалификатор +позволяет гарантировать, что независимо от текущей ориентации экрана +приложение имеет доступную ширину пользовательского интерфейса не менее {@code <N>} пикселов.

+

Например, если для макета требуется, чтобы минимальный размер области экрана всегда был +не менее 600 пикселов, можно использовать этот квалификатор для создания ресурсов этого макета, {@code +res/layout-sw600dp/}. Система будет использовать эти ресурсы только в том случае, если минимальный размер +доступной области экрана составляет не менее 600 пикселов, независимо от воспринимаемой пользователем +высоты или ширины. Значение минимальной ширины является постоянной характеристикой размера экрана для устройства; минимальная +ширина устройства не изменяется при изменении ориентации экрана.

+

Минимальная ширина устройства учитывает оформление экрана и пользовательский интерфейс системы. Например, +если не экране присутствуют постоянные элементы пользовательского интерфейса, которые занимают пространство вдоль +оси минимальной ширины, система объявляет, что минимальная ширина меньше фактического +размера экрана, так как эти пикселы экрана недоступны для пользовательского интерфейса приложения. Следовательно используемое значение +должно быть фактическим наименьшим размером, который необходим для вашего макета (обычно это значение является +«минимальной шириной», которую поддерживает ваш макет, независимо от текущей ориентации экрана).

+

Здесь приведены некоторые значения, которые можно использовать для экранов обычных размеров:

+
    +
  • 320 для устройств с конфигурациями экрана: +
      +
    • 240x320 ldpi (смартфон QVGA)
    • +
    • 320x480 mdpi (смартфон)
    • +
    • 480x800 hdpi (смартфон высокой плотности)
    • +
    +
  • +
  • 480 для таких экранов, как 480x800 mdpi (планшет/смартфон).
  • +
  • 600 для таких экранов, как 600x1024 mdpi (планшет с диагональю 7").
  • +
  • 720 для таких экранов, как 720x1280 mdpi (планшет с диагональю 10").
  • +
+

Когда приложение предоставляет несколько каталогов ресурсов с разными значениями +квалификатора «минимальная ширина», система использует квалификатор, ближайший +к минимальной ширине устройства, но не превышающий ее.

+

Добавлено в API уровня 13.

+

См. также атрибут {@code +android:requiresSmallestWidthDp}, который объявляет минимальную ширину, совместимую +с вашим приложением, и поле конфигурации {@link +android.content.res.Configuration#smallestScreenWidthDp}, которое содержит значение +минимальной ширины устройства.

+

Дополнительную информацию о разработке для различных экранов и использовании этого +квалификатора см. в руководстве разработчика Поддержка +нескольких экранов.

+
Доступная ширинаw<N>dp

+ Примеры:
+ w720dp
+ w1024dp
+ и т. д. +
+

Указывает минимальную доступную ширину экрана в единицах {@code dp}, для которой должен использоваться ресурс, +заданный значением <N>. Это +значение конфигурации будет изменяться в соответствии с текущей фактической шириной +при изменении альбомной/книжной ориентации.

+

Когда приложение предоставляет несколько каталогов ресурсов с разными значениями +этой конфигурации, система использует ширину, ближайшую к текущей +ширине экрана устройства, но не превышающую ее. Это значение +учитывает оформление экрана, поэтому, если устройство содержит +постоянные элементы пользовательского интерфейса вдоль левого или правого края дисплея, оно +использует значение ширины, которое меньше реального размера экрана: эти элементы +пользовательского интерфейса учитываются и уменьшают пространство, доступное для приложения.

+

Добавлено в API уровня 13.

+

См. также поле конфигурации {@link android.content.res.Configuration#screenWidthDp} +, которое содержит текущую ширину экрана.

+

Дополнительную информацию о разработке для различных экранов и использовании этого +квалификатора см. в руководстве разработчика Поддержка +нескольких экранов.

+
Доступная высотаh<N>dp

+ Примеры:
+ h720dp
+ h1024dp
+ и т. д. +
+

Указывает минимальную доступную высоту экрана в пикселах, для которой должен использоваться ресурс, +заданный значением <N>. Это +значение конфигурации будет изменяться в соответствии с текущей фактической высотой +при изменении альбомной/книжной ориентации.

+

Когда приложение предоставляет несколько каталогов ресурсов с разными значениями +этой конфигурации, система использует высоту, ближайшую к текущей +высоте экрана устройства, но не превышающую ее. Это значение +учитывает оформление экрана, поэтому, если устройство содержит +постоянные элементы пользовательского интерфейса вдоль верхнего или нижнего края дисплея, оно +использует значение высоты, которое меньше реального размера экрана: эти элементы +пользовательского интерфейса учитываются и уменьшают пространство, доступное для приложения. Элементы оформления +экрана, которые не являются постоянными (например, строка состояния телефона может быть +скрыта в полноэкранном режиме), здесь не учитываются; также не учитываются такие элементы +оформления окна, как строка заголовка или строка действий, поэтому приложения должны быть готовы к работе с меньшим +пространством, чем указано. +

Добавлено в API уровня 13.

+

См. также поле конфигурации {@link android.content.res.Configuration#screenHeightDp} +, которое содержит текущую ширину экрана.

+

Дополнительную информацию о разработке для различных экранов и использовании этого +квалификатора см. в руководстве разработчика Поддержка +нескольких экранов.

+
Размер экрана + small
+ normal
+ large
+ xlarge +
+
    +
  • {@code small}: Экраны, подобные по размеру +экрану QVGA низкой плотности. Минимальный размер макета для маленького экрана +составляет приблизительно 320x426 пикселов. Примерами являются экраны QVGA низкой плотности и VGA высокой +плотности.
  • +
  • {@code normal}: Экраны, подобные по размеру +экрану HVGA средней плотности. Минимальный +размер макета для нормального экрана составляет приблизительно 320x470 пикселов. Примерами таких экранов +являются экраны WQVGA низкой плотности, HVGA средней плотности, WVGA +высокой плотности.
  • +
  • {@code large}: Экраны, подобные по размеру +экрану VGA средней плотности. + Минимальный размер макета для большого экрана составляет приблизительно 480x640 пикселов. + Примерами являются экраны VGA и WVGA средней плотности.
  • +
  • {@code xlarge}: Экраны значительно крупнее обычного +экрана HVGA средней плотности. Минимальный размер макета для очень большого экрана составляет +приблизительно 720x960 пикселов. В большинстве случаев устройства с очень большими +экранами слишком велики для карманного использования и, скорее всего, +относятся к планшетам. Добавлено в API уровня 9.
  • +
+

Примечание. Использование квалификатора размера не подразумевает, что +ресурсы предназначены только для экранов этого размера. Если не предусмотрены +альтернативные ресурсы с квалификаторами, лучше подходящими к текущей конфигурации устройства, система может использовать +любые наиболее подходящие ресурсы.

+

Предупреждение! Если все ресурсы используют квалификатор размера, +который превосходит размер текущего экрана, система не будет использовать эти ресурсы, и приложение +аварийно завершится во время выполнения (например, если все ресурсы макета отмечены квалификатором {@code +xlarge}, но устройство оснащено экраном нормального размера).

+

Добавлено в API уровня 4.

+ +

Дополнительную информацию см. в разделе Поддержка нескольких +экранов.

+

См. также поле конфигурации {@link android.content.res.Configuration#screenLayout}, которое +указывает тип размера экрана: маленький, нормальный +или большой.

+
Формат экрана + long
+ notlong +
+
    +
  • {@code long}: Длинные экраны, такие как WQVGA, WVGA, FWVGA
  • +
  • {@code notlong}: Недлинные экраны, такие как QVGA, HVGA и VGA
  • +
+

Добавлено в API уровня 4.

+

Формат основан исключительно на соотношении сторон экрана («длинный» экран шире). Это +не связано с ориентацией экрана.

+

См. также поле конфигурации {@link android.content.res.Configuration#screenLayout}, которое +указывает, является ли экран длинным.

+
Ориентация экрана + port
+ land +
+
    +
  • {@code port}: Устройство в портретной (вертикальной) ориентации
  • +
  • {@code land}: Устройство в книжной (горизонтальной) ориентации
  • + +
+

Ориентация может измениться за время работы приложения, если +пользователь поворачивает экран. В разделе Обработка изменений в режиме выполнения содержится информация +о воздействии таких изменений на приложение во время выполнения.

+

См. также поле конфигурации {@link android.content.res.Configuration#orientation}, которое +указывает текущую ориентацию устройства.

+
Режим пользовательского интерфейса + car
+ desk
+ television
+ appliance + watch +
+
    +
  • {@code car}: Устройство подсоединено к автомобильной док-станции
  • +
  • {@code desk}: Устройство подсоединено к настольной док-станции
  • +
  • {@code television}: Устройство подсоединено к телевизору, обеспечивая +взаимодействие с расстояния «три метра», когда пользовательский интерфейс находится на большом экране, +находящемся вдалеке от пользователя, ориентированное на управление с помощью навигационной клавиши или другого +устройства без указателя
  • +
  • {@code appliance}: Устройство служит в качестве прибора без +дисплея
  • +
  • {@code watch}: Устройство с дисплеем для ношения на запястье
  • +
+

Добавлено в API уровня 8, телевизор добавлен в API 13, часы добавлены в API 20.

+

Информацию о том, как приложение может реагировать на установку устройства в +док-станцию или извлечение из нее, прочитайте документ Определение +и мониторинг типа и состояния подключения к док-станции.

+

Подключение может измениться за время работы приложения, если пользователь помещает устройство +в док-станцию. Некоторые из этих режимов можно включить или отключить с помощью {@link +android.app.UiModeManager}. В разделе Обработка изменений в режиме выполнения содержится +информация о воздействии таких изменений на приложение во время выполнения.

+
Ночной режим + night
+ notnight +
+
    +
  • {@code night}: Ночное время
  • +
  • {@code notnight}: Дневное время
  • +
+

Добавлено в API уровня 8.

+

Этот режим может измениться за время работы, если ночной режим оставлен в +автоматическом режиме (по умолчанию), в котором режим изменяется в зависимости от времени суток. Этот режим можно включить +или отключить с помощью {@link android.app.UiModeManager}. В разделе Обработка изменений в режиме выполнения содержится информация о воздействии таких +изменений на приложение во время выполнения.

+
Плотность пикселов на экране (dpi) + ldpi
+ mdpi
+ hdpi
+ xhdpi
+ xxhdpi
+ xxxhdpi
+ nodpi
+ tvdpi +
+
    +
  • {@code ldpi}: Экраны низкой плотности; приблизительно 120 dpi.
  • +
  • {@code mdpi}: Экраны средней плотности (обычные HVGA); приблизительно +160 dpi.
  • +
  • {@code hdpi}: Экраны высокой плотности; приблизительно 240 dpi.
  • +
  • {@code xhdpi}: Экраны очень высокой плотности; приблизительно 320 dpi. Добавлено в API +уровня 8.
  • +
  • {@code xxhdpi}: Экраны сверхвысокой плотности; приблизительно 480 dpi. Добавлено в API +уровня 16.
  • +
  • {@code xxxhdpi}: Использование исключительно высокой плотности (только значок запуска, см. +примечание +в документе Поддержка нескольких экранов); приблизительно 640 dpi. Добавлено в API +уровня 18.
  • +
  • {@code nodpi}: Этот режим можно использовать для растровых графических ресурсов, которые не требуется масштабировать +в соответствии с плотностью устройства.
  • +
  • {@code tvdpi}: Экраны промежуточной плотности между mdpi и hdpi; приблизительно 213 dpi. Этот режим не считается +«основной» группой плотности. Он главным образом предназначен для телевизоров, и большинство +приложений в нем не нуждается — при условии, что ресурсов mdpi и hdpi достаточно для большинства приложений, и +система будет масштабировать их при необходимости. Этот квалификатор введен в API уровня 13.
  • +
+

Шесть основных уровней плотности соотносятся как 3:4:6:8:12:16 (если игнорировать +плотность tvdpi). Так, растровое изображение 9x9 в ldpi представляется как 12x12 в mdpi, 18x18 в hdpi, 24x24 в xhdpi и т. д. +

+

Если графические ресурсы выглядят недостаточно хорошо на телевизоре или +других определенных устройствах, и хочется попробовать ресурсы tvdpi, используйте масштабный коэффициент 1,33*mdpi. Например, + изображение 100 x 100 пикселов для экранов mdpi должно иметь размер 133 x 133 пиксела для tvdpi.

+

Примечание. Использование квалификатора плотности не подразумевает, что +ресурсы предназначены только для экранов этой плотности. Если не предусмотрены +альтернативные ресурсы с квалификаторами, лучше подходящими к текущей конфигурации устройства, система может использовать +любые наиболее подходящие ресурсы.

+

Дополнительную информацию о том, как обрабатывать +различные плотности экранов, и как Android может масштабировать растровые изображения в соответствии текущей плотностью, см. в разделе Поддержка нескольких +экранов.

+
Тип сенсорного экрана + notouch
+ finger +
+
    +
  • {@code notouch}: Устройство не оснащено сенсорным экраном.
  • +
  • {@code finger}: Устройство оснащено сенсорным экраном, предназначенным +для ввода с помощью пальцев пользователя.
  • +
+

См. также поле конфигурации {@link android.content.res.Configuration#touchscreen}, которое +указывает тип сенсорного экрана устройства.

+
Доступность клавиатуры + keysexposed
+ keyshidden
+ keyssoft +
+
    +
  • {@code keysexposed}: В устройстве доступна клавиатура. Если в устройстве включена +экранная клавиатура (что весьма вероятно), она может использоваться даже в случае, когда аппаратная клавиатура + недоступна пользователю, даже если устройство не имеет аппаратной клавиатуры. Если экранная +клавиатура отсутствует или отключена, это квалификатор используется только в том случае, когда доступна +аппаратная клавиатура.
  • +
  • {@code keyshidden}: Устройство имеет аппаратную клавиатуру, но она +скрыта, и в устройстве не включена экранная клавиатура.
  • +
  • {@code keyssoft}: Экранная клавиатура в устройстве включена независимо от того, +видна она или нет.
  • +
+

Если предоставляются ресурсы keysexposed, но не предоставляются ресурсы keyssoft, +система использует ресурсы keysexposed независимо от видимости +клавиатуры, поскольку в системе включена экранная клавиатура.

+

Это состояние может измениться за время работы приложения, если +пользователь открывает аппаратную клавиатуру. В разделе Обработка изменений в режиме выполнения содержится информация о воздействии таких +изменений на приложение во время выполнения.

+

См. также поля конфигурации {@link +android.content.res.Configuration#hardKeyboardHidden} и {@link +android.content.res.Configuration#keyboardHidden}, которые указывают видимость аппаратной +клавиатуры и видимость клавиатуры любого типа (включая экранную), соответственно.

+
Основной способ ввода текста + nokeys
+ qwerty
+ 12key +
+
    +
  • {@code nokeys}: В устройстве отсутствуют аппаратные клавиши для ввода текста.
  • +
  • {@code qwerty}: Устройство оснащено аппаратной клавиатурой с раскладкой qwerty, независимо от того, видна она +пользователю +или нет.
  • +
  • {@code 12key}: Устройство оснащено аппаратной клавиатурой с 12 клавишами, независимо от того, видна она пользователю +или нет.
  • +
+

См. также поле конфигурации {@link android.content.res.Configuration#keyboard}, которое +указывает основной доступный способ ввода текста.

+
Версия платформы (уровень API)Примеры:
+ v3
+ v4
+ v7
+ и т. д.
+

Уровень API, поддерживаемый устройством. Например, v1 для уровня API +1 (устройства с Android 1.0 или выше) и v4 для уровня API 4 (устройства с Android +1.6 или выше). В документе Уровни API Android содержится дополнительная информация +об этих значениях.

+
+ + +

Примечание. Некоторые квалификаторы конфигурации добавлены после версии Android +1.0, поэтому в некоторых версиях Android поддерживаются не все квалификаторы. При использовании нового квалификатора косвенно +добавляется квалификатор версии платформы, чтобы более старые устройства игнорировали его. Например, при использовании +квалификатора w600dp автоматически добавляется квалификатор v13, так как квалификатор +доступной ширины был новым в API уровня 13. Чтобы исключить какие-либо проблемы, всегда включайте набор +ресурсов по умолчанию (набор ресурсов без квалификаторов). Для получения дополнительной информации см. +раздел Обеспечение оптимальной совместимости устройства с +ресурсами.

+ + + +

Правила квалификатора имени

+ +

Здесь приведены некоторые правила использования имен квалификаторов:

+ +
    +
  • Можно указать несколько квалификаторов для одного набора ресурсов, разделяя их дефисами. Например, + drawable-en-rUS-land применяется к устройствам в США, на английском языке в альбомной +ориентации.
  • +
  • Квалификаторы должны идти в том же порядке, в котором они перечислены в таблице 2. Например: + +
      +
    • Неправильно: drawable-hdpi-port/
    • +
    • Правильно: drawable-port-hdpi/
    • +
    +
  • +
  • Нельзя использовать вложенные каталоги альтернативных ресурсов. Например, нельзя иметь каталог +res/drawable/drawable-en/.
  • +
  • Значения не зависят от регистра букв. Компилятор ресурсов преобразует имена каталогов +в нижний регистр перед обработкой, чтобы избежать проблем в файловых системах, +не учитывающих регистр. Прописные буквы в именах служат исключительно для удобочитаемости.
  • +
  • Поддерживается только одно значение квалификатора каждого типа. Например, если требуется использовать +одинаковые графические файлы для испанского и французского языков, нельзя создавать +каталог с именем drawable-rES-rFR/. Вместо этого необходимо создать два каталога ресурсов, например, +drawable-rES/ и drawable-rFR/, которые содержат соответствующие файлы. +Однако не обязательно фактически копировать одинаковые файлы в оба каталога. Вместо этого +можно создать псевдоним для ресурса. См. раздел Создание +псевдонимов ресурсов ниже.
  • +
+ +

После сохранения альтернативных ресурсов в каталоги с именами +этих квалификаторов Android автоматически применяет ресурсы в приложении на основе текущей конфигурации +устройства. При каждом запросе ресурсов Android проверяет каталоги альтернативных +ресурсов, которые содержат файл запрошенного ресурса, затем находят +наиболее подходящий ресурс (обсуждается ниже). Если нет альтернативных ресурсов, которые +соответствуют конкретной конфигурации устройства, Android использует ресурсы по умолчанию (набор +ресурсов для конкретного типа ресурсов, которые не содержат квалификатора +конфигурации).

+ + + +

Создание псевдонимов ресурсов

+ +

Ресурс, предназначенный для нескольких конфигураций +устройства (но не являющийся ресурсом по умолчанию), следует помещать +только в один каталог альтернативных ресурсов. Вместо этого можно (в некоторых случаях) создать +альтернативный +ресурс, действующий в качестве псевдонима для ресурса, сохраненного в каталоге ресурсов по умолчанию.

+ +

Примечание. Не все ресурсы предлагают механизм, позволяющий +создавать псевдоним для другого ресурса. В частности, анимации, меню, необработанные и другие неустановленные +ресурсы в каталоге {@code xml/} не содержат такой возможности.

+ +

Например, представьте, что имеется значок приложения, {@code icon.png}, и требуется иметь уникальные версии +этого значка для разных языков. Однако в двух языках, канадском английском и канадском французском, требуется +использовать одинаковую версию. Можно предположить, что требуется скопировать одно изображение +в каталоги ресурсов для обоих языков, но +это неверно. Вместо этого можно сохранить изображение для обоих языков, как {@code icon_ca.png} (любое +имя, кроме {@code icon.png}), и поместить его +в каталог по умолчанию {@code res/drawable/}. Затем создайте файл {@code icon.xml} в каталогах {@code +res/drawable-en-rCA/} и {@code res/drawable-fr-rCA/} который ссылается на ресурс {@code icon_ca.png} +с помощью элемента {@code <bitmap>}. Это позволяет хранить только одну версию файла PNG +и два маленьких файла XML, которые указывают на него. (Пример файла XML показан ниже.)

+ + +

Графические объекты

+ +

Чтобы создать псевдоним для существующего графического объекта, используйте элемент {@code <bitmap>}. +Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/icon_ca" />
+
+ +

Если сохранить этот файл под именем {@code icon.xml} (в каталоге альтернативных ресурсов, например, +{@code res/drawable-en-rCA/}), он компилируется в ресурс, на который +можно ссылаться с помощью {@code R.drawable.icon}, но фактически он является псевдонимом для ресурса {@code +R.drawable.icon_ca} (который сохранен в каталоге {@code res/drawable/}).

+ + +

Макет

+ +

Чтобы создать псевдоним для существующего макета, используйте элемент {@code <include>} +, заключенный в теги {@code <merge>}. Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<merge>
+    <include layout="@layout/main_ltr"/>
+</merge>
+
+ +

Если сохранить этот файл под именем {@code main.xml}, он компилируется в ресурс, на который можно ссылаться +с помощью {@code R.layout.main}, но фактически он является псевдонимом для ресурса {@code R.layout.main_ltr} +.

+ + +

Строки и другие простые значения

+ +

Чтобы создать псевдоним для существующей строки используйте идентификатор ресурса нужной +строки в качестве значения для новой строки. Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello</string>
+    <string name="hi">@string/hello</string>
+</resources>
+
+ +

Ресурс {@code R.string.hi} теперь является псевдонимом для {@code R.string.hello}.

+ +

Другие простые значения работают +аналогично. Например, цвет:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="yellow">#f00</color>
+    <color name="highlight">@color/red</color>
+</resources>
+
+ + + + +

Обеспечение оптимальной совместимости устройства с ресурсами

+ +

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

+ +

Например, если приложение поддерживает несколько языков, всегда включайте каталог {@code +values/} (в котором сохранены строки) без квалификатора языка и региона. Если вместо этого поместить все файлы строк +в каталоги с квалификаторами языка и региона, приложение закроется с ошибкой при запуске +на устройстве, на котором установлен язык, отсутствующий в ваших строках. Но как только вы предоставили ресурсы +{@code values/} по умолчанию, приложение будет работать правильно (даже если пользователь не +понимает этого языка, это лучше, чем завершение с ошибкой).

+ +

Таким же образом, если вы предоставляете различные ресурсы макета в зависимости от ориентации экрана, следует +указать одну ориентацию в качестве ориентации по умолчанию. Например, вместо предоставления ресурсов макета в каталоге {@code +layout-land/} для альбомной ориентации и в каталоге {@code layout-port/} для книжной ориентации, оставьте один вариант по умолчанию: например, +{@code layout/} для альбомной и {@code layout-port/} для книжной ориентации.

+ +

Предоставление ресурсов по умолчанию важно не только потому, что приложение сможет работать на конфигурации, +которую вы не предусмотрели, но также и потому, что новые версии Android иногда добавляют +квалификаторы конфигураций, которые не поддерживаются более старыми версиями. Если вы используете новый квалификатор ресурсов, +но поддерживаете совместимость кода с более старыми версиями Android, то при выполнении вашего приложения в более старой версии +Android оно завершится в ошибкой, если вы не предусмотрели ресурсы по умолчанию, так как +оно не может использовать ресурсы, проименованные новым квалификатором. Например, если для параметра {@code +minSdkVersion} установлено значение 4 и вы квалифицировали все графические ресурсы с использованием ночного режима ({@code night} или {@code notnight}, который был добавлен в API +уровня 8), то устройства с API уровня 4 не смогут получить доступ к графическим ресурсам и приложение завершится с ошибкой. В этом +случае, вероятно, следует использовать {@code notnight} в качестве ресурсов по умолчанию и исключить этот +квалификатор, разместив графические ресурсы в каталогах {@code drawable/} или {@code drawable-night/}.

+ +

Поэтому для обеспечения оптимальной совместимости с устройствами обязательно предоставляйте ресурсы +по умолчанию, которые приложение может правильно выполнять. Затем создайте альтернативные +ресурсы для определенных конфигураций устройств с помощью квалификаторов конфигурации.

+ +

Из этого правила есть одно исключение: Если в приложении для параметра {@code minSdkVersion} установлено значение 4 или +выше, не требуется предоставлять графические ресурсы по умолчанию при предоставлении альтернативных графических +ресурсов с квалификатором плотность экрана. Даже без графических ресурсов +по умолчанию Android может найти наиболее подходящую альтернативную плотность экрана и масштабировать +растровые изображения при необходимости. Однако для оптимальной работы на устройствах всех типов следует +предоставить альтернативные графические ресурсы для всех трех типов плотности.

+ + + +

Как Android находит наиболее подходящий ресурс

+ +

Когда вы запрашиваете ресурс, для которого предоставлена альтернатива, Android выбирает +альтернативный ресурс для использования в режиме выполнения в зависимости от текущей конфигурации устройства. Чтобы +продемонстрировать, как Android выбирает альтернативный ресурс, допустим, что имеются следующие каталоги графических ресурсов, +каждый из которых содержит различные версии одинаковых изображений:

+ +
+drawable/
+drawable-en/
+drawable-fr-rCA/
+drawable-en-port/
+drawable-en-notouch-12key/
+drawable-port-ldpi/
+drawable-port-notouch-12key/
+
+ +

И допустим, что устройство имеет следующую конфигурацию:

+ +

+Язык = en-GB
+Ориентация экрана = port
+Плотность пикселов на экране = hdpi
+Тип сенсорного экрана = notouch
+Основной способ ввода текста = 12key +

+ +

Сравнивая конфигурацию устройства с доступными альтернативными ресурсами, Android выбирает +графику из каталога {@code drawable-en-port}.

+ +

Система приходит к решению об используемых ресурсах на основе следующей +логики:

+ + +
+ +

Рисунок 2. Как Android находит наиболее подходящий ресурс. +Структурная схема.

+
+ + +
    +
  1. Исключение файлов ресурсов, которые противоречат конфигурации устройства. +

    Каталог drawable-fr-rCA/ исключается, так как он +противоречит языку en-GB.

    +
    +drawable/
    +drawable-en/
    +drawable-fr-rCA/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    +

    Исключение. Квалификатор плотности пикселов на экране не исключается +вследствие противоречия. Хотя плотность экрана устройства hdpi, +каталог drawable-port-ldpi/ не исключается, так как на этом этапе любая плотность экрана +считается подходящей. Более подробная информация доступна в документе Поддержка нескольких +экранов.

  2. + +
  3. Указание (следующего) квалификатора с высшим приоритетом в списке (таблица 2). +(Начать с MCC, затем двигаться вниз.)
  4. +
  5. Содержат ли какие-либо каталоги ресурсов этот квалификатор?
  6. +
      +
    • Если Нет, вернуться к шагу 2 и найти следующий квалификатор. (В нашем примере +получается ответ «нет», пока не достигнут квалификатор языка.)
    • +
    • Если Да, перейти к шагу 4.
    • +
    + + +
  7. Исключить каталоги ресурсов, которые не содержат этого квалификатора. В данном примере система исключает +все каталоги, которые не содержат квалификатора языка:
  8. +
    +drawable/
    +drawable-en/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    +

    Исключение. Если получен квалификатор плотности пикселов на экране, +Android выбирает вариант, наиболее близко соответствующий плотности экрана устройства. +Как правило, Android предпочитает уменьшать большие исходные изображения, чем увеличивать +мелкие. См. раздел Поддержка нескольких +экранов.

    + + +
  9. Вернуться и повторять шаги 2, 3 и 4, пока не останется только один каталог. В нашем примере следующим +квалификатором, для которого есть совпадения, является ориентация экрана. +Поэтому исключаются ресурсы, не указывающие ориентацию экрана: +
    +drawable-en/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +
    +

    Остается каталог {@code drawable-en-port}.

    +
  10. +
+ +

Хотя эта процедура выполняется для каждого запрошенного ресурса, система дополнительно оптимизирует +некоторые вопросы. Одна из таких оптимизаций состоит в том, что поскольку конфигурация устройства известна, можно +исключить альтернативные ресурсы, которые не могут подойти. Например, если используется конфигурация с английским +языком ("en"), все каталоги ресурсов, для которых установлен другой квалификатор языка, +никогда не включаются в пул проверяемых ресурсов (хотя +каталоги ресурсов без квалификатора языка включаются).

+ +

При выборе ресурсов на основе квалификаторов размера экрана система будет использовать ресурсы +предназначенные для экрана, меньшего чем текущий экран, если нет более подходящих ресурсов +(например, на экранах большого размера при необходимости будут использоваться ресурсы, предназначенные для экранов нормального размера). Однако, если + единственные доступные ресурсы превосходят размер текущего экрана, система +не будет использовать эти ресурсы, и приложение аварийно завершится, если нет других ресурсов, соответствующих конфигурации +устройства (например, если все ресурсы макета отмечены квалификатором {@code xlarge}, +но устройство оснащено экраном нормального размера).

+ +

Примечание. Приоритет квалификатора (в таблице 2) более важен, +чем число квалификаторов, которые точно соответствуют устройству. Например, на шаге 4 выше, последний +вариант в списке содержит три квалификатора, которые точно соответствуют устройству (ориентация, тип +сенсорного экрана и способ ввода), в то время как drawable-en содержит только один подходящий параметр +(язык). Однако язык имеет более высокий приоритет, чем эти остальные квалификаторы, поэтому +drawable-port-notouch-12key вычеркивается.

+ +

Для получения более подробной информации об использовании ресурсов в приложении перейдите к разделу Доступ к ресурсам.

diff --git a/docs/html-intl/intl/ru/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/ru/guide/topics/resources/runtime-changes.jd new file mode 100644 index 0000000000000000000000000000000000000000..5dc59c81559ff46c84e0f789ee74145aa6e703d5 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=Обработка изменений в режиме выполнения +page.tags=операция,жизненный цикл +@jd:body + + + +

Некоторые конфигурации устройств могут изменяться в режиме выполнения +(например, ориентация экрана, доступность клавиатуры и язык). Когда происходит такое изменение, +Android перезапускает выполнение +{@link android.app.Activity} (вызывается {@link android.app.Activity#onDestroy()}, затем {@link +android.app.Activity#onCreate(Bundle) onCreate()}). Поведение при перезапуске позволяет +приложению учитывать новые конфигурации путем автоматической перезагрузки в приложение +альтернативных ресурсов, которые соответствуют новой конфигурации устройства.

+ +

Для правильной обработки перезапуска важно, чтобы операция восстанавливала предыдущее +состояние в течение нормального жизненного цикла +операции, при котором Android вызывает +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} перед уничтожением +операции, чтобы вы могли сохранить данные о состоянии приложения. Затем вы можете восстановить состояние +во время выполнения метода {@link android.app.Activity#onCreate(Bundle) onCreate()} или {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.

+ +

Чтобы проверить, перезапускается ли приложение в неизмененном состоянии, следует +вызывать изменение конфигурации (например, изменение ориентации экрана) во время выполнения различных +задач в приложении. Приложение должно перезапускаться в любой момент без потери +пользовательских данных и состояния, чтобы обработать события, например, изменение конфигурации или получение пользователем +входящего телефонного звонка с последующим возвращением в приложение после того, как процесс +приложения мог быть уничтожен. Чтобы узнать, как восстановить состояние операции, прочитайте раздел Жизненный цикл операции.

+ +

Однако возможна ситуация, при которой перезапуск приложения и +восстановление значительного объема данных может быть слишком ресурсоемким и может создавать неприятное впечатление у пользователя. В такой ситуации +есть два других варианта:

+ +
    +
  1. Сохранение объекта во время изменения конфигурации +

    Необходимо позволить перезапуск операции при изменении конфигурации, при этом перенести объект +с сохранением состояния в новый экземпляр операции.

    + +
  2. +
  3. Самостоятельная обработка изменения конфигурации +

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

    +
  4. +
+ + +

Сохранение объекта во время изменения конфигурации

+ +

Если перезапуск операции требует восстановления больших объемов данных, повторного подключения к сети +или выполнения других масштабных действий, то полный перезапуск вследствие изменения конфигурации +может тормозить работу пользователя. Кроме того, может быть невозможно полное восстановление состояния операции + из объекта {@link android.os.Bundle}, который система сохраняет с помощью обратного вызова {@link +android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()}, поскольку он не +предназначен для переноса больших объектов (таких как растровые изображения), и данные в нем должны быть преобразованы в последовательную +форму, а затем обратно, что потребляет много памяти и тормозит изменение конфигурации. В такой ситуации +вы можете облегчить бремя повторной инициализации операции путем сохранения фрагмента {@link +android.app.Fragment} в случае перезапуска операции вследствие изменения конфигурации. Этот фрагмент +может содержать ссылки на объекты с сохранением состояния, которые требуется сохранить.

+ +

Когда система Android завершает операцию вследствие изменения конфигурации, фрагменты +операции, отмеченные для сохранения, не уничтожаются. Такие фрагменты можно добавлять в операцию +для защиты объектов с сохранением состояния.

+ +

Сохранение объектов с сохранением состояния во фрагменте во время изменения конфигурации в режиме выполнения:

+ +
    +
  1. Расширьте класс {@link android.app.Fragment} и объявите ссылки на объекты, +сохраняющие состояние.
  2. +
  3. Вызовите {@link android.app.Fragment#setRetainInstance(boolean)}, когда фрагмент создан. +
  4. +
  5. Добавьте фрагмент в операцию.
  6. +
  7. Используйте {@link android.app.FragmentManager} для извлечения фрагмента при перезапуске +операции.
  8. +
+ +

В качестве привмера приведено определение фрагмента следующим образом:

+ +
+public class RetainedFragment extends Fragment {
+
+    // data object we want to retain
+    private MyDataObject data;
+
+    // this method is only called once for this fragment
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // retain this fragment
+        setRetainInstance(true);
+    }
+
+    public void setData(MyDataObject data) {
+        this.data = data;
+    }
+
+    public MyDataObject getData() {
+        return data;
+    }
+}
+
+ +

Внимание! Хотя можно сохранить любой объект, +не следует передавать объект, связанный с {@link android.app.Activity}, например, {@link +android.graphics.drawable.Drawable}, {@link android.widget.Adapter}, {@link android.view.View} +или любой другой объект, связанный с {@link android.content.Context}. В этом случае произойдет +утечка всех видов и ресурсов исходного экземпляра операции. (Утечка ресурсов +означает, что приложение удерживает их, и система не может очистить от них память, поэтому +может теряться значительный объем памяти).

+ +

Затем используйте {@link android.app.FragmentManager} для добавления фрагмента в операцию. +Можно получить объект данных из фрагмента, когда операция повторно запускается в результате изменения конфигурации +в режиме выполнения. В качестве примера операция определена следующим образом:

+ +
+public class MyActivity extends Activity {
+
+    private RetainedFragment dataFragment;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        // find the retained fragment on activity restarts
+        FragmentManager fm = getFragmentManager();
+        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
+
+        // create the fragment and data the first time
+        if (dataFragment == null) {
+            // add the fragment
+            dataFragment = new DataFragment();
+            fm.beginTransaction().add(dataFragment, “data”).commit();
+            // load the data from the web
+            dataFragment.setData(loadMyData());
+        }
+
+        // the data is available in dataFragment.getData()
+        ...
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // store the data in the fragment
+        dataFragment.setData(collectMyLoadedData());
+    }
+}
+
+ +

В этом примере {@link android.app.Activity#onCreate(Bundle) onCreate()} добавляет фрагмент +или восстанавливает ссылку на него. Метод {@link android.app.Activity#onCreate(Bundle) onCreate()} также +хранит объект, сохраняющий состояние, внутри экземпляра фрагмента. +Метод {@link android.app.Activity#onDestroy() onDestroy()} обновляет объект, сохраняющий состояние, внутри +сохраненного экземпляра фрагмента.

+ + + + + +

Самостоятельная обработка изменения конфигурации

+ +

Если приложению не требуется обновление ресурсов во время определенного изменения +конфигурации и имеются ограничения производительности, не разрешающие +перезапуск операции, можно объявить самостоятельную обработку операцией изменения конфигурации +, что запрещает системе перезапускать эту операцию.

+ +

Примечание. Самостоятельная обработка изменения конфигурации может +значительно затруднить использование альтернативных ресурсов, так как система не +применяет их автоматически. Этот подход может применяться в крайнем случае, когда необходимо избежать перезапуска в результате +изменения конфигурации, и для большинства приложений его использование не рекомендуется.

+ +

Для объявления самостоятельной обработки изменения конфигурации операцией отредактируйте соответствующий элемент {@code <activity>} +в файле манифеста и включите в него атрибут {@code +android:configChanges} со значением, представляющим конфигурацию, которую +требуется обрабатывать самостоятельно. Возможные значения перечислены в документации для атрибута {@code +android:configChanges} (наиболее часто используются значения {@code "orientation"} для +предотвращения перезапуска при изменении ориентации экрана и {@code "keyboardHidden"} для предотвращения +перезапуска при изменении доступности клавиатуры). Можно объявить несколько значений конфигурации +в атрибуте, разделяя их символом вертикальной черты {@code |}.

+ +

Например, следующий код манифеста объявляет операцию, которая обрабатывает как +изменение ориентации экрана, так и изменение доступности экрана:

+ +
+<activity android:name=".MyActivity"
+          android:configChanges="orientation|keyboardHidden"
+          android:label="@string/app_name">
+
+ +

Теперь, при изменении одного из этих элементов конфигурации, операция {@code MyActivity} не перезапускается. +Вместо этого {@code MyActivity} получает вызов {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Этот метод +получает объект {@link android.content.res.Configuration}, в котором указана +новая конфигурация устройства. При считываении полей {@link android.content.res.Configuration} +можно определить новую конфигурацию и внести соответствующие изменения, обновляя +ресурсы, которые используются в интерфейсе. В момент +вызова этого метода объект {@link android.content.res.Resources} операции обновляется +и возвращает ресурсы на основе новой конфигурации, поэтому можно просто +сбросить элементы пользовательского интерфейса без перезапуска операции системой.

+ +

Внимание! Начиная с Android 3.2 (уровень API 13), +«размер экрана» также изменяется при переходе между книжной и альбомной +ориентациями устройства. Таким образом, если в режиме выполнения требуется предотвратить перезапуск вследствие изменения ориентации при разработке для +уровня API 13 или выше (как объявлено в атрибутах {@code minSdkVersion} и {@code targetSdkVersion} +), необходимо включить значение {@code "screenSize"} в дополнение к значению {@code +"orientation"}. То есть необходимо объявить {@code +android:configChanges="orientation|screenSize"}. Однако, если приложение нацелено на уровень API +12 или ниже, ваша операция всегда обрабатывает это изменение конфигурации самостоятельно (это изменение +конфигурации не перезапускает операцию даже при работе на устройстве с Android 3.2 или более поздней версией).

+ +

Например, следующая реализация метода {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} +проверяет текущую ориентацию устройства:

+ +
+@Override
+public void onConfigurationChanged(Configuration newConfig) {
+    super.onConfigurationChanged(newConfig);
+
+    // Checks the orientation of the screen
+    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
+    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
+        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
+    }
+}
+
+ +

Объект {@link android.content.res.Configuration} представляет все текущие +конфигурации, а не только изменившиеся. В большинстве случаев неважно, +как именно изменилась конфигурация, поэтому можно просто переназначить все ресурсы, предоставляющие альтернативу +для обрабатываемой конфигурации. Например, так как объект {@link +android.content.res.Resources} обновлен, можно сбросить +любые виды {@link android.widget.ImageView} с помощью {@link android.widget.ImageView#setImageResource(int) +setImageResource()} +, и после этого будут использоваться ресурсы, соответствующие новой конфигурации (как описано в разделе Предоставление ресурсов).

+ +

Обратите внимание, что значения из полей {@link +android.content.res.Configuration} являются целыми, которые соответствуют определенным константам +из класса {@link android.content.res.Configuration}. Документацию об используемых константах +для каждого поля см. в соответствующем поле по ссылке {@link +android.content.res.Configuration}.

+ +

Помните! При объявлении операции, обрабатывающей изменение +конфигурации, вы отвечаете за сброс любых элементов, для которых вы предоставили альтернативы. Если вы +объявили, что операция обрабатывает изменение ориентации, и у вас есть изображения, которые должны быть заменены при переходе между +альбомной и книжной ориентацией, вы должны переназначить каждый ресурс каждому элементу во время выполнения метода {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.

+ +

Если вам не требуется обновлять приложение на основе этих изменений +конфигурации, можно вместо этого не реализовывать метод {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. В этом +случае все ресурсы, использованные до изменения конфигурации, продолжают использоваться +, просто не происходит перезапуск операции. Однако ваше приложение обязательно должно быть +способно выключаться и перезапускаться в предыдущем состоянии, поэтому не следует использовать эту технику +как способ избежать сохранения состояния во время нормального жизненного цикла операции. Не только потому, что +есть другие изменения конфигурации, которые приведут к перезапуску приложения, но +также и потому, что вы должны обрабатывать события, например, когда пользователь покидает приложение, и оно +закрывается до возвращения в него пользователя.

+ +

Более подробную информацию об изменениях конфигурации, которые можно обрабатывать внутри операции, см. в документации {@code +android:configChanges} и описании +класса {@link android.content.res.Configuration}.

diff --git a/docs/html-intl/intl/ru/guide/topics/ui/controls.jd b/docs/html-intl/intl/ru/guide/topics/ui/controls.jd new file mode 100644 index 0000000000000000000000000000000000000000..62f4c76686f5ed5c5ca0252594e2178ad8c3e440 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=Элементы управления вводом +parent.title=Пользовательский интерфейс +parent.link=index.html +@jd:body + +
+ +
+ +

Элементы управления вводом представляют собой интерактивные компоненты пользовательского интерфейса приложения. В Android имеется +широкий набор элементов управления, которые можно использовать в пользовательском интерфейсе, например, кнопки, текстовые поля, полосы прокрутки, +флажки, кнопки изменения масштаба, переключатели и многие другие элементы.

+ +

Чтобы добавить элемент управления вводом в пользовательский интерфейс, достаточно вставить соответствующий элемент XML в XML-файл макета. Ниже представлен пример макета +с текстовым полем и кнопкой.

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal">
+    <EditText android:id="@+id/edit_message"
+        android:layout_weight="1"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:hint="@string/edit_message" />
+    <Button android:id="@+id/button_send"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/button_send"
+        android:onClick="sendMessage" />
+</LinearLayout>
+
+ +

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

+ + +

Часто используемые элементы управления

+

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

+ +

Примечание. Ниже перечислены далеко не все элементы управления, которые имеются в системе +Android. Все они имеются в пакете {@link android.widget}. Если в вашем приложении требуется реализовать +определенный тип элемента управления вводом, вы можете создать собственные настраиваемые компоненты.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Тип элемента управленияОписаниеСвязанные классы
КнопкаКнопка, которую пользователь может нажать для выполнения действия.{@link android.widget.Button Button}
Текстовое полеРедактируемое текстовое поле. Можно воспользоваться виджетом AutoCompleteTextView, чтобы создать виджет для ввода текста с возможностью автозаполнения с помощью подсказок.{@link android.widget.EditText EditText}, {@link android.widget.AutoCompleteTextView}
ФлажокПереключатель, которым можно воспользоваться для включения или отключения функции или компонента. Флажки следует использовать при отображении группы доступных для выбора параметров, которые не являются взаимоисключающими.{@link android.widget.CheckBox CheckBox}
ПереключательЭтот элемент управления аналогичен флажку, за исключением того, что в группе элементов можно выбрать только один вариант.{@link android.widget.RadioGroup RadioGroup} +
{@link android.widget.RadioButton RadioButton}
Кнопка-переключательКнопка включения/отключения с индикатором.{@link android.widget.ToggleButton ToggleButton}
Раскрывающийся списокРаскрывающийся список параметров, в котором пользователь может выбрать только одно значение.{@link android.widget.Spinner Spinner}
Элементы выбораЭлементы диалогового окна для выбора одного значения из набора с помощью кнопок со стрелками вверх и вниз или с помощью жеста пролистывания. Для ввода значений даты (дня, месяца, года) используйте виджет DatePicker, а для ввода значений времени (часов, минут и указания времени до полудня или после [AM/PM]) — виджет TimePicker. Формат этих виджетов выбирается автоматически на основе региональных настроек на устройстве.{@link android.widget.DatePicker}, {@link android.widget.TimePicker}
diff --git a/docs/html-intl/intl/ru/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/ru/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000000000000000000000000000000..71428f6e1cdd4e407814afc809521c35fa543bdb --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=Макеты +page.tags=view,viewgroup +@jd:body + + + +

Макет определяет визуальную структуру пользовательского интерфейса, например, пользовательского интерфейса операции или виджета приложения. +Существует два способа объявить макет:

+
    +
  • Объявление элементов пользовательского интерфейса в XML. В Android имеется удобный справочник XML-элементов +для классов View и их подклассов, например таких, которые используются для виджетов и макетов.
  • +
  • Создание экземпляров элементов во время выполнения. Ваше +приложение может программным образом создавать объекты View и ViewGroup (а также управлять их свойствами).
  • +
+ +

Платформа Android предоставляет вам гибкость при использовании любого из этих способов для объявления пользовательского интерфейса приложения и его управления. Например, вы можете объявить в XML макеты по умолчанию, включая элементы экрана, которые будут отображаться в макетах, и их свойства. Затем вы можете добавить в приложение код, который позволяет изменять состояние объектов на экране (включая объявленные в XML) во время выполнения.

+ + + +

Преимущество объявления пользовательского интерфейса в файле XML заключается в том, что таким образом вы можете более эффективно отделить представление своего приложения от кода, который управляет его поведением. Описания пользовательского интерфейса находятся за пределами кода вашего приложения. Это означает, что вы можете изменять или адаптировать интерфейс без необходимости вносить правки в исходный код и повторно компилировать его. Например, можно создать разные файлы XML макета для экранов разных размеров и разных ориентаций экрана, а также для различных языков. Кроме того, объявление макета в XML упрощает визуализацию структуры пользовательского интерфейса, благодаря чему отладка проблем также становится проще. В данной статье мы научим вас объявлять макет в XML. Если вы +предпочитаете создавать экземпляры объектов View во время выполнения, обратитесь к справочной документации для классов {@link android.view.ViewGroup} и +{@link android.view.View}.

+ +

Как правило, справочник XML-элементов для объявления элементов пользовательского интерфейса точно следует структуре и правилам именования для классов и методов — названия элементов соответствуют названиям классов, а названия атрибутов соответствуют методам. Фактически, соответствие зачастую такое точное, что вы можете с легкостью догадаться, какой атрибут XML соответствует тому или иному методу класса, или какой класс соответствует заданному элементу XML. Однако следует отметить, что не все справочники являются идентичными. В некоторых случаях названия могут несколько отличаться. Например, +у элемента EditText есть атрибут text, который соответствует методу +EditText.setText().

+ +

Совет. Дополнительные сведения о различных типах макетов представлены в разделе +Часто используемые макеты. Также в инструкциях по построению +объектов представления Hello изложены сведения о создании различных макетов.

+ +

Создание XML

+ +

С помощью справочника XML-элементов, который имеется в Android, можно быстро и просто создавать макеты пользовательского интерфейса и содержащиеся в нем элементы, точно так же, как при создании веб-страниц в HTML — с помощью вложенных элементов.

+ +

В каждом файле макета должен быть всего один корневой элемент, в качестве которого должен выступать объект представления (View) или представления группы (ViewGroup). После определения корневого элемента можно приступать к добавлению дополнительных объектов макета или виджетов в качестве дочерних элементов для постепенного формирования иерархии представлений, которая определяет ваш макет. Ниже представлен пример макета XML, в котором используется вертикальный объект {@link android.widget.LinearLayout}, +в котором размещены элементы {@link android.widget.TextView} и {@link android.widget.Button}.

+
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical" >
+    <TextView android:id="@+id/text"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="Hello, I am a TextView" />
+    <Button android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Hello, I am a Button" />
+</LinearLayout>
+
+ +

После объявления макета в файле XML сохраните файл с расширением .xml +в каталог res/layout/ своего проекта Android для последующей компиляции.

+ +

Дополнительные сведения о синтаксисе для файла XML макета представлены в документе Ресурсы макета.

+ +

Загрузка ресурса XML

+ +

Во время компиляции приложения каждый файл XML макета компилируется в ресурс +{@link android.view.View}. Вам необходимо загрузить ресурс макета в коде приложения в ходе реализации метода обратного вызова +{@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()}. +Для этого вызовите метод +{@link android.app.Activity#setContentView(int) setContentView()}, передайте в него ссылку на ресурс макета в следующей форме: +R.layout.layout_file_name. +Например, если макет XML сохранен как файл main_layout.xml, загрузить его +для вашей операции необходимо следующим образом:

+
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main_layout);
+}
+
+ +

Метод обратного вызова onCreate() в вашей операции вызывается платформой Android во время +запуска операции (см. сведения о жизненных циклах в +документе +Операции).

+ + +

Атрибуты

+ +

Каждый объект View и ViewGroup поддерживают свои собственные атрибуты XML. +Некоторые атрибуты характерны только для объекта View (например, объект TextView поддерживает атрибут textSize), +однако эти атрибуты также наследуются любыми объектами View, которые могут наследовать этот класс. +Некоторые атрибуты являются общими для всех объектов View, поскольку они наследуются от корневого класса View (такие как атрибут +id). Любые другие атрибуты рассматриваются как «параметры макета». Такие атрибуты +описывают определенные ориентации макета для объекта View, которые заданы +родительским объектом ViewGroup такого объекта.

+ +

Идентификатор

+ +

У любого объекта View может быть связанный с ним целочисленный идентификатор, который служит для обозначения уникальности объекта View в иерархии. +Во время компиляции приложения этот идентификатор используется как целое число, однако идентификатор +обычно назначается в файле XML макета в виде строки в атрибуте id. +Этот атрибут XML является общим для всех объектов View +(определенных классом {@link android.view.View}), который вы будете использовать довольно часто. +Синтаксис для идентификатора внутри тега XML следующий:

+
android:id="@+id/my_button"
+ +

Символ @ в начале строки указывает на то, что обработчику XML следует выполнить синтаксический анализ остальной части +идентификатора, выполнить ее синтаксический анализ и определить ее в качестве ресурса идентификатора. Символ плюса (+) обозначает, что это имя нового ресурса, +который необходимо создать и добавить к нашим ресурсам (в файле R.java). В Android существует ряд других ресурсов +идентификатора. При ссылке на идентификатор ресурса Android вам не нужно указывать символ плюса, +однако необходимо добавить пространство имен пакета android, как указано ниже:

+
android:id="@android:id/empty"
+

После добавления пространства имен пакета android можно сослаться на идентификатор из класса ресурсовandroid.R, +а не из локального класса ресурсов.

+ +

Чтобы создать представления и сослаться на них из приложения, обычно следует выполнить указанные ниже действия.

+
    +
  1. Определите представление или виджет в файле макета и присвойте ему уникальный идентификатор: +
    +<Button android:id="@+id/my_button"
    +        android:layout_width="wrap_content"
    +        android:layout_height="wrap_content"
    +        android:text="@string/my_button_text"/>
    +
    +
  2. +
  3. Затем создайте экземпляр объекта представления и выполните его захват из макета +(обычно с помощью метода {@link android.app.Activity#onCreate(Bundle) onCreate()}): +
    +Button myButton = (Button) findViewById(R.id.my_button);
    +
    +
  4. +
+

Определение идентификаторов для объектов представления имеет важное значение при создании объекта {@link android.widget.RelativeLayout}. +В относительном макете универсальные идентификаторы используются для +расположения представлений относительно друг друга.

+

Идентификатор не обязательно должен быть уникальным в рамках всей иерархии, +а только в той ее части, где вы выполняете поиск (зачастую это может быть как раз вся иерархия, поэтому +при возможности идентификаторы должны быть полностью уникальными).

+ + +

Параметры макета

+ +

Атрибуты макета XML, которые называются layout_something, определяют +параметры макета для объекта представления, подходящие для класса ViewGroup, в котором он находится.

+ +

Каждый класс ViewGroup реализует вложенный класс, который наследует {@link +android.view.ViewGroup.LayoutParams}. В этом подклассе +имеются типы свойств, которые определяют размер и положение каждого дочернего представления, +подходящие для его группы. На рисунке 1 показано, что родительская группа +представлений определяет параметры макета для каждого дочернего представления (включая дочернюю группу представлений).

+ + +

Рисунок 1. Графическое представление иерархии представления с параметрами +макета каждого представления.

+ +

Обратите внимание, что подкласс LayoutParams имеет собственный синтаксис для задания +значений. Каждый дочерний элемент должен определять LayoutParams, которые подходят для его родительского элемента, +тогда как он сам может определять другие LayoutParams для своих дочерних элементов.

+ +

Все группы представлений включают в себя параметры ширины и высоты (layout_width и +layout_height), и каждое представление должно определять их. Многие +LayoutParams также включают дополнительные параметры полей и границ.

+ +

Для параметров ширины и высоты можно указать точные значения, хотя, возможно, +вам не захочется делать это часто. Обычно для задания значений ширины и высоты используется одна из следующих +констант:

+ +
    +
  • wrap_content — размер представления задается по размерам +его содержимого;
  • +
  • match_parent (которая до API уровня 8 называлась fill_parent ) + — размер представления определяется ограничениями, задаваемыми его родительской группой представлений.
  • +
+ +

Как правило, не рекомендуется задавать абсолютные значения ширины и высоты макета +(например, в пикселах). Вместо этого используйте относительные единицы измерения, такие как +пикселы, не зависящие от разрешения экрана (dp), wrap_contentили +match_parentЭто гарантирует одинаковое отображение +вашего приложения на устройствах с экранами разных размеров. +Принятые типы измерения определены в +документе +Доступные ресурсы.

+ + +

Размещение макета

+

+ Представление имеет прямоугольную форму. Расположение представления +определяется его координатами слева и сверху, а его размеры +— параметрами ширины и высоты. Расположение измеряется +в пикселах. +

+ +

+ Расположение представления можно получить путем вызова методов +{@link android.view.View#getLeft()} и {@link android.view.View#getTop()}. Первый возвращает координату слева (по оси X) +для прямоугольника представления. Второй возвращает верхнюю координату + (по оси Y) для прямоугольника представления. Оба этих метода +возвращают расположение представления относительно его родительского элемента. Например, +когда метод getLeft() возвращает 20, это означает, что представление находится на расстоянии 20 пикселов +от левого края его непосредственного родительского элемента. +

+ +

+ Кроме того, имеется несколько удобных методов +({@link android.view.View#getRight()} и {@link android.view.View#getBottom()}), которые позволяют избежать лишних вычислений. + Эти методы возвращают координаты правого и нижнего краев +прямоугольника представления. Например, вызов метода {@link android.view.View#getRight()} +аналогичен следующему вычислению: getLeft() + getWidth(). +

+ + +

Размер, отступ и поля

+

+ Размер представления выражается его шириной и высотой. Фактически, представление +обладает двумя парами значений «ширина-высота». +

+ +

+ Первая пара — это измеренная ширина и +измеренная высота. Эти размеры определяют размер представления +в границах своего родительского элемента. Измеренные +размеры можно получить, вызвав методы {@link android.view.View#getMeasuredWidth()} +и{@link android.view.View#getMeasuredHeight()}. +

+ +

+ Вторая пара значений — это просто ширина и высота (иногда они называются +чертежная ширина и чертежная высота). Эти размеры +определяют фактический размер представления на экране после разметки во время их +отрисовки. Эти значения могут отличаться от +измеренных ширины и высоты, хотя это и не обязательно. Значения ширины и высоты можно получить, вызвав методы +{@link android.view.View#getWidth()} и {@link android.view.View#getHeight()}. +

+ +

+ При измерении своих размеров представление учитывает заполнение. Отступ +выражается в пикселах для левой, верхней, правой и нижней частей представления. + Отступ можно использовать для смещения содержимого представления на определенное количество +пикселов. Например, значение отступа слева, равное 2, приведет к тому, что содержимое представления будет смещено +на 2 пиксела вправо от левого края представления. Для задания отступов можно использовать метод +{@link android.view.View#setPadding(int, int, int, int)}. Чтобы запросить отступ, используются методы +{@link android.view.View#getPaddingLeft()}, {@link android.view.View#getPaddingTop()}, +{@link android.view.View#getPaddingRight()} и {@link android.view.View#getPaddingBottom()}. +

+ +

+ Даже если представление может определить отступ, в нем отсутствует поддержка +полей. Такая возможность имеется у группы представлений. Дополнительные сведения представлены в справке по объектам +{@link android.view.ViewGroup} и +{@link android.view.ViewGroup.MarginLayoutParams}. +

+ +

Дополнительные сведения о размерах представлены в разделе +Значения размеров. +

+ + + + + + + + + + + +

Часто используемые макеты

+ +

В каждом подклассе класса {@link android.view.ViewGroup} имеется уникальный способ отображения +вложенных в него представлений. Ниже представлены некоторые из наиболее часто используемых типов макетов, которые входят в состав платформы +Android.

+ +

Примечание. Несмотря на то, что для формирования пользовательского интерфейса один +макет может содержать один или несколько вложенных макетов, рекомендуется использовать как можно более простую +иерархию макетов. Чем меньше в макете вложенных элементов, тем быстрее выполняется его отрисовка (горизонтальная иерархия +представлений намного лучше вертикальной).

+ + + + +
+

Линейный макет

+ +

Макет, в котором дочерние элементы представлены в горизонтальных или вертикальных столбцах. Если длина окна больше длины экрана, в нем +создается полоса прокрутки.

+
+ +
+

Относительный макет

+ +

В этом макете можно задавать расположение дочерних объектов относительно друг друга (дочерний элемент А находится +слева от дочернего элемента Б) или относительно родительского объекта (выравнивание относительно верхнего края родительского элемента).

+
+ +
+

Представление веб-страницы

+ +

Отображение веб-страниц.

+
+ + + + +

Создание макетов с помощью адаптера

+ +

Если содержимое макета является динамическим или не определено заранее, можно использовать макет, который создает подклассы класса +{@link android.widget.AdapterView} для заполнения макета представлениями во время выполнения. Подкласс класса +{@link android.widget.AdapterView} использует {@link android.widget.Adapter} для +привязки данных к своему макету. {@link android.widget.Adapter} выступает в качестве посредника между источником +данных и макетом {@link android.widget.AdapterView} — {@link android.widget.Adapter} +извлекает данные (из источника, например, массива или запроса к базе данных) и преобразует каждую запись +в представление, которое можно добавить в макет {@link android.widget.AdapterView}.

+ +

Часто используемые макеты, для создания которых можно использовать адаптер, включают в себя следующие:

+ +
+

Представление в виде списка

+ +

Отображение списка в один столбец с возможностью прокрутки.

+
+ +
+

Представление в виде сетки

+ +

Отображение сетки из столбцов и строк с возможностью прокрутки.

+
+ + + +

Заполнение представления адаптера данными

+ +

Чтобы заполнить адаптер {@link android.widget.AdapterView}, такой как {@link android.widget.ListView} или +{@link android.widget.GridView}, свяжите экземпляр {@link android.widget.AdapterView} с приложением +{@link android.widget.Adapter}, которое извлекает данные из внешнего источника и создает объект {@link +android.view.View}, который представляет каждую запись данных.

+ +

В Android предусмотрено несколько подклассов адаптера {@link android.widget.Adapter}, которые полезно использовать для +извлечения данных различных видов и создания представлений для {@link android.widget.AdapterView}. Вот +два наиболее часто используемых адаптера:

+ +
+
{@link android.widget.ArrayAdapter}
+
Этот адаптер используется в случае, когда в качестве источника данных выступает массив. По умолчанию {@link +android.widget.ArrayAdapter} создает представление для каждого элемента массива путем вызова метода {@link +java.lang.Object#toString()} для каждого элемента и помещения его содержимого в объект {@link +android.widget.TextView}. +

Например, если имеется массив строк, которые необходимо отобразить в объекте {@link +android.widget.ListView}, запустите новый {@link android.widget.ArrayAdapter} с помощью +конструктора, чтобы указать макет для каждой строки и массива строк:

+
+ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+        android.R.layout.simple_list_item_1, myStringArray);
+
+

Аргументы для такого конструктора следующие:

+
    +
  • ваше приложение {@link android.content.Context};
  • +
  • макет, в котором содержится объект {@link android.widget.TextView} для каждой строки в массиве;
  • +
  • массив строк.
  • +
+

Затем достаточно вызвать метод +{@link android.widget.ListView#setAdapter setAdapter()} для своего объекта {@link android.widget.ListView}:

+
+ListView listView = (ListView) findViewById(R.id.listview);
+listView.setAdapter(adapter);
+
+ +

Чтобы настроить внешний вид каждого элемента, можно переопределить метод {@link +java.lang.Object#toString()} для объектов в массиве. Либо можно создать представление для каждого элемента, +который отличается от{@link android.widget.TextView} (например, если для каждого элемента массива требуется объект +{@link android.widget.ImageView}), наследовать класс {@link +android.widget.ArrayAdapter} и переопределить метод {@link android.widget.ArrayAdapter#getView +getView()}, чтобы возвратить требуемый тип представления для каждого элемента.

+ +
+ +
{@link android.widget.SimpleCursorAdapter}
+
Этот адаптер используется в случае, когда в качестве источника данных выступает объект {@link android.database.Cursor}. Если +используется {@link android.widget.SimpleCursorAdapter}, необходимо указать макет, который будет +использоваться для каждой строки в объекте {@link android.database.Cursor}, а также в какие представления макета необходимо вставить столбцы в объекте {@link android.database.Cursor} +. Например, если необходимо создать список +имен людей с их номерами телефонов, можно выполнить запрос, который возвращает объект {@link +android.database.Cursor}, содержащий строку для каждого человека и столбцы для имен и номеров +телефонов. Затем создается массив строк с указанием столбцов из объекта {@link +android.database.Cursor}, которые необходимо поместить в макет для каждого результата, а также массив целых чисел, в котором указаны +соответствующие представления для вставки каждого столбца:

+
+String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
+                        ContactsContract.CommonDataKinds.Phone.NUMBER};
+int[] toViews = {R.id.display_name, R.id.phone_number};
+
+

При создании экземпляра объекта {@link android.widget.SimpleCursorAdapter} передайте в него макет, +который будет использоваться для каждого результата, объект {@link android.database.Cursor} с результатами и два следующих массива:

+
+SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
+        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
+ListView listView = getListView();
+listView.setAdapter(adapter);
+
+

Затем {@link android.widget.SimpleCursorAdapter} создает представление для каждой строки в объекте +{@link android.database.Cursor} с помощью предоставленного макета путем вставки каждого элемента {@code +fromColumns} в соответствующее представление {@code toViews}.

.
+
+ + +

Если в течение жизненного цикла вашего приложения вы вносите изменения в соответствующие данные, чтение которых +выполняет адаптер, вам следует вызвать метод {@link android.widget.ArrayAdapter#notifyDataSetChanged()}. Это позволит +уведомить связанное представление о том, что данные были изменены и ему требуется выполнить обновление.

+ + + +

Обработка нажатий

+ +

Чтобы организовать реагирование на нажатие каждого элемента в {@link android.widget.AdapterView}, +реализуйте интерфейс {@link android.widget.AdapterView.OnItemClickListener}. Например:

+ +
+// Create a message handling object as an anonymous class.
+private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
+    public void onItemClick(AdapterView parent, View v, int position, long id) {
+        // Do something in response to the click
+    }
+};
+
+listView.setOnItemClickListener(mMessageClickedHandler);
+
+ + + diff --git a/docs/html-intl/intl/ru/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/ru/guide/topics/ui/dialogs.jd new file mode 100644 index 0000000000000000000000000000000000000000..515ecc6a0130d97cf6cdc40b7d4e7a6be4980b88 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=Диалоговые окна +page.tags=alertdialog,dialogfragment + +@jd:body + + + + + +

Диалоговое окно — это небольшое окно, которое предлагает пользователю +принять решение или ввести дополнительную информацию. Диалоговое окно не заполняет весь экран и, +как правило, используется при модальных событиях, для продолжения которых требуется действие пользователя.

+ +
+

Дизайн диалогового окна

+

Подробную информацию о дизайне диалоговых окон, включая рекомендации + для вашего языка см. в Руководстве по дизайну диалоговых окон.

+
+ + + +

Класс {@link android.app.Dialog} — это базовый класс для создания диалоговых окон, но +реализовывать напрямую класс {@link android.app.Dialog} не рекомендуется. +Вместо этого следует использовать один из следующих подклассов:

+
+
{@link android.app.AlertDialog}
+
Диалоговое окно, в котором могут отображаться заголовок, кнопки вплоть до трех штук, список + из выбираемых элементов либо пользовательский макет.
+
{@link android.app.DatePickerDialog} или {@link android.app.TimePickerDialog}
+
Диалоговое окно с предопределенным пользовательским интерфейсом, с помощью которого пользователь указывает значения даты или времени.
+
+ + + +

Эти классы определяют стиль и структуру вашего диалогового окна, однако следует +использовать{@link android.support.v4.app.DialogFragment} в качестве контейнера вашего диалогового окна. +Класс {@link android.support.v4.app.DialogFragment} предоставляет все функции, +необходимые для создания диалогового окна и управления его внешним видом, вместо вызова методов +к объекту{@link android.app.Dialog}.

+ +

Использование {@link android.support.v4.app.DialogFragment} для управления диалоговым окном +обеспечивает корректную обработку событий жизненного цикла +, таких как нажатие пользователем кнопки Назад или поворот экрана. С помощью класса {@link +android.support.v4.app.DialogFragment} также происходит повторное использование пользовательского интерфейса диалогового окна в качестве +встраиваемого компонента в пользовательский интерфейс более высокого уровня — подобно традиционному классу {@link +android.support.v4.app.Fragment} (например, когда необходимо различное отображение пользовательского интерфеса диалогового окна +на больших и маленьких экранах).

+ +

В следующих разделах руководства описано использование {@link +android.support.v4.app.DialogFragment} в сочетании с объектом {@link android.app.AlertDialog} +. Если необходимо создать элемент выбора даты или времени, обратитесь к руководству +Элементы выбора.

+ +

Примечание: +Поскольку класс {@link android.app.DialogFragment} изначально включен в +Android 3.0 (уровень API 11), в настоящем документе описывается использование класса {@link +android.support.v4.app.DialogFragment}, предоставляемого в Библиотеке поддержки. После добавления этой библиотеки +в приложение появится возможность использовать{@link android.support.v4.app.DialogFragment} и множество других +API на устройствах, работающих на Android 1.6 и выше. Если минимальная версия вашего приложения поддерживает +уровень API 11 и выше, можно использовать фреймворк-версию {@link +android.app.DialogFragment}, но следует иметь в виду, что данный документ ссылается на API + со вспомогательными библиотеками. При использовании вспомогательной библиотеки +необходимо импортировать класс android.support.v4.app.DialogFragment +, а не android.app.DialogFragment.

+ + +

Создание фрагмента диалогового окна

+ +

Вы можете реализовать широкие возможности дизайна диалоговых окон, включая создание +пользовательских макетов, а также пути, описанные в руководстве по дизайну Диалоговых окон +—путем расширения +{@link android.support.v4.app.DialogFragment} и создания{@link android.app.AlertDialog} +в методе обратного вызова {@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()}.

+ +

Например, имеется базовый класс {@link android.app.AlertDialog}, управляемый в рамках +{@link android.support.v4.app.DialogFragment}:

+ +
+public class FireMissilesDialogFragment extends DialogFragment {
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Use the Builder class for convenient dialog construction
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(R.string.dialog_fire_missiles)
+               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // FIRE ZE MISSILES!
+                   }
+               })
+               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // User cancelled the dialog
+                   }
+               });
+        // Create the AlertDialog object and return it
+        return builder.create();
+    }
+}
+
+ +
+ +

Рисунок 1. +Диалоговое окно с сообщением и двумя кнопками действия.

+
+ +

Итак, после создания экземпляра этого класса и вызова {@link +android.support.v4.app.DialogFragment#show show()} к этому объекту появляется диалоговое окно, +показанное на рисунке 1.

+ +

В следующем разделе предоставлена более подробная информация об использовании API {@link android.app.AlertDialog.Builder} +для создания диалоговых окон.

+ +

В зависимости от сложности создаваемого диалогового окна возможно реализовать множество других методов +обратного вызова в {@link android.support.v4.app.DialogFragment}, включая все базовые +методы жизненных циклов фрагментов. + + + + + +

Создание диалогового окна оповещения

+ + +

С помощью класса {@link android.app.AlertDialog} создается многообразие решений касательно внешнего вида диалогового окна +, и зачастую этого класса вполне достаточно. +Как показано на рис. 2, диалоговое окно состоит из трех областей:

+ +
+ +

Рисунок 2. Макет диалогового окна.

+
+ +
    +
  1. Заголовок +

    Это дополнительная возможность, которая используется только в случае, если область содержимого + занята подробным сообщением, списком или пользовательским макетом. Если необходимо отобразить + простое сообщение или вопрос (как, например, в диалоге на рисунке 1), заголовок не нужен.

  2. +
  3. Область содержимого +

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

  4. +
  5. Кнопки действия +

    В диалоговом окне не должно содержаться более трех кнопок действия.

  6. +
+ +

Класс {@link android.app.AlertDialog.Builder} +предоставляет API, с помощью которых можно создавать {@link android.app.AlertDialog} +с этими видами содержимого, включая пользовательский макет.

+ +

Создание {@link android.app.AlertDialog}:

+ +
+// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+// 2. Chain together various setter methods to set the dialog characteristics
+builder.setMessage(R.string.dialog_message)
+       .setTitle(R.string.dialog_title);
+
+// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}
+AlertDialog dialog = builder.create();
+
+ +

В следующих главах показано, как определять различные атрибуты диалоговых окон с помощью класса +{@link android.app.AlertDialog.Builder}.

+ + + + +

Добавление кнопок

+ +

Для добавления кнопок, изображенных на рисунке 2, +вызывайте методы {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} и +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()}:

+ +
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+// Add the buttons
+builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+           public void onClick(DialogInterface dialog, int id) {
+               // User clicked OK button
+           }
+       });
+builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+           public void onClick(DialogInterface dialog, int id) {
+               // User cancelled the dialog
+           }
+       });
+// Set other dialog properties
+...
+
+// Create the AlertDialog
+AlertDialog dialog = builder.create();
+
+ +

Методы set...Button() предполагают заголовок для кнопки (реализуемый +через строковый ресурс) и +{@link android.content.DialogInterface.OnClickListener}, который определяет действие, +следующее за нажатием кнопки пользователем.

+ +

Реализована возможность добавлять три различных вида кнопок действий:

+
+
Положительные
+
Используются для подтверждения и продолжения дейстия (кнопка «ОК»).
+
Отрицательные
+
Используются для отмены действия.
+
Нейтральные
+
Используются в случаях, когда пользователь может не желать продолжить действие, + но при этом необязательно хочет его отменить. Появляется между положительными и отрицательнымиI + кнопками. Примером такого действия может быть «Напомнить позже».
+
+ +

Можно добавлять только одну кнопку каждого вида в {@link +android.app.AlertDialog}. Это означает, что нельзя использовать более одной «положительной» кнопки.

+ + + +
+ +

Рисунок 3. +Диалоговое окно с заголовком и списком.

+
+ +

Добавление списка

+ +

В API {@link android.app.AlertDialog} реализована возможность использования трех видов списков:

+
    +
  • Традиционный список с выбором одного варианта
  • +
  • Интерактивный список с выбором одного варианта (переключатели)
  • +
  • Интерактивный список с выбором нескольких вариантов (флажки)
  • +
+ +

Для создания списка с выбором одного варианта, как на рисунке 3, +используйте метод{@link android.app.AlertDialog.Builder#setItems setItems()}:

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    builder.setTitle(R.string.pick_color)
+           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
+               public void onClick(DialogInterface dialog, int which) {
+               // The 'which' argument contains the index position
+               // of the selected item
+           }
+    });
+    return builder.create();
+}
+
+ +

Поскольку список отображается в области содержимого диалогового окна, +диалоговое окно не может показать одновременно сообщение и список, поэтому необходимо задать заголовок +диалогового окна с помощью {@link android.app.AlertDialog.Builder#setTitle setTitle()}. +Для указания элементов списка необходимо вызвать {@link +android.app.AlertDialog.Builder#setItems setItems()}, передающий указатель. +В качестве другого варианта можно указать список с помощью {@link +android.app.AlertDialog.Builder#setAdapter setAdapter()}. Наполнение списка +динамическими данными (например, из базы данных) происходит с помощью {@link android.widget.ListAdapter}.

+ +

Если вы предпочтете реализовать список с помощью {@link android.widget.ListAdapter}, +рекомендуется использовать {@link android.support.v4.content.Loader}, чтобы содержимое загружалось +асинхронно. Подробно этот процесс описан далее в руководстве по +Созданию макетов +с помощью адаптера и загрузчиков +.

+ +

Примечание: По умолчанию нажатие по элементу списка отменяет диалоговое окно, +за исключением случаев, когда используется один из следующих интерактивных списков.

+ +
+ +

Рисунок 4. +Список с несколькими вариантами ответов.

+
+ + +

Добавление интерактивного списка с одним или несколькими вариантами ответов

+ +

Для добавления списка с несколькими вариантами ответов (флажки) или +списка с одним вариантом ответа (переключатели) используйте методы +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} или +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} соответственно.

+ +

Например, таким образом можно создать список с несколькими вариантами ответов, как на +рисунке 4, который сохраняет выбранные +элементы в {@link java.util.ArrayList}:

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    mSelectedItems = new ArrayList();  // Where we track the selected items
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    // Set the dialog title
+    builder.setTitle(R.string.pick_toppings)
+    // Specify the list array, the items to be selected by default (null for none),
+    // and the listener through which to receive callbacks when items are selected
+           .setMultiChoiceItems(R.array.toppings, null,
+                      new DialogInterface.OnMultiChoiceClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int which,
+                       boolean isChecked) {
+                   if (isChecked) {
+                       // If the user checked the item, add it to the selected items
+                       mSelectedItems.add(which);
+                   } else if (mSelectedItems.contains(which)) {
+                       // Else, if the item is already in the array, remove it 
+                       mSelectedItems.remove(Integer.valueOf(which));
+                   }
+               }
+           })
+    // Set the action buttons
+           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   // User clicked OK, so save the mSelectedItems results somewhere
+                   // or return them to the component that opened the dialog
+                   ...
+               }
+           })
+           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   ...
+               }
+           });
+
+    return builder.create();
+}
+
+ +

Несмотря на то, что и традиционный список, и список с переключателями +предполагают действие по выбору одного элемента, вам необходимо использовать {@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()}, чтобы сохранить выбор пользователя. +Это значит, что при повторном открытии диалогового окна будет отображаться текущий выбор пользователя, +а затем создается список с переключателями.

+ + + + + +

Создание пользовательского макета

+ +
+ +

Рисунок 5. Пользовательский макет диалогового окна.

+
+ +

Если в диалоговом окне необходим пользовательский макет, нужно создать макет и добавить его в +{@link android.app.AlertDialog} путем вызова {@link +android.app.AlertDialog.Builder#setView setView()} в объекте {@link +android.app.AlertDialog.Builder}.

+ +

По умолчанию пользовательский мает заполняет окно диалога, при это все равно можно +использовать методы {@link android.app.AlertDialog.Builder} для добавления кнопок и заголовка.

+ +

В качестве примера на рисунке 5 приведен файл макета для диалогового окна.

+ +

res/layout/dialog_signin.xml

+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <ImageView
+        android:src="@drawable/header_logo"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:scaleType="center"
+        android:background="#FFFFBB33"
+        android:contentDescription="@string/app_name" />
+    <EditText
+        android:id="@+id/username"
+        android:inputType="textEmailAddress"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="4dp"
+        android:hint="@string/username" />
+    <EditText
+        android:id="@+id/password"
+        android:inputType="textPassword"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        android:fontFamily="sans-serif"
+        android:hint="@string/password"/>
+</LinearLayout>
+
+ +

Совет. По умолчанию при настройке элемента {@link android.widget.EditText} + для типа ввода {@code "textPassword"} используется семейство шрифтов фиксированной ширины, поэтому +необходимо изменить семейство шрифтов на{@code "sans-serif"}, чтобы в обоих текстовых полях использовались +одинаковые стили шрифта.

+ +

Для применения макета в вашем {@link android.support.v4.app.DialogFragment} +вам понадобится {@link android.view.LayoutInflater} с +{@link android.app.Activity#getLayoutInflater()} и вызов +{@link android.view.LayoutInflater#inflate inflate()}, где первым параметром будет являться +ID ресурса макета, а вторым параметром — исходный вид макета. +Затем можно вызвать{@link android.app.AlertDialog#setView setView()} +для размещения макета в диалоговом окне.

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    // Get the layout inflater
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+
+    // Inflate and set the layout for the dialog
+    // Pass null as the parent view because its going in the dialog layout
+    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
+    // Add action buttons
+           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   // sign in the user ...
+               }
+           })
+           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+               public void onClick(DialogInterface dialog, int id) {
+                   LoginDialogFragment.this.getDialog().cancel();
+               }
+           });      
+    return builder.create();
+}
+
+ +
+

Совет. Если необходимо пользовательское диалоговое окно, +можно отображать {@link android.app.Activity} в качестве диалога +вместо API {@link android.app.Dialog}. Нужно создать операцию и установить тему +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog} +в элементе манифеста{@code +<операция>}:

+ +
+<activity android:theme="@android:style/Theme.Holo.Dialog" >
+
+

Готово. Операция теперь отображается в диалоговом окне, а не в полноэкранном режиме.

+
+ + + +

Передача событий обратно в основное диалоговое приложение

+ +

Когда пользователь нажимает одну из кнопок действий в диалоговом окне, либо выбирает элемент из списка, + {@link android.support.v4.app.DialogFragment} может самостоятельно произвести необходимое +действие, однако зачастую вам может понадобиться доставить информацию о событии операции или фрагменту, которые +открыли диалоговое окно. Для этого нобходимо определить интерфейс метода для каждого типа события нажатия. +Затем этот интерфейс применяется в основном компоненте приложения, которое +получает информацию о событиях из диалогового окна.

+ +

Например, {@link android.support.v4.app.DialogFragment} определяет +интерфейс, по который доставляет события обратно в основной компонент операции:

+ +
+public class NoticeDialogFragment extends DialogFragment {
+    
+    /* The activity that creates an instance of this dialog fragment must
+     * implement this interface in order to receive event callbacks.
+     * Each method passes the DialogFragment in case the host needs to query it. */
+    public interface NoticeDialogListener {
+        public void onDialogPositiveClick(DialogFragment dialog);
+        public void onDialogNegativeClick(DialogFragment dialog);
+    }
+    
+    // Use this instance of the interface to deliver action events
+    NoticeDialogListener mListener;
+    
+    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        // Verify that the host activity implements the callback interface
+        try {
+            // Instantiate the NoticeDialogListener so we can send events to the host
+            mListener = (NoticeDialogListener) activity;
+        } catch (ClassCastException e) {
+            // The activity doesn't implement the interface, throw exception
+            throw new ClassCastException(activity.toString()
+                    + " must implement NoticeDialogListener");
+        }
+    }
+    ...
+}
+
+ +

Операция, выполняемая диалоговым окном, создает экземпляр диалогового окна +с помощью конструктора фрагментов диалогового окна и получает события +диалога с помощью реализации интерфейса {@code NoticeDialogListener}:

+ +
+public class MainActivity extends FragmentActivity
+                          implements NoticeDialogFragment.NoticeDialogListener{
+    ...
+    
+    public void showNoticeDialog() {
+        // Create an instance of the dialog fragment and show it
+        DialogFragment dialog = new NoticeDialogFragment();
+        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
+    }
+
+    // The dialog fragment receives a reference to this Activity through the
+    // Fragment.onAttach() callback, which it uses to call the following methods
+    // defined by the NoticeDialogFragment.NoticeDialogListener interface
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // User touched the dialog's positive button
+        ...
+    }
+
+    @Override
+    public void onDialogNegativeClick(DialogFragment dialog) {
+        // User touched the dialog's negative button
+        ...
+    }
+}
+
+ +

Поскольку выполняемая операция реализуется через {@code NoticeDialogListener} +с помощью метода обратного вызова + {@link android.support.v4.app.Fragment#onAttach onAttach()}, во фрагменте диалога могут использоваться +методы обратного вызова интерфейса для доставки событий нажатий к операциям:

+ +
+public class NoticeDialogFragment extends DialogFragment {
+    ...
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Build the dialog and set up the button click handlers
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(R.string.dialog_fire_missiles)
+               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // Send the positive button event back to the host activity
+                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
+                   }
+               })
+               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // Send the negative button event back to the host activity
+                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
+                   }
+               });
+        return builder.create();
+    }
+}
+
+ + + +

Отображение диалогового окна

+ +

Для отображения диалогового окна необходимо создать экземпляр {@link +android.support.v4.app.DialogFragment} и вызвать {@link android.support.v4.app.DialogFragment#show +show()}, передавая {@link android.support.v4.app.FragmentManager} и наименование тега +для фрагмента диалога.

+ +

Можно получить {@link android.support.v4.app.FragmentManager} путем вызова +{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} из + {@link android.support.v4.app.FragmentActivity} или {@link +android.support.v4.app.Fragment#getFragmentManager()} из {@link +android.support.v4.app.Fragment}. Пример:

+ +
+public void confirmFireMissiles() {
+    DialogFragment newFragment = new FireMissilesDialogFragment();
+    newFragment.show(getSupportFragmentManager(), "missiles");
+}
+
+ +

Второй аргумент, {@code "missiles"}, — это уникальное наименование тега, которое система использует для сохранения +и восстановления состояния фрагмента, когда это необходимо. С помощью этого тега также можно управлять +фрагментом путем вызова {@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

+ + + + +

Отображение диалогового окна в полноэкранном режиме или в виде встроенного фрагмента

+ +

Вам может понадобиться макет пользовательского интерфейса, в котором в некоторых ситуациях часть пользовательского интерфейса должна появляться как диалоговое окно +, отображаемое в полноэкранном режиме либо в виде встроенного фрагмента (возможно, в зависимости от того, +маленький или большой экран у устройства). С помощью класса {@link android.support.v4.app.DialogFragment} +обеспечивается гибкость решения, поскольку он может вести себя как встраиваемый {@link +android.support.v4.app.Fragment}.

+ +

Тем не менее, в этом случае нельзя использовать{@link android.app.AlertDialog.Builder AlertDialog.Builder} +или другие объекты {@link android.app.Dialog} для построения диалогового окна. Если +необходимо сделать {@link android.support.v4.app.DialogFragment} +встраиваемым, нужно определить пользовательский интерфейс диалогового окна в макете методом обратного вызова +{@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()}.

+ +

В качестве примера приведен {@link android.support.v4.app.DialogFragment}, который появляется либо в виде +диалогового окна, либо в виде встраиваемого фрагмента (используя макет с наименованием purchase_items.xml):

+ +
+public class CustomDialogFragment extends DialogFragment {
+    /** The system calls this to get the DialogFragment's layout, regardless
+        of whether it's being displayed as a dialog or an embedded fragment. */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate the layout to use as dialog or embedded fragment
+        return inflater.inflate(R.layout.purchase_items, container, false);
+    }
+  
+    /** The system calls this only when creating the layout in a dialog. */
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // The only reason you might override this method when using onCreateView() is
+        // to modify any dialog characteristics. For example, the dialog includes a
+        // title by default, but your custom layout might not need it. So here you can
+        // remove the dialog title, but you must call the superclass to get the Dialog.
+        Dialog dialog = super.onCreateDialog(savedInstanceState);
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        return dialog;
+    }
+}
+
+ +

Приведен пример кода, реализующего принятие решения об отображении фргмента в качестве диалогового окна +или полноэкранного пользовательского интерфейса на основе размера экрана:

+ +
+public void showDialog() {
+    FragmentManager fragmentManager = getSupportFragmentManager();
+    CustomDialogFragment newFragment = new CustomDialogFragment();
+    
+    if (mIsLargeLayout) {
+        // The device is using a large layout, so show the fragment as a dialog
+        newFragment.show(fragmentManager, "dialog");
+    } else {
+        // The device is smaller, so show the fragment fullscreen
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        // For a little polish, specify a transition animation
+        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+        // To make it fullscreen, use the 'content' root view as the container
+        // for the fragment, which is always the root view for the activity
+        transaction.add(android.R.id.content, newFragment)
+                   .addToBackStack(null).commit();
+    }
+}
+
+ +

Подробные сведения о выполнении операций с фрагментами приведены в руководстве +Фрагменты.

+ +

В приведенном примере mIsLargeLayout булеан указывает, должно ли текущее устройство использовать + большой макет приложения (и отображать фрагмент как диалоговое окно, а не +в полноэкранном режиме). Лучшим способом установить такой вид булеана является объявление +значения булевой переменнойальтернативным значением для других размеров экранов. В качестве примера приведены два +варианта булевых ресурсов для различных размеров экранов:

+ +

res/values/bools.xml

+
+<!-- Default boolean values -->
+<resources>
+    <bool name="large_layout">false</bool>
+</resources>
+
+ +

res/values-large/bools.xml

+
+<!-- Large screen boolean values -->
+<resources>
+    <bool name="large_layout">true</bool>
+</resources>
+
+ +

Затем можно инизиализировать значение {@code mIsLargeLayout} в течение выполнения метода операции +{@link android.app.Activity#onCreate onCreate()}:

+ +
+boolean mIsLargeLayout;
+
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_main);
+
+    mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
+}
+
+ + + +

Отображение операции в качестве диалога на больших экранах

+ +

Вместо отображения диалогового окна в полноэкранном режиме на экранах малого размера можно +отображать {@link android.app.Activity} в качестве диалогового окна на +экранах большого размера. Выбор зависит от дизайна приложения, но +отображение операции в качестве диалогового окна имеет смысл, когда приложение предназначено для использования на малых +экранах, и необходимо улучшить взаимодейтсвие с ним на планшетах, показывая кратковременные операции +в качестве диалогового окна.

+ +

Для отображения операции в качестве диалогового окна только на больших экранах +необходимо применить тему {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} +к элементу манифеста {@code +<операция>}:

+ +
+<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
+
+ +

Подробная информация о темах операций приведена в руководстве Стили и темы.

+ + + +

Закрытие диалогового окна

+ +

Когда пользователь нажимает кнопки, созданные с помощью +{@link android.app.AlertDialog.Builder}, система закрывает диалоговое окно самостоятельно.

+ +

Система также закрывает диалоговое окно, когда пользователь нажимает на элемент списка в диалоговом окне, за исключением +списков с переключателями или флажками. В иных случаях можно вручную закрыть диалоговое окно +путем вызова {@link android.support.v4.app.DialogFragment#dismiss()} в {@link +android.support.v4.app.DialogFragment}.

+ +

В случае, если необходимо произвести определенные +действия после закрытия диалогового окна, можно реализовать метод {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} в {@link +android.support.v4.app.DialogFragment}.

+ +

Также можно отменить диалоговое окно. Это особое событие, возникающее, когда пользователь +покинул диалоговое окно, не завершив задачу. Так происходит, когда пользователь нажимает кнопку +Назад, касается экрана за областью диалогового окна, +либо когда задано {@link android.app.Dialog#cancel()} в {@link +android.app.Dialog} (например, в качестве отклика на нажатие кнопки «Отмена» в диалоговом окне).

+ +

Как показано в примере выше, можно ответить на событие отмены с помощью +{@link android.support.v4.app.DialogFragment#onCancel onCancel()} в классе {@link +android.support.v4.app.DialogFragment}.

+ +

Примечание: Система вызывает +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} при каждом событии, +которое вызывается методом обратного вызова{@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Тем не менее, +при вызове{@link android.app.Dialog#dismiss Dialog.dismiss()} или {@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()}, +система вызывает {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} , а +не {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Поэтому в общих случаях +вызов{@link android.support.v4.app.DialogFragment#dismiss dismiss()} производится при нажатии пользователем +положительной кнопки в диалоговом окне, а после диалоговое окно закрывается.

+ + diff --git a/docs/html-intl/intl/ru/guide/topics/ui/menus.jd b/docs/html-intl/intl/ru/guide/topics/ui/menus.jd new file mode 100644 index 0000000000000000000000000000000000000000..2f3ce1eb95804630313b6f6d576a3c9a24226190 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=Меню +parent.title=Пользовательский интерфейс +parent.link=index.html +@jd:body + + + +

Меню являются стандартным компонентом пользовательского интерфейса в приложениях многих типов. Для обеспечения привычной +и единообразной технологии работы с приложением следует представлять действия пользователя и другие варианты выбора в своих операциях +с помощью API-интерфейсов класса {@link android.view.Menu}.

+ +

Начиная с версии Android 3.0 (уровень API 11) в устройствах, работающих под управлением Android, +наличие отдельной кнопки Меню больше не требуется. С учетом этого изменения приложения для Android должны перестать +зависеть от традиционной панели меню из 6 пунктов. Вместо нее в них должна быть строка действий с часто используемыми +действиями пользователя.

+ +

Несмотря на то что оформление и поведение некоторых пунктов меню изменились, семантика для определения +набора действий и вариантов по-прежнему основана на API-интерфейсах класса {@link android.view.Menu}. В этом +руководстве рассказывается, как создавать три основополагающих типа меню или представлений действий в системе +Android всех версий:

+ +
+
Меню параметров и строка действий
+
Пункты меню параметров представляют собой основные варианты выбора действий в пределах +операции. Именно здесь следует размещать действия, которые затрагивают приложение в целом, например: +"Поиск", "Составить сообщение эл. почты" и "Настройки". +

При разработке приложений для версии Android 2.3 или более ранних версий пользователи могут +открыть панель меню параметров нажатием кнопки Меню.

+

В версии Android 3.0 и последующих версиях пункты меню параметров размещаются в строке действий в виде сочетания отображаемых на экране вариантов +действий и раскрывающегося списка дополнительных вариантов выбора. Начиная с Android 3.0 кнопка Меню больше не используется (на некоторых +устройствах +ее нет), поэтому для предоставления доступа к действиям и другим вариантам выбора вам следует перейти к использованию +строки действий.

+

См. раздел Создание меню параметров

+
+ +
Контекстное меню и режим контекстных действий
+ +
Контекстное меню ― это плавающее меню, которое открывается, когда +пользователь длительно нажимает на элемент. В нем содержатся действия, которые затрагивают выбранный контент или +контекстный кадр. +

При разработке приложения для версии Android 3.0 или выше вместо этого для обеспечения действий с выбранным контентом следует использовать режим контекстных действий. В этом режиме +в строке, расположенной вверху экрана, отображаются пункты действий, затрагивающие выбранный контент, причем пользователь может +выбрать сразу несколько элементов.

+

См. раздел Создание контекстного меню

+
+ +
Всплывающее меню
+
Во всплывающем меню отображается вертикальный список пунктов, который привязан к представлению, +вызвавшему меню. Он хорошо подходит для предоставления возможности дополнительных вариантов действий, относящихся к определенному контенту или +для выдачи вариантов для второй части команды. Действия во всплывающем меню +не должны напрямую затрагивать соответствующий контент — для этого предназначены контекстные +действия. Всплывающее меню предназначено для расширенных действий, относящихся к областям контента в вашей +операции. +

См. раздел Создание всплывающего меню

+
+
+ + + +

Определение меню в файле XML

+ +

Для определения пунктов меню всех типов в Android используется стандартный формат XML. +Вместо того чтобы создавать меню в коде своей операции, определять меню и все его пункты следует в + ресурсе меню формата XML. После этого +ресурс меню можно будет загружать как объект {@link android.view.Menu} в свои операции или +фрагменты.

+ +

Использовать ресурсы меню рекомендуется по нескольким причинам:

+
    +
  • в XML проще визуализировать структуру меню;
  • +
  • это позволяет отделить контент для меню от кода, определяющего работу приложения;
  • +
  • это позволяет создавать альтернативные варианты меню для разных версий платформы, +размеров экрана и других конфигураций путем использования структуры ресурсов приложения.
  • +
+ +

Чтобы определить меню, создайте файл XML в папке res/menu/ +вашего проекта и постройте меню со следующими элементами:

+
+
<menu>
+
Определяет класс {@link android.view.Menu}, который является контейнером для пунктов меню. Элемент +<menu> должен быть корневым узлом файла, в котором может находиться один или несколько элементов +<item> и <group>.
+ +
<item>
+
Создает класс {@link android.view.MenuItem}, который представляет один пункт меню. Этот +элемент может содержать вложенный элемент <menu> для создания вложенных меню.
+ +
<group>
+
Необязательный, невидимый контейнер для элементов {@code <item>}. Он позволяет +разделять пункты меню на категории и назначать им одинаковые свойства, такие как активное состояние и видимость. Подробные +сведения изложены в разделе Создание групп меню.
+
+ + +

Вот пример меню с именем game_menu.xml:

+
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/new_game"
+          android:icon="@drawable/ic_new_game"
+          android:title="@string/new_game"
+          android:showAsAction="ifRoom"/>
+    <item android:id="@+id/help"
+          android:icon="@drawable/ic_help"
+          android:title="@string/help" />
+</menu>
+
+ +

Элемент <item> поддерживает несколько атрибутов, с помощью которых можно определить внешний вид и +поведение пункта меню. Пункты приведенного выше меню имеют следующие атрибуты:

+ +
+
{@code android:id}
+
Идентификатор ресурса, который является уникальным для этого пункта, что позволяет приложению распознавать пункт, +когда его выбирает пользователь.
+
{@code android:icon}
+
Ссылка на графический элемент, который будет использоваться в качестве значка пункта меню.
+
{@code android:title}
+
Ссылка на строку, которая будет использоваться в качестве названия пункта меню.
+
{@code android:showAsAction}
+
Указывает, когда и как этот пункт должен отображаться в строке действий.
+
+ +

Это самые важные атрибуты, которые следует использовать, но есть также множество других атрибутов. +Сведения обо всех поддерживаемых атрибутах см. в документе Ресурс меню.

+ +

К пункту любого меню (кроме вложенного меню) можно прикрепить вложенное меню, добавив элемент {@code <menu>} +в качестве дочернего элемента {@code <item>}. Вложенные меню полезны, когда в приложении имеется множество +функций, которые можно разделить на категории подобно строке меню приложения для ПК ("Файл", +"Правка", "Вид" и т. д.). Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/file"
+          android:title="@string/file" >
+        <!-- "file" submenu -->
+        <menu>
+            <item android:id="@+id/create_new"
+                  android:title="@string/create_new" />
+            <item android:id="@+id/open"
+                  android:title="@string/open" />
+        </menu>
+    </item>
+</menu>
+
+ +

Для использования меню в операции необходимо загрузить ресурс меню (преобразовать ресурс XML +в программируемый объект) с помощью метода {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()}. В приведенных далее разделах рассказывается, как загружать меню +каждого типа.

+ + + +

Создание меню параметров

+ +
+ +

Рисунок 1. Меню параметров в +браузере на Android 2.3.

+
+ +

В меню параметров следует размещать действия и другие варианты выбора, которые имеют отношение к +контексту текущей операции, например: "Поиск", "Составить сообщение эл. почты" и "Настройки".

+ +

Место, где отображаются на экране пункты вашего меню параметров, определяется версией платформы, для которой +разработано приложение.

+ +
    +
  • Если приложение написано для версии Android 2.3.x (уровень API 10) или +более ранней, содержимое вашего меню параметров отображается внизу экрана, когда пользователь +нажимает кнопку Меню, как показано на рисунке 1. Когда меню открывается, первой видимой частью является меню +значков, +в котором имеется шесть пунктов. Если в вашем меню больше шести пунктов, система Android разместит +шестой и остальные пункты в дополнительном меню, которое пользователь может открыть, выбрав вариант + Еще.
  • + +
  • Если приложение предназначено для версии Android 3.0 (уровень API 11) и +более поздних, пункты меню параметров будут отображаться в строке действий. По умолчанию система +размещает все действия на панели дополнительных вариантов, которые пользователь может открыть с помощью значка дополнительных действий, расположенного +с правой стороны строки действий (либо нажатием кнопки Меню, если на устройстве есть такая кнопка). Чтобы +обеспечить +быстрый доступ к важным действиям, можно принудительно разместить несколько пунктов меню в строке действий, добавив +{@code android:showAsAction="ifRoom"} к соответствующим элементам {@code <item>} (см. рисунок +2).

    Подробные сведения о пунктах действий и других особенностях строки действий см. в руководстве Строка действий.

    +

    Примечание. Даже если ваше приложение не предназначено для работы в версии Android 3.0 или +более поздней, можно создать собственный макет со строкой действий, чтобы реализовать похожий эффект. Пример того, как можно +поддерживать работу строки действий в старых версиях Android см. в образце кода, иллюстрирующем + Совместимость строки действий.

    +
  • +
+ + +

Рисунок 2. Строка действий из приложения Honeycomb Gallery, содержащая +вкладки навигации и пункт включения камеры (а также кнопку открытия дополнительной панели действий).

+ +

Объявлять пункты меню параметров можно либо из подкласса {@link android.app.Activity}, +либо из подкласса {@link android.app.Fragment}. Если и ваша операция, и фрагменты +объявляют пункты меню параметров, в пользовательском интерфейсе они объединяются. Сначала отображаются пункты +операции, а за ними следуют пункты фрагментов в том порядке, в котором каждый фрагмент добавляется в +операцию. При необходимости можно изменить порядок следования пунктов меню с помощью атрибута {@code android:orderInCategory}, +указываемого в каждом {@code <item>}, который требуется переместить.

+ +

Чтобы указать меню параметров для операции, переопределите {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} (фрагменты предоставляют собственный обратный +вызов {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}). В этом +методе можно загрузить собственный ресурс меню (определенный в XML) в класс {@link +android.view.Menu}, имеющийся в обратном вызове. Например:

+ +
+@Override
+public boolean onCreateOptionsMenu(Menu menu) {
+    MenuInflater inflater = {@link android.app.Activity#getMenuInflater()};
+    inflater.inflate(R.menu.game_menu, menu);
+    return true;
+}
+
+ +

Пункты меню также можно добавлять с помощью {@link android.view.Menu#add(int,int,int,int) +add()}, а получать их с помощью {@link android.view.Menu#findItem findItem()} для пересмотра их +свойств с помощью API-интерфейсов {@link android.view.MenuItem}.

+ +

Если ваше приложение предназначено для версии Android 2.3.x или более ранней, система вызывает метод {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} для создания меню параметров, +когда пользователь открывает это меню впервые. Если приложение предназначено для версии Android 3.0 и или более поздней, система +вызывает метод{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} при +запуске операции, чтобы отобразить пункты в строке действий.

+ + + +

Обработка нажатий

+ +

Когда пользователь выбирает пункт меню параметров (в том числе пункты действий из строки действий), +система вызывает метод {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} вашей операции. Этот метод передает выбранный класс {@link android.view.MenuItem}. Идентифицировать +пункт меню можно, вызвав метод {@link android.view.MenuItem#getItemId()}, который возвращает уникальный +идентификатор пункта меню (определенный атрибутом {@code android:id} из ресурса меню или +целым числом, переданным методу {@link android.view.Menu#add(int,int,int,int) add()}). Этот идентификатор +можно сопоставить с известными пунктами меню, чтобы выполнить соответствующее действие. Например:

+ +
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    // Handle item selection
+    switch (item.getItemId()) {
+        case R.id.new_game:
+            newGame();
+            return true;
+        case R.id.help:
+            showHelp();
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+    }
+}
+
+ +

Когда пункт меню успешно обработан, возвращается {@code true}. Если пункт меню не +обрабатывается, следует вызвать реализацию суперкласса {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} (реализация +по умолчанию возвращает значение false).

+ +

Если в вашей операции имеются фрагменты, система сначала вызовет метод {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} для операции, а затем +будет вызывать этот метод для каждого фрагмента (в том порядке, в котором они были добавлены), пока он не возвратит +значение{@code true} или не закончатся фрагменты.

+ +

Совет. В Android 3.0 появилась возможность определять в XML поведение +при нажатии для пунктов меню с помощью атрибута {@code android:onClick}. Значением этого +атрибута должно быть имя метода, определенное операцией с помощью меню. Этот метод +должен быть общедоступным и принимать один параметр {@link android.view.MenuItem}, — когда система +вызывает этот метод, она передает ему выбранный пункт меню. Подробные сведения и пример см. в документе Ресурс меню.

+ +

Совет. Если в приложении предусмотрено несколько операций и +в некоторых из них имеются одинаковые меню параметров, рассмотрите возможность создания +операции, которая будет использовать исключительно методы {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} и {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()}. Затем распространите этот класс на все операции, у которых должно быть +одинаковое меню параметров. Таким образом можно управлять одним набором кода для обработки действий +меню, а каждый класс-потомок при этом будет наследовать режимы работы меню. +Если требуется добавить пункты меню в одну из операций-потомков, +переопределите метод {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} в этой операции. Вызовите метод {@code super.onCreateOptionsMenu(menu)}, с тем чтобы +создать первоначальные пункты меню, а затем добавьте новые пункты меню с помощью метода {@link +android.view.Menu#add(int,int,int,int) menu.add()}. Также можно переопределять режимы работы +суперкласса для отдельных пунктов меню.

+ + +

Изменение пунктов меню во время выполнения

+ +

После того как система вызовет метод {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}, она сохранит заполненный вами экземпляр {@link android.view.Menu} и +будет вызывать метод {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} +,только если меню по каким-то причинам станет некорректным. Однако метод {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} следует использовать только для создания начального +состояния меню, а не для внесения в него изменений в течение жизненного цикла операции.

+ +

Если вам требуется изменять меню параметров в зависимости от +событий, которые возникают в течение жизненного цикла операции, сделать это можно в +методе{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}. Этот +метод передает объект {@link android.view.Menu} в том виде, в котором он в данный момент существует. Его-то и можно изменить +путем, например, добавления, удаления или отключения пунктов меню. (Фрагменты также предоставляют обратный вызов {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()}.)

+ +

В версии Android 2.3.x или более ранней система вызывает метод {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()} каждый раз, когда пользователь открывает меню параметров (нажимает кнопку Меню +).

+ +

В версии Android 3.0 и последующих версиях считается, что меню параметров всегда открыто, когда пункты меню +приведены в строке действий. Когда возникает событие и требуется обновить меню, следует +вызвать метод {@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()}, чтобы запросить у +системы вызов метода {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}.

+ +

Примечание. +Никогда не следует изменять пункты меню параметров с учетом класса {@link android.view.View}, действующего +в данный момент. В сенсорном режиме (когда пользователь не использует трекбол или кнопки направления движения) фокус +не может переводиться на представления, поэтому никогда не следует использовать фокус в качестве основы для изменения +пунктов меню параметров. Если вы желаете предоставить пункты меню, которые зависят от контекста {@link +android.view.View}, используйте контекстное меню.

+ + + + +

Создание контекстного меню

+ +
+ +

Рисунок 3. Снимки экрана с плавающим контекстным меню (слева) +и строкой контекстных действий (справа).

+
+ +

В контекстном меню содержатся действия, которые затрагивают определенный элемент или контекстный кадр в пользовательском интерфейсе. Контекстное +меню можно создать для любого представления, но чаще всего они используются для элементов из {@link +android.widget.ListView}, {@link android.widget.GridView} или других групп представлений, в которых +пользователь может выполнять действия непосредственно с каждым элементом.

+ +

Существует два способа предоставления возможности контекстных действий:

+
    +
  • В плавающем контекстном меню. Меню отображается в виде +плавающего списка пунктов меню (наподобие диалогового окна), когда пользователь длительно нажимает на экран (нажимает и +удерживает нажатым) в представлении, которое объявляет поддержку контекстного меню. Пользователи могут каждый раз выполнять контекстное +действие только с одним элементом.
  • + +
  • В режиме контекстных действий. Этот режим является системной реализацией +{@link android.view.ActionMode}, которая отображает строку контекстных действий вверху +экрана с пунктами действий, которые затрагивают выбранные элементы. Когда этот режим активен, пользователи +могут одновременно выполнять действие с несколькими элементами (если это допускается приложением).
  • +
+ +

Примечание. Режим контекстных действий поддерживается в версии Android 3.0 (уровень API +11) и последующих версиях. Если этот режим предусмотрен, именно его рекомендуется использовать для отображения контекстных +действий. Если ваше приложение поддерживает версии ниже 3.0, то для этих устройств следует вернуться к плавающему +контекстному меню.

+ + +

Создание плавающего контекстного меню

+ +

Программирование плавающего контекстного меню

+
    +
  1. Зарегистрируйте класс {@link android.view.View}, с которым следует связать контекстное меню, +вызвав метод {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} и передав +ему {@link android.view.View}. +

    Если операция использует {@link android.widget.ListView} или {@link android.widget.GridView} и +требуется, чтобы каждый элемент предоставлял одинаковое контекстное меню, зарегистрируйте все элементы для контекстного меню, +передав {@link android.widget.ListView} или {@link android.widget.GridView} методу {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()}.

    +
  2. + +
  3. Реализуйте метод {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} в + {@link android.app.Activity} или {@link android.app.Fragment}. +

    Когда зарегистрированное представление примет событие длительного нажатия, система вызовет ваш метод {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +. Именно здесь определяются пункты меню. Делается это обычно путем загрузки ресурса меню. Например: +

    +
    +@Override
    +public void onCreateContextMenu(ContextMenu menu, View v,
    +                                ContextMenuInfo menuInfo) {
    +    super.onCreateContextMenu(menu, v, menuInfo);
    +    MenuInflater inflater = getMenuInflater();
    +    inflater.inflate(R.menu.context_menu, menu);
    +}
    +
    + +

    {@link android.view.MenuInflater} позволяет загружать контекстное меню из ресурса меню. В число параметров метода +обратного вызова входят {@link android.view.View}, +выбранный пользователем, и объект{@link android.view.ContextMenu.ContextMenuInfo}, который предоставляет +дополнительную информацию о выбранном элементе. Если в вашей операции есть несколько представлений и все они предоставляют +разные контекстные меню, то с помощью этих параметров можно определять, какое контекстное меню +загружать.

    +
  4. + +
  5. Реализуйте метод {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}. +

    Когда пользователь выбирает пункт меню, система вызывает этот метод, с тем чтобы вы могли выполнить +соответствующее действие. Например:

    + +
    +@Override
    +public boolean onContextItemSelected(MenuItem item) {
    +    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
    +    switch (item.getItemId()) {
    +        case R.id.edit:
    +            editNote(info.id);
    +            return true;
    +        case R.id.delete:
    +            deleteNote(info.id);
    +            return true;
    +        default:
    +            return super.onContextItemSelected(item);
    +    }
    +}
    +
    + +

    Метод {@link android.view.MenuItem#getItemId()} запрашивает идентификатор для +выбранного пункта меню. Идентификаторы должны быть назначены каждому пункту меню в файле XML с помощью атрибута {@code +android:id}, как описано в разделе Определение меню в файле +XML.

    + +

    Когда пункт меню успешно обработан, возвращается {@code true}. Если пункт меню не +обрабатывается, следует передать его реализации суперкласса. Если в вашей операции есть фрагменты, +то она получит этот обратный вызов первой. Вызывая суперкласс, когда пункт меню не обрабатывается, система +передает событие соответствующему методу обратного вызова в каждом фрагменте по одному (в порядке +добавления каждого фрагмента), пока не будет возвращено значение {@code true} или {@code false}. (Реализация +{@link android.app.Activity} и {@code android.app.Fragment} по умолчанию возвращает {@code +false}, поэтому, когда событие не обрабатывается, следует всегда вызывать суперкласс.)

    +
  6. +
+ + +

Использование режима контекстных действий

+ +

Режим контекстных действий представляет собой системную реализацию класса {@link android.view.ActionMode}, которая +направляет пользователя на выполнение контекстных действий при взаимодействии с приложением. Когда +пользователь использует этот режим, выбирая элемент, вверху экрана открывается строка контекстных действий, +содержащая действия, которые пользователь может выполнить с выбранными в данный момент элементами. В этом режиме +пользователь может выбирать несколько элементов (если это допускается приложением), снимать выделения с элементов и продолжать +навигацию в операции (в тех пределах, в которых это поддерживается приложением). Режим контекстных действий отключается, +а строка контекстных действий исчезает, когда пользователь снимет выделение со всех элементов, нажмет кнопку НАЗАД +или выберет действие Готово, расположенное с левой стороны строки.

+ +

Примечание. Строка контекстных действий не обязательно бывает +связана со строкой действий. Они работают +независимо друг от друга, даже несмотря на то, что визуально строка контекстных действий занимает положение +строки действий.

+ +

Если вы разрабатываете приложение для версии Android 3.0 (уровень API 11) или последующих версий, то +обычно вместо плавающего контекстного меню вам следует использовать контекстные действия.

+ +

Для представлений, которые предоставляют возможность контекстные действия, обычно следует вызывать режим контекстных действий +при возникновении одного из двух (или сразу обоих) событий:

+
    +
  • пользователь длительно нажимает в представлении;
  • +
  • пользователь устанавливает флажок или выбирает другой подобный компонент пользовательского интерфейса в представлении.
  • +
+ +

То, каким образом ваше представление вызывает режим контекстных действий и определяет поведение каждого +действия, зависит от вас. Есть два базовых варианта:

+
    +
  • для контекстных действий в отдельных, произвольных представлениях;
  • +
  • для пакетных контекстных действий с группами элементов в {@link +android.widget.ListView} или {@link android.widget.GridView} (что позволяет пользователю выбирать по несколько +элементов и выполнять действие с ними всеми).
  • +
+ +

В следующих разделах описывается конфигурация, необходимая для каждого из этих вариантов.

+ + +

Включение режима контекстных действий для отдельных представлений

+ +

Если вам требуется вызывать режим контекстных действий, только когда пользователь выбирает определенные +представления, то вам следует:

+
    +
  1. Реализовать интерфейс {@link android.view.ActionMode.Callback}. В его методах обратного вызова вы +можете указать действия для строки контекстных действий, реагировать на нажатия пунктов действий и +обрабатывать другие события жизненного цикла для режима действий.
  2. +
  3. Вызывайте {@link android.app.Activity#startActionMode startActionMode()}, когда требуется показать +строку (например, когда пользователь выполняет длительное нажатие представления).
  4. +
+ +

Например:

+ +
    +
  1. Реализуйте интерфейс {@link android.view.ActionMode.Callback ActionMode.Callback}: +
    +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
    +
    +    // Called when the action mode is created; startActionMode() was called
    +    @Override
    +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    +        // Inflate a menu resource providing context menu items
    +        MenuInflater inflater = mode.getMenuInflater();
    +        inflater.inflate(R.menu.context_menu, menu);
    +        return true;
    +    }
    +
    +    // Called each time the action mode is shown. Always called after onCreateActionMode, but
    +    // may be called multiple times if the mode is invalidated.
    +    @Override
    +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    +        return false; // Return false if nothing is done
    +    }
    +
    +    // Called when the user selects a contextual menu item
    +    @Override
    +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    +        switch (item.getItemId()) {
    +            case R.id.menu_share:
    +                shareCurrentItem();
    +                mode.finish(); // Action picked, so close the CAB
    +                return true;
    +            default:
    +                return false;
    +        }
    +    }
    +
    +    // Called when the user exits the action mode
    +    @Override
    +    public void onDestroyActionMode(ActionMode mode) {
    +        mActionMode = null;
    +    }
    +};
    +
    + +

    Обратите внимание, что эти обратные вызовы событий почти точно такие же, как и обратные вызовы для меню параметров. Отличаются они только тем, что каждый из них также передает объект {@link +android.view.ActionMode}, связанный с событием. С помощью API-интерфейсов {@link +android.view.ActionMode} можно вносить различные изменения в CAB, например, указывать другой заголовок и +подзаголовок с помощью {@link android.view.ActionMode#setTitle setTitle()} и {@link +android.view.ActionMode#setSubtitle setSubtitle()} (удобно для указания количества выбранных +элементов).

    + +

    Также обратите внимание, что приведенный выше образец кода задает для переменной {@code mActionMode} значение null, когда +режим действия прекращает свое существование. Далее вы узнаете, каким образом он инициализируется и чем может быть +полезно сохранение составной переменной в операции или фрагменте.

    +
  2. + +
  3. Для включения режима контекстных действий, когда это необходимо, +например, в ответ на длительное нажатие {@link +android.view.View}, вызывайте {@link android.app.Activity#startActionMode startActionMode()}:

    + +
    +someView.setOnLongClickListener(new View.OnLongClickListener() {
    +    // Called when the user long-clicks on someView
    +    public boolean onLongClick(View view) {
    +        if (mActionMode != null) {
    +            return false;
    +        }
    +
    +        // Start the CAB using the ActionMode.Callback defined above
    +        mActionMode = getActivity().startActionMode(mActionModeCallback);
    +        view.setSelected(true);
    +        return true;
    +    }
    +});
    +
    + +

    При вызове метода {@link android.app.Activity#startActionMode startActionMode()} система возвращает +созданный класс {@link android.view.ActionMode}. Сохранив его в составной переменной, вы сможете +вносить изменения в строку контекстных действий в ответ на другие события. В приведенном выше образце кода +{@link android.view.ActionMode} используется для того, чтобы экземпляр {@link android.view.ActionMode} +не создавался повторно, если он уже активен. Достигается это путем проверки, имеет ли элемент значение null перед запуском +режима действий.

    +
  4. +
+ + + +

Включение пакетных контекстных действий в ListView или GridView

+ +

Если при наличии набора элементов в {@link android.widget.ListView} или {@link +android.widget.GridView} (либо другом расширении {@link android.widget.AbsListView}) требуется +разрешить пользователям выполнять пакетные действия, следует:

+ +
    +
  • реализовать интерфейс {@link android.widget.AbsListView.MultiChoiceModeListener} и задать его +для группы представлений с помощью метода {@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()}; в методах обратного вызова приемника событий вы можете указывать действия +для строки контекстных действий, реагировать на нажатия пунктов действий и обрабатывать другие обратные вызовы, +унаследованные от интерфейса {@link android.view.ActionMode.Callback};
  • + +
  • вызвать метод {@link android.widget.AbsListView#setChoiceMode setChoiceMode()} с аргументом {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
  • +
+ +

Например:

+ +
+ListView listView = getListView();
+listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+    @Override
+    public void onItemCheckedStateChanged(ActionMode mode, int position,
+                                          long id, boolean checked) {
+        // Here you can do something when items are selected/de-selected,
+        // such as update the title in the CAB
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        // Respond to clicks on the actions in the CAB
+        switch (item.getItemId()) {
+            case R.id.menu_delete:
+                deleteSelectedItems();
+                mode.finish(); // Action picked, so close the CAB
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        // Inflate the menu for the CAB
+        MenuInflater inflater = mode.getMenuInflater();
+        inflater.inflate(R.menu.context, menu);
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+        // Here you can make any necessary updates to the activity when
+        // the CAB is removed. By default, selected items are deselected/unchecked.
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        // Here you can perform updates to the CAB due to
+        // an {@link android.view.ActionMode#invalidate} request
+        return false;
+    }
+});
+
+ +

Готово. Теперь, когда пользователь выберет элемент с помощью длительного нажатия, система вызовет метод {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +и отобразит строку контекстных действий с указанными действиями. Пока строка +контекстных действий отображается, пользователи могут выбирать дополнительные элементы.

+ +

В некоторых случаях, когда контекстные действия содержат общие пункты, можно +добавить флажок или другой подобный элемент пользовательского интерфейса, с помощью которого пользователи могут выбирать пункты, поскольку они +могут не обнаружить варианта с длительным нажатием. Когда пользователь устанавливает флажок, можно +вызывать режим контекстных действий, переводя соответствующий элемент списка в выбранное +состояние с помощью {@link android.widget.AbsListView#setItemChecked setItemChecked()}.

+ + + + +

Создание всплывающего меню

+ +
+ +

Рисунок 4. Всплывающее меню в приложении Gmail, привязанное к расположенной вверху справа кнопке открытия панели +дополнительных пунктов.

+
+ +

{@link android.widget.PopupMenu} является модальным меню, привязанным к {@link android.view.View}. +Оно отображается ниже представления, к которому привязано, если там есть место, либо поверх него. Варианты использования:

+
    +
  • Предоставление меню с дополнительными пунктами для действий, которые относятся к определенному контенту (например, +к заголовкам сообщений Gmail, показанным на рисунке 4). +

    Примечание. Оно отличается от контекстного меню, которое +обычно используется для действий, затрагивающих выбранный контент. Для действий, которые затрагивают выбранный +контент, используйте режим контекстных действий или плавающее контекстное меню.

  • +
  • Предоставление второй части командной последовательности (например, кнопки, обозначенной как "Добавить", +которая открывает всплывающее меню с различными вариантами добавления);
  • +
  • Предоставление раскрывающегося меню наподобие {@link android.widget.Spinner}, которое не сохраняет +постоянное выделение.
  • +
+ + +

Примечание. Использование класса {@link android.widget.PopupMenu} поддерживается на уровне API + 11 и выше.

+ +

Если для определения меню используется XML, вот каким образом можно показать всплывающее меню:

+
    +
  1. Создайте экземпляр класса {@link android.widget.PopupMenu} с помощью его конструктора, принимающий +текущие {@link android.content.Context} и {@link android.view.View} приложения, к которым +должно быть привязано меню.
  2. +
  3. С помощью {@link android.view.MenuInflater} загрузите свой ресурс меню в объект {@link +android.view.Menu}, возвращенный методом {@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()}. На API уровня 14 и выше вместо этого можно использовать +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()}.
  4. +
  5. Вызовите метод {@link android.widget.PopupMenu#show() PopupMenu.show()}.
  6. +
+ +

Например, вот кнопка с атрибутом {@link android.R.attr#onClick android:onClick}, +которая показывает всплывающее меню:

+ +
+<ImageButton
+    android:layout_width="wrap_content" 
+    android:layout_height="wrap_content" 
+    android:src="@drawable/ic_overflow_holo_dark"
+    android:contentDescription="@string/descr_overflow_button"
+    android:onClick="showPopup" />
+
+ +

После этого операция сможет показать вот такое всплывающее меню:

+ +
+public void showPopup(View v) {
+    PopupMenu popup = new PopupMenu(this, v);
+    MenuInflater inflater = popup.getMenuInflater();
+    inflater.inflate(R.menu.actions, popup.getMenu());
+    popup.show();
+}
+
+ +

На API уровня 14 и выше можно объединить две строки, которые загружают меню, с помощью {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()}.

+ +

Меню закрывается, когда пользователь выбирает один из пунктов или касается экрана за пределами области +меню. Прослушивать событие закрытия меню можно с помощью {@link +android.widget.PopupMenu.OnDismissListener}.

+ +

Обработка нажатий

+ +

Для выполнения +действия, когда пользователь выбирает пункт меню, необходимо реализовать интерфейс {@link +android.widget.PopupMenu.OnMenuItemClickListener} и зарегистрировать его в своем {@link +android.widget.PopupMenu}, вызвав метод {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()}. Когда пользователь выбирает пункт меню, система выполняет обратный вызов {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} в +вашем интерфейсе.

+ +

Например:

+ +
+public void showMenu(View v) {
+    PopupMenu popup = new PopupMenu(this, v);
+
+    // This activity implements OnMenuItemClickListener
+    popup.setOnMenuItemClickListener(this);
+    popup.inflate(R.menu.actions);
+    popup.show();
+}
+
+@Override
+public boolean onMenuItemClick(MenuItem item) {
+    switch (item.getItemId()) {
+        case R.id.archive:
+            archive(item);
+            return true;
+        case R.id.delete:
+            delete(item);
+            return true;
+        default:
+            return false;
+    }
+}
+
+ + +

Создание групп меню

+ +

Группа меню ― это набор пунктов меню с рядом общих характеристик. С помощью группы +можно:

+
    +
  • показывать или скрывать все пункты с помощью {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()};
  • +
  • включать или отключать все пункты с помощью {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()};
  • +
  • Указывать, можно ли помечать все пункты, с помощью {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}.
  • +
+ +

Для создания группы необходимо вложить элементы {@code <item>} в элемент {@code <group>} +в своем ресурсе меню либо указать идентификатор группы с помощью метода {@link +android.view.Menu#add(int,int,int,int) add()}.

+ +

Вот пример ресурса меню, в котором имеется группа:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/menu_save"
+          android:icon="@drawable/menu_save"
+          android:title="@string/menu_save" />
+    <!-- menu group -->
+    <group android:id="@+id/group_delete">
+        <item android:id="@+id/menu_archive"
+              android:title="@string/menu_archive" />
+        <item android:id="@+id/menu_delete"
+              android:title="@string/menu_delete" />
+    </group>
+</menu>
+
+ +

Элементы, находящиеся в группе, отображаются на одном уровне с первым элементом — все три пункта +меню являются элементами одного уровня. Однако можно изменить характеристики двух +пунктов из группы, указав ссылку на идентификатор группы и воспользовавшись приведенными выше методами. Кроме того, +система никогда не будет разделять сгруппированные пункты. Например, если объявить {@code +android:showAsAction="ifRoom"} для каждого пункта, то они оба будут отображены либо в строке +действий, либо в дополнительных действиях.

+ + +

Использование пунктов меню, которые можно пометить

+ +
+ +

Рисунок 5. Снимок экрана с вложенным меню, пункты которого можно +пометить.

+
+ +

Такое меню можно использовать в качестве интерфейса для включения и отключения тех или иных параметров. При этом для автономных +параметров используется флажок, а для групп +взаимоисключающих вариантов ― переключатель. На рисунке 5 показано вложенное меню с пунктами, которые можно выбирать с помощью +переключателей.

+ +

Примечание. Пункты в меню значков (из меню параметров) не могут +отображать флажки или переключатели. Если вы решите сделать так, чтобы пункты меню значков можно было помечать, +вам необходимо будет вручную указать помеченное состояние путем замены значка и/или теста +при каждом изменении состояния.

+ +

Определять возможность помечать отдельные пункты меню можно с помощью атрибута {@code +android:checkable} в элементе {@code <item>}, а для всей группы это делается с помощью +атрибута {@code android:checkableBehavior} в элементе {@code <group>}. Например +, все пункты этой группы меню можно помечать с помощью переключателей:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <group android:checkableBehavior="single">
+        <item android:id="@+id/red"
+              android:title="@string/red" />
+        <item android:id="@+id/blue"
+              android:title="@string/blue" />
+    </group>
+</menu>
+
+ +

Атрибут {@code android:checkableBehavior} принимает один из трех параметров: +

+
{@code single}
+
Только один пункт из группы можно пометить (переключатель)
+
{@code all}
+
Все пункты можно пометить (флажки)
+
{@code none}
+
Пометить нельзя ни один пункт
+
+ +

Для того чтобы применить к пункту помеченное состояние по умолчанию, служит атрибут {@code android:checked} в +элементе {@code <item>}, а изменить его в коде можно с помощью метода {@link +android.view.MenuItem#setChecked(boolean) setChecked()}.

+ +

Когда выбирается пункт, который может быть помечен, система вызывает соответствующий ему метод обратного вызова +(например {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). Именно +здесь необходимо задать состояние флажка, поскольку флажок или переключатель не +изменяет свое состояние автоматически. Запросить текущее состояние пункта (в котором он находился до того, как был +выбран пользователем) можно с помощью{@link android.view.MenuItem#isChecked()}, а затем задать помеченное состояние с помощью +{@link android.view.MenuItem#setChecked(boolean) setChecked()}. Например:

+ +
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+        case R.id.vibrate:
+        case R.id.dont_vibrate:
+            if (item.isChecked()) item.setChecked(false);
+            else item.setChecked(true);
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+    }
+}
+
+ +

Если помеченное состояние не установить этим способом, то, когда пользователь выберет пункт, его отображаемое состояние (флажок или +переключатель) не +изменится. Если же помеченное состояние установить, операция сохранит его +для пункта, с тем чтобы, когда пользователь откроет это меню, он увидел, +что галочка поставлена.

+ +

Примечание. +Пункты меню, которые можно пометить галочкой, предназначены для использования только в рамках одного сеанса. Они не сохраняются после +прекращения существования приложения. Если имеются настройки приложения, которые требуется сохранить для пользователя, +делать это следует с помощью общих настроек.

+ + + +

Добавление пунктов меню на основе объектов Intent

+ +

Иногда требуется, чтобы пункт меню запускал операцию с помощью объекта {@link android.content.Intent} +(это может быть операция как из вашего, так и из другого приложения). Когда вам известен объект Intent, который +требуется использовать, и у вас есть определенный пункт меню, который должен инициировать этот объект Intent, можно выполнить объект +Intent с помощью {@link android.app.Activity#startActivity(Intent) startActivity()} во время +выполнения соответствующего метода обратного вызова, запускаемого при выборе пункта меню (например, обратного вызова {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}).

+ +

Однако если вы не уверены, что на устройстве пользователя +есть приложение, которое может обработать этот объект Intent, добавление пункта меню, который его вызывает, может привести +к тому, что он не будет работать, поскольку объект Intent может не быть передан в +операцию. Чтобы решить эту проблему, Android позволяет динамически добавлять в меню пункты, +когда система Android обнаруживает на устройстве операции, которые могут обработать ваш объект Intent.

+ +

Добавление пунктов меню на основе имеющихся операций, которые принимают объект Intent:

+
    +
  1. Определите объект +с категорией {@link android.content.Intent#CATEGORY_ALTERNATIVE} и/или +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, а также с любыми другими условиями.
  2. +
  3. Вызовите метод {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}. Затем Android выполнит поиск приложений, которые могут выполнить этот объект Intent, +и добавит их в ваше меню.
  4. +
+ +

При отсутствии установленных приложений, +которые удовлетворяют требованиям объекта Intent, ни одного пункта меню добавлено не будет.

+ +

Примечание. +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} используется для обработки элемента, выбранного +в данный момент на экране. Поэтому его следует использовать только при создании меню в {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()}.

+ +

Например:

+ +
+@Override
+public boolean onCreateOptionsMenu(Menu menu){
+    super.onCreateOptionsMenu(menu);
+
+    // Create an Intent that describes the requirements to fulfill, to be included
+    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
+    Intent intent = new Intent(null, dataUri);
+    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
+
+    // Search and populate the menu with acceptable offering applications.
+    menu.addIntentOptions(
+         R.id.intent_group,  // Menu group to which new items will be added
+         0,      // Unique item ID (none)
+         0,      // Order for the items (none)
+         this.getComponentName(),   // The current activity name
+         null,   // Specific items to place first (none)
+         intent, // Intent created above that describes our requirements
+         0,      // Additional flags to control items (none)
+         null);  // Array of MenuItems that correlate to specific items (none)
+
+    return true;
+}
+ +

Каждая обнаруженная операция, в которой имеется фильтр Intent, соответствующий данному объекту Intent, добавляется в виде +пункта меню. Для этого значение из элемента android:label фильтра Intent используется в качестве +заголовка пункта меню, а значок приложения ― в качестве значка этого пункта меню. Метод +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} возвращает количество добавленных пунктов меню.

+ +

Примечание. При вызове метода {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} он переопределяет все пункты меню по группе меню, указанной в первом +аргументе.

+ + +

Предоставление возможности добавить свою операцию в другие меню

+ +

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

+ +

Чтобы операция могла быть включена в меню других приложений, необходимо определить фильтр +Intent обычным образом, но непременно включить в него значения {@link android.content.Intent#CATEGORY_ALTERNATIVE} +и/или {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} для категории фильтра +Intent. Например:

+
+<intent-filter label="@string/resize_image">
+    ...
+    <category android:name="android.intent.category.ALTERNATIVE" />
+    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
+    ...
+</intent-filter>
+
+ +

Подробные сведения о написании фильтров Intent см. в документе +Объекты Intent и фильтры объектов Intent.

+ +

Образец приложения, в котором используется эта методика, см. в образце кода +Note +Pad.

diff --git a/docs/html-intl/intl/ru/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/ru/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 0000000000000000000000000000000000000000..d072b77eae24106212d9eb8aa0baabbc700f9d6d --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=Уведомления +@jd:body + +
+
+

Содержание документа

+
    +
  1. Рекомендации по разработке
  2. +
  3. Создание уведомления +
      +
    1. Обязательное содержимое уведомления
    2. +
    3. Необязательные содержимое и настройки уведомления
    4. +
    5. Действия уведомлений
    6. +
    7. Приоритет уведомлений
    8. +
    9. Создание простого уведомления
    10. +
    11. Применение расширенного макета к уведомлению
    12. +
    13. Вопросы совместимости
    14. +
    +
  4. +
  5. Управление уведомлениями +
      +
    1. Обновление уведомлений
    2. +
    3. Удаление уведомлений
    4. +
    +
  6. +
  7. Сохранение навигации при запуске операции +
      +
    1. Настройка PendingIntent обычной операции
    2. +
    3. Настройка PendingIntent особой операции
    4. +
    +
  8. +
  9. Отображение хода выполнения в уведомлении +
      +
    1. Отображение индикатора хода выполнения фиксированной продолжительности
    2. +
    3. Отображение непрерывного индикатора операции
    4. +
    +
  10. +
  11. Метаданные уведомления
  12. +
  13. Уведомления heads-up
  14. +
  15. Уведомления на экране блокировки
  16. +
      +
    1. Настройка видимости
    2. +
    3. Управление воспроизведением мультимедиа на экране блокировки
    4. +
    +
  17. Нестандартные макеты уведомлений
  18. +
+ +

Основные классы

+
    +
  1. {@link android.app.NotificationManager}
  2. +
  3. {@link android.support.v4.app.NotificationCompat}
  4. +
+

Видеоролики

+
    +
  1. + + Уведомления в 4.1 +
  2. +
+

См. также

+
    +
  1. + Дизайн Android: уведомления +
  2. +
+
+
+

+ Уведомление ― это сообщение, которое может быть выведено на экран за пределами обычного пользовательского + интерфейса приложения. Когда вы сообщаете системе о необходимости выдать уведомление, оно сначала отображается в виде значка в + области уведомлений. Чтобы просмотреть подробные сведения об уведомлении, пользователь открывает + панель уведомлений. И областью уведомлений, и панелью уведомлений + управляет система, а пользователь может их просматривать в любое время. +

+ +

+ Рисунок 1. Уведомления в области уведомлений. +

+ +

+ Рисунок 2. Уведомления в панели уведомлений. +

+ +

Примечание. Если не указано иное, в этом руководстве речь идет о классе +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +в версии 4 вспомогательной библиотеки. +Класс{@link android.app.Notification.Builder Notification.Builder} был добавлен в Android +3.0 (уровень API 11).

+ +

Рекомендации по разработке

+ +

Поскольку уведомления являются как важной составной частью пользовательского интерфейса Android, для них имеются собственные инструкции по проектированию. +Появившиеся в Android 5.0 (уровень API 21) значительные изменения дизайна имеют особо важное +значение, поэтому для получения более подробной информации вам следует ознакомиться с учебником по интерфейсу Material Design +. Чтобы узнать, как проектировать уведомления и взаимодействие с ними, прочитайте руководство по проектированию +Уведомления.

+ +

Создание уведомления

+ +

Информация о пользовательском интерфейсе и действия для уведомления указываются в объекте +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. +Чтобы создать само уведомление, вызывается метод +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()}, +который возвращает объект {@link android.app.Notification}, содержащий заданные вами спецификации. Чтобы выдать +уведомление, объект {@link android.app.Notification} передается в систему путем вызова метода +{@link android.app.NotificationManager#notify NotificationManager.notify()}.

+ +

Обязательное содержимое уведомления

+

+ Объект {@link android.app.Notification} должен содержать следующие элементы: +

+
    +
  • + небольшой значок, заданный с помощью + {@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()}; +
  • +
  • + заголовок, заданный с помощью + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()}; +
  • +
  • + подробный текст, заданный с помощью + {@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()}. +
  • +
+

Необязательные содержимое и настройки уведомления

+

+ Все прочие настройки и содержимое уведомления являются необязательными. Подробные сведения о них + см. в справочной документации по {@link android.support.v4.app.NotificationCompat.Builder}. +

+ +

Действия уведомлений

+

+ Несмотря на то что действия не являются обязательными, в уведомление необходимо добавить хотя бы одно из них. + Действие позволяет пользователю перейти из уведомления прямо к + операции {@link android.app.Activity} из вашего приложения, где они могут просмотреть одно или несколько событий + либо выполнить какую-либо другую работу. +

+

+ Уведомление может предоставлять возможность выполгить несколько действий. Следует всегда определять действие, которое + вызывается, когда пользователь нажимает уведомление; обычно это действие открывает + операцию {@link android.app.Activity} из вашего приложения. В уведомление также можно добавлять кнопки, + которые выполняют дополнительные действия, например отключение сигнала будильника или немедленный ответ на текстовое + сообщение; эта функция поддерживается начиная с версии Android 4.1. Если используются дополнительные кнопки действий, то также + необходимо сделать их функции доступными в операции {@link android.app.Activity} из вашего приложения (подробные + сведения см. в разделе Вопросы совместимости). +

+

+ Внутри класса {@link android.app.Notification} само действие определяется + объектом {@link android.app.PendingIntent}, содержащим объект + {@link android.content.Intent}, который запускает + операцию {@link android.app.Activity} из вашего приложения. Чтобы связать объект + {@link android.app.PendingIntent} с жестом, вызовите соответствующий метод + {@link android.support.v4.app.NotificationCompat.Builder}. Например, если вам требуется запустить + операцию {@link android.app.Activity}, когда пользователь нажимает текст уведомления в + панели уведомлений, вы добавляете объект {@link android.app.PendingIntent} путем вызова метода + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}. +

+

+ Запуск операции {@link android.app.Activity}, когда пользователь нажимает уведомление, является + наиболее распространенным вариантом действия. Операцию {@link android.app.Activity} также можно запускать, когда пользователь + закрывает уведомление. В версии Android 4.1 или более поздних версиях запускать + операцию {@link android.app.Activity} можно с помощью кнопки действия. Подробные сведения см. в справочном руководстве по классу + {@link android.support.v4.app.NotificationCompat.Builder}. +

+ +

Приоритет уведомлений

+

+ При желании уведомлению можно задать приоритет. Приоритет действует + как подсказка пользовательскому интерфейсу устройства о том, каким образом следует выводить уведомление. + Чтобы задать приоритет уведомления, вызовите метод {@link + android.support.v4.app.NotificationCompat.Builder#setPriority(int) + NotificationCompat.Builder.setPriority()} и передайте ему одну из констант приоритетов {@link + android.support.v4.app.NotificationCompat}. Имеется + пять уровней приоритета, начиная от {@link + android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) и до {@link + android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2). Если приоритет не задан, + то по умолчанию он будет иметь значение {@link + android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0). +

+

Сведения о присвоении подходящего уровня приоритета см. в разделе "Правильная настройка + приоритета уведомления и управление им" в руководстве "Разработка уведомлений" +. +

+ +

Создание простого уведомления

+

+ Следующий фрагмент кода иллюстрирует простое уведомление, указывающее операцию, которую нужно будет открыть, когда + пользователь нажмет уведомление. Обратите внимание, что этот код создает объект + {@link android.support.v4.app.TaskStackBuilder} и использует его для создания объекта + {@link android.app.PendingIntent} для действия. Более подробно этот шаблон описан + в разделе + "Сохранение навигации при запуске операции": +

+
+NotificationCompat.Builder mBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.notification_icon)
+        .setContentTitle("My notification")
+        .setContentText("Hello World!");
+// Creates an explicit intent for an Activity in your app
+Intent resultIntent = new Intent(this, ResultActivity.class);
+
+// The stack builder object will contain an artificial back stack for the
+// started Activity.
+// This ensures that navigating backward from the Activity leads out of
+// your application to the Home screen.
+TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+// Adds the back stack for the Intent (but not the Intent itself)
+stackBuilder.addParentStack(ResultActivity.class);
+// Adds the Intent that starts the Activity to the top of the stack
+stackBuilder.addNextIntent(resultIntent);
+PendingIntent resultPendingIntent =
+        stackBuilder.getPendingIntent(
+            0,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        );
+mBuilder.setContentIntent(resultPendingIntent);
+NotificationManager mNotificationManager =
+    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+// mId allows you to update the notification later on.
+mNotificationManager.notify(mId, mBuilder.build());
+
+

Готово. Теперь пользователь получит уведомление.

+ +

Применение расширенного макета к уведомлению

+

+ Чтобы уведомление отображалось в расширенном виде, сначала создайте объект + {@link android.support.v4.app.NotificationCompat.Builder} с требуемыми параметрами + обычного представления. Затем вызовите метод {@link android.support.v4.app.NotificationCompat.Builder#setStyle + Builder.setStyle()}, первым аргументом которого должен быть объект расширенного макета. +

+

+ Помните, что расширенные уведомления не поддерживаются на платформах версии более ранней, чем Android 4.1. Сведения + о том, как обрабатывать уведомления для версий платформы Android 4.1 и более ранних, см. в + разделе Вопросы совместимости. +

+

+ Например, следующий фрагмент кода демонстрирует, каким образом следует изменить уведомление, созданное + в предыдущем фрагменте, чтобы использовать расширенный макет: +

+
+NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
+    .setSmallIcon(R.drawable.notification_icon)
+    .setContentTitle("Event tracker")
+    .setContentText("Events received")
+NotificationCompat.InboxStyle inboxStyle =
+        new NotificationCompat.InboxStyle();
+String[] events = new String[6];
+// Sets a title for the Inbox in expanded layout
+inboxStyle.setBigContentTitle("Event tracker details:");
+...
+// Moves events into the expanded layout
+for (int i=0; i < events.length; i++) {
+
+    inboxStyle.addLine(events[i]);
+}
+// Moves the expanded layout object into the notification object.
+mBuilder.setStyle(inBoxStyle);
+...
+// Issue the notification here.
+
+ +

Вопросы совместимости

+ +

+ Не все функции уведомлений поддерживаются в той или иной версии платформы, даже несмотря на то, что + методы для их задания имеются в классе вспомогательной библиотеки + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. + Например, кнопки действий, которые зависят от расширенных уведомлений, отображаются только в версии Android + 4.1 и последующих версиях, поскольку сами расширенные уведомления поддерживаются только начиная с версии + Android 4.1. +

+

+ Для обеспечения наилучшей совместимости создавать уведомления следует с помощью класса + {@link android.support.v4.app.NotificationCompat NotificationCompat} и его подклассов, + в частности {@link android.support.v4.app.NotificationCompat.Builder + NotificationCompat.Builder}. Кроме того, при реализации уведомления придерживайтесь вот такого процесса: +

+
    +
  1. + Предоставляйте все функции уведомления всем пользователям независимо от используемой ими + версии. Для этого убедитесь, что все функции вызываются из + операции {@link android.app.Activity} в вашем приложении. Возможно, для этого вам потребуется добавить новый объект + {@link android.app.Activity}. +

    + Например, если требуется использовать + {@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} для + создания элемента управления, который останавливает и запускает воспроизведение мультимедиа, сначала реализуйте этот + элемент управления в операции {@link android.app.Activity} из вашего приложения. +

    +
  2. +
  3. + Обеспечьте доступ к этой функции операции {@link android.app.Activity} всех пользователей, + сделав так, чтобы эта операция запускалась, когда пользователь нажимает уведомление. Для этого + создайте объект {@link android.app.PendingIntent} + для операции {@link android.app.Activity}. Вызовите + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}, чтобы добавить объект {@link android.app.PendingIntent} в уведомление. +
  4. +
  5. + Затем добавьте в свое уведомление требуемые функции расширенного уведомления. Помните, + что любая добавляемая функция должна быть предусмотрена в операции {@link android.app.Activity}, + которая запускается, когда пользователь нажимает уведомление. +
  6. +
+ + + + +

Управление уведомлениями

+

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

+

+ Например, Gmail уведомляет пользователя о поступлении новых сообщений электронной почты путем увеличения счетчика + непрочитанных сообщений и добавления в уведомления кратких сведений о каждом из них. Это называется + "укладкой уведомлений в стопку" (stacking) и подробно описано в руководстве + "Разработка уведомлений". +

+

+ Примечание. Для этой функции Gmail требуется расширенный макет папки входящих сообщений, который + входит в состав функции расширенных уведомлений, поддержка которой предусмотрена начиная с версии Android 4.1. +

+

+ В разделах ниже описано, как обновлять уведомления, а также как удалять их. +

+

Обновление уведомлений

+

+ Чтобы настроить уведомление таким образом, что его впоследствии можно было обновлять, его следует выдавать с идентификатором уведомления путем + вызова метода {@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()}. + Чтобы изменить это уведомление, после того как оно выдано, + обновите или создайте объект {@link android.support.v4.app.NotificationCompat.Builder}, + постройте на его основе объект {@link android.app.Notification} и выдайте + объект {@link android.app.Notification} с тем же идентификатором, который использовался ранее. Если + предыдущее уведомление все еще отображается на экране, система обновит его с использованием содержимого + объекта{@link android.app.Notification}. Если предыдущее уведомление было закрыто, то + вместо него будет создано новое уведомление. +

+

+ Следующий фрагмент кода демонстрирует уведомление, которое обновляется с учетом + количества произошедших событий. Он накладывает уведомления друг на друга (укладывает в стопку), отображая сводную информацию: +

+
+mNotificationManager =
+        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+// Sets an ID for the notification, so it can be updated
+int notifyID = 1;
+mNotifyBuilder = new NotificationCompat.Builder(this)
+    .setContentTitle("New Message")
+    .setContentText("You've received new messages.")
+    .setSmallIcon(R.drawable.ic_notify_status)
+numMessages = 0;
+// Start of a loop that processes data and then notifies the user
+...
+    mNotifyBuilder.setContentText(currentText)
+        .setNumber(++numMessages);
+    // Because the ID remains unchanged, the existing notification is
+    // updated.
+    mNotificationManager.notify(
+            notifyID,
+            mNotifyBuilder.build());
+...
+
+ + +

Удаление уведомлений

+

+ Уведомления отображаются на экране, пока не произойдет одно из следующих событий: +

+
    +
  • + пользователь закроет уведомления по одному или командой "Очистить все" (если + уведомление можно очистить); +
  • +
  • + пользователь нажмет уведомление, а вы вызвали метод + {@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()}, когда + создавали уведомление; +
  • +
  • + вы вызовете метод {@link android.app.NotificationManager#cancel(int) cancel()} для уведомления с определенным + идентификатором. Кроме того, этот метод удаляет текущие уведомления; +
  • +
  • + вы вызовете метод {@link android.app.NotificationManager#cancelAll() cancelAll()}, который удаляет + все ранее выданные вами уведомления. +
  • +
+ + +

Сохранение навигации при запуске операции

+

+ Когда вы запускаете операцию {@link android.app.Activity} из уведомления, вам необходимо сохранить + привычные пользователю приемы навигации. При нажатии "Назад" пользователь должен возвращаться назад в + обычном потоке операций приложения вплоть до главного экрана, а при нажатии "Последние" операция + {@link android.app.Activity} должна быть отображена как отдельная задача. Чтобы сохранить приемы навигации, операцию + {@link android.app.Activity} следует запускать в новой задаче. Как настроить объект + {@link android.app.PendingIntent} таким образом, чтобы получить новую задачу, зависит от типа операции + {@link android.app.Activity}, которую вы запускаете. В целом есть две ситуации: +

+
+
+ Обычная операция +
+
+ Вы запускаете операцию {@link android.app.Activity}, которая является частью обычного потока + работы приложения. В этом случае настройте объект {@link android.app.PendingIntent} на + запуск новой задачи и предоставьте объекту {@link android.app.PendingIntent} стек переходов назад, + который воспроизводит обычную работу приложения в ситуации, когда пользователь нажимает "Назад" . +

+ Это можно увидеть на примере уведомлений приложения Gmail. При нажатии уведомления для + одного сообщения электронной почты отображается само сообщение. При нажатии Назад вы переходите + назад по представлениям Gmail вплоть до главного экрана точно так же, как если бы вы вошли в Gmail с + главного экрана, а не из уведомления. +

+

+ Происходит это независимо от того, в каком приложении вы находились в тот момент, когда нажали + уведомление. Например, если при составлении сообщения в Gmail вы нажмете + уведомление об одном сообщении электронной почты, вы сразу же перейдете в это сообщение. При нажатии "Назад" + вы перейдете в папку входящих сообщений, а затем на главный экран, а не в + сообщение, которое составляли. +

+
+
+ Особая операция +
+
+ Пользователь может увидеть эту операцию {@link android.app.Activity}, только если она запущена из уведомления. + В некотором смысле операция {@link android.app.Activity} расширяет уведомление путем предоставления + информации, которую было бы сложно отобразить в самом уведомлении. В этом случае + настройте объект {@link android.app.PendingIntent} на запуск в новой задаче. При этом создавать + стек переходов назад не требуется, поскольку запущенная операция {@link android.app.Activity} не является частью + потока операций приложения. При нажатии "Назад" пользователь все же перейдет на + главный экран. +
+
+ +

Настройка PendingIntent обычной операции

+

+ Чтобы настроить объект {@link android.app.PendingIntent}, который непосредственно запускает операцию + {@link android.app.Activity}, выполните следующие шаги: +

+
    +
  1. + Определите иерархию операций {@link android.app.Activity} своего приложения в файле манифеста. +
      +
    1. + Добавьте поддержку для версии Android 4.0.3 и более ранних версий. Для этого укажите родительский объект операции + {@link android.app.Activity}, которую запускаете, добавив элемент +<meta-data> + в качестве дочернего для элемента +<activity>. +

      + Для этого элемента задайте +android:name="android.support.PARENT_ACTIVITY". + Задайте +android:value="<parent_activity_name>", + где <parent_activity_name> ― это значение +android:name + для родительского элемента +<activity> +. В качестве примера см. следующий код XML. +

      +
    2. +
    3. + Также добавьте поддержку для версии Android 4.1 и более поздних версий. Для этого добавьте атрибут +android:parentActivityName + в элемент +<activity> + запускаемой операции{@link android.app.Activity}. +
    4. +
    +

    + Итоговый код XML должен выглядеть следующим образом: +

    +
    +<activity
    +    android:name=".MainActivity"
    +    android:label="@string/app_name" >
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +<activity
    +    android:name=".ResultActivity"
    +    android:parentActivityName=".MainActivity">
    +    <meta-data
    +        android:name="android.support.PARENT_ACTIVITY"
    +        android:value=".MainActivity"/>
    +</activity>
    +
    +
  2. +
  3. + Создайте стек переходов назад, основанный на объекте {@link android.content.Intent}, который запускает операцию + {@link android.app.Activity}: +
      +
    1. + Создайте объект {@link android.content.Intent}, который запускает операцию {@link android.app.Activity}. +
    2. +
    3. + Создайте построитель стека, вызвав метод {@link android.app.TaskStackBuilder#create + TaskStackBuilder.create()}. +
    4. +
    5. + Добавьте стек переходов назад в построитель стеков путем вызова метода + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}. + Для каждой операции {@link android.app.Activity} из иерархии, определенной в + файле манифеста, в стеке переходов назад имеется объект {@link android.content.Intent}, который + запускает {@link android.app.Activity}. Этот метод также добавляет флаги, которые запускают + стек в новой задаче. +

      + Примечание. Несмотря на то что аргумент + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} + является ссылкой на запускаемую операцию {@link android.app.Activity}, при вызове этого метода + не добавляется объект {@link android.content.Intent}, который запускает операцию + {@link android.app.Activity}. Это делается на следующем шаге. +

      +
    6. +
    7. + Добавьте объект {@link android.content.Intent}, который запускает операцию {@link android.app.Activity} + из уведомления, путем вызова метода + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. + Передайте объект {@link android.content.Intent}, созданный вами на первом шаге, в качестве + аргумента методу + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. +
    8. +
    9. + При необходимости добавьте аргументы в объекты {@link android.content.Intent} из + стека путем вызова метода {@link android.support.v4.app.TaskStackBuilder#editIntentAt + TaskStackBuilder.editIntentAt()}. Иногда это требуется для того, чтобы + целевая операция {@link android.app.Activity} отображала значимые данные, когда пользователь переходит + в нее с помощью действия "Назад". +
    10. +
    11. + Получите объект {@link android.app.PendingIntent} для этого стека переходов назад путем вызова метода + {@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}. + Затем этот объект {@link android.app.PendingIntent} можно будет использовать в качестве аргумента для метода + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
    12. +
    +
  4. +
+

+ Следующий фрагмент кода демонстрирует этот процесс: +

+
+...
+Intent resultIntent = new Intent(this, ResultActivity.class);
+TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+// Adds the back stack
+stackBuilder.addParentStack(ResultActivity.class);
+// Adds the Intent to the top of the stack
+stackBuilder.addNextIntent(resultIntent);
+// Gets a PendingIntent containing the entire back stack
+PendingIntent resultPendingIntent =
+        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+...
+NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+builder.setContentIntent(resultPendingIntent);
+NotificationManager mNotificationManager =
+    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+mNotificationManager.notify(id, builder.build());
+
+ +

Настройка PendingIntent особой операции

+

+ В приведенном далее разделе описывается настройка объекта + {@link android.app.PendingIntent} для особой операции. +

+

+ Особой операции {@link android.app.Activity} не требуется стек перехода назад, поэтому не нужно + определять иерархию объектов {@link android.app.Activity} в файле манифеста и + вызывать + метод {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} для построения + стека перехода назад. Вместо этого в файле манифеста задайте параметры задачи {@link android.app.Activity} + и создайте объект {@link android.app.PendingIntent} путем вызова метода + {@link android.app.PendingIntent#getActivity getActivity()}: +

+
    +
  1. + Добавьте следующие атрибуты в элемент +<activity> + в файле манифеста для операции {@link android.app.Activity} +
    +
    +android:name="activityclass" +
    +
    + Полное имя класса операции. +
    +
    +android:taskAffinity="" +
    +
    + В сочетании с + флагом {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK}, + который вы задали в коде, это гарантирует, что данная операция {@link android.app.Activity} не + перейдет в задачу приложения, используемую по умолчанию. Любые существующие задачи, имеющие + отношение к используемой по умолчанию задаче приложения, затронуты не будут. +
    +
    +android:excludeFromRecents="true" +
    +
    + Исключает новую задачу из списка "Последние",с тем чтобы пользователь не мог случайно + вернуться в нее. +
    +
    +

    + Данный элемент показан в этом фрагменте кода: +

    +
    +<activity
    +    android:name=".ResultActivity"
    +...
    +    android:launchMode="singleTask"
    +    android:taskAffinity=""
    +    android:excludeFromRecents="true">
    +</activity>
    +...
    +
    +
  2. +
  3. + Построение и выдача уведомления: +
      +
    1. + Создайте объект {@link android.content.Intent}, который запускает операцию + {@link android.app.Activity}. +
    2. +
    3. + Настройте операцию {@link android.app.Activity}, запускаемую в новой пустой задаче, путем вызова метода + {@link android.content.Intent#setFlags setFlags()} с флагами + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + и + {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK}. +
    4. +
    5. + Задайте для объекта {@link android.content.Intent} любые другие требуемые параметры. +
    6. +
    7. + Создайте объект {@link android.app.PendingIntent} из объекта {@link android.content.Intent} + путем вызова метода {@link android.app.PendingIntent#getActivity getActivity()}. + Затем этот объект {@link android.app.PendingIntent} можно будет использовать в качестве аргумента для метода + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
    8. +
    +

    + Следующий фрагмент кода демонстрирует этот процесс: +

    +
    +// Instantiate a Builder object.
    +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    +// Creates an Intent for the Activity
    +Intent notifyIntent =
    +        new Intent(this, ResultActivity.class);
    +// Sets the Activity to start in a new, empty task
    +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    +                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    +// Creates the PendingIntent
    +PendingIntent notifyPendingIntent =
    +        PendingIntent.getActivity(
    +        this,
    +        0,
    +        notifyIntent,
    +        PendingIntent.FLAG_UPDATE_CURRENT
    +);
    +
    +// Puts the PendingIntent into the notification builder
    +builder.setContentIntent(notifyPendingIntent);
    +// Notifications are issued by sending them to the
    +// NotificationManager system service.
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// Builds an anonymous Notification object from the builder, and
    +// passes it to the NotificationManager
    +mNotificationManager.notify(id, builder.build());
    +
    +
  4. +
+ + +

Отображение хода выполнения в уведомлении

+

+ Уведомления могут содержать индикатор хода выполнения с эффектом анимации, который показывает пользователям состояние + текущей операции. Если имеется возможность оценить, сколько времени занимает операция и какой процент ее объема + уже завершен, используйте "определенную" форму индикатора + (индикатор хода выполнения). Если продолжительность операции оценить невозможно, используйте + "неопределенную" форму индикатора (индикатор операции). +

+

+ Индикаторы хода выполнения отображаются с помощью реализации класса + {@link android.widget.ProgressBar} платформы. +

+

+ Для использования индикатора хода выполнения на платформах начиная с Android 4.0 вызовите метод + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. Для + предыдущих версий необходимо будет создать собственный нестандартный макет уведомлений, который + содержит представление {@link android.widget.ProgressBar}. +

+

+ В приведенных далее разделах описывается отображение хода выполнения в уведомлении с помощью + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. +

+ +

Отображение индикатора хода выполнения фиксированной продолжительности

+

+ Чтобы вывести на экран определенный индикатор хода выполнения, добавьте его в свое уведомление, вызвав метод + {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(max, progress, false)}, а затем выдайте уведомление. По мере выполнения + увеличивайте значение progress и обновляйте уведомление. По окончании операции + progress должен быть равен max. Стандартный способ вызова метода + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} + заключается в следующем: задать значение max равным 100 с последующим увеличением progress в виде + величины "процента выполнения" операции. +

+

+ По окончании операции можно оставить этот индикатор хода выполнения на экране или удалить его. В + любом случае не забывайте обновить текст уведомления, чтобы указать, что операция выполнена. + Чтобы удалить индикатор выполнения, вызовите метод + {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(0, 0, false)}. Например: +

+
+...
+mNotifyManager =
+        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+mBuilder = new NotificationCompat.Builder(this);
+mBuilder.setContentTitle("Picture Download")
+    .setContentText("Download in progress")
+    .setSmallIcon(R.drawable.ic_notification);
+// Start a lengthy operation in a background thread
+new Thread(
+    new Runnable() {
+        @Override
+        public void run() {
+            int incr;
+            // Do the "lengthy" operation 20 times
+            for (incr = 0; incr <= 100; incr+=5) {
+                    // Sets the progress indicator to a max value, the
+                    // current completion percentage, and "determinate"
+                    // state
+                    mBuilder.setProgress(100, incr, false);
+                    // Displays the progress bar for the first time.
+                    mNotifyManager.notify(0, mBuilder.build());
+                        // Sleeps the thread, simulating an operation
+                        // that takes time
+                        try {
+                            // Sleep for 5 seconds
+                            Thread.sleep(5*1000);
+                        } catch (InterruptedException e) {
+                            Log.d(TAG, "sleep failure");
+                        }
+            }
+            // When the loop is finished, updates the notification
+            mBuilder.setContentText("Download complete")
+            // Removes the progress bar
+                    .setProgress(0,0,false);
+            mNotifyManager.notify(ID, mBuilder.build());
+        }
+    }
+// Starts the thread by calling the run() method in its Runnable
+).start();
+
+ + +

Отображение непрерывного индикатора операции

+

+ Для отображения неопределенного индикатора операции добавьте его в свое уведомление с помощью метода + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)} + (два первых аргумента игнорируются) и выдайте уведомление. В результате будет показан индикатор, + который будет иметь такой же стиль, что и индикатор хода выполнения, за исключением того, что его анимация будет непрерывной. +

+

+ Выдайте уведомление в начале операции. Анимация будет воспроизводиться до тех пор, пока вы не + измените уведомление. Когда операция будет выполнена, вызовите метод + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)}, + после чего обновите уведомление, удалив индикатор операции. + Всегда поступайте таким образом. В противном случае анимация будет воспроизводиться и после завершения операции. Также + не забывайте изменить текст уведомления, чтобы указать, что операция выполнена. +

+

+ Чтобы посмотреть, как работают индикаторы операций, перейдите к предыдущему фрагменту кода. Найдите следующие строки: +

+
+// Sets the progress indicator to a max value, the current completion
+// percentage, and "determinate" state
+mBuilder.setProgress(100, incr, false);
+// Issues the notification
+mNotifyManager.notify(0, mBuilder.build());
+
+

+ Замените их вот этими строками: +

+
+ // Sets an activity indicator for an operation of indeterminate length
+mBuilder.setProgress(0, 0, true);
+// Issues the notification
+mNotifyManager.notify(0, mBuilder.build());
+
+ +

Метаданные уведомления

+ +

Уведомления можно сортировать в соответствии с метаданными, которые назначаются с помощью +следующих методов {@link android.support.v4.app.NotificationCompat.Builder}:

+ +
    +
  • метод {@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} + информирует систему о том, как обрабатывать уведомления вашего приложения, когда устройство находится в режиме приоритета + (например, если ваше уведомление сообщает о входящем вызове, мгновенном сообщении или сигнале будильника);
  • +
  • метод{@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} вызывает + отображение уведомлений, в поле приоритета которых задано значение {@code PRIORITY_MAX} или {@code PRIORITY_HIGH} +, в небольшом плавающем окне, если уведомление также сопровождается звуковым сигналом или вибрацией;
  • +
  • метод {@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} + позволяет добавить к уведомлению список людей. С его помощью ваше уведомление может сигнализировать + системе о том, что она должна сгруппировать уведомления от указанных людей или считать уведомления + от этих людей более важными.
  • +
+ +
+ +

+ Рисунок 3. Полноэкранная операция, отображающая уведомление heads-up +

+
+ +

Уведомления heads-up

+ +

В Android 5.0 (уровень API 21) уведомления могут отображаться в небольшом плавающем окне +(оно также называется уведомлением heads-up), когда устройство активно +(то есть устройство разблокировано и его экран включен). Эти уведомления +выглядят так же, как компактная форма вашего уведомления, за исключением того, что в +уведомлениях heads-up также отображаются кнопки действий. Пользователи могут выполнять действия или закрывать +уведомление heads-up, не покидая текущее приложение.

+ +

Примеры ситуаций, в которых могут быть вызваны уведомления heads-up:

+ +
    +
  • операция пользователя выполняется в полноэкранном режиме (приложение использует +{@link android.app.Notification#fullScreenIntent}) или;
  • +
  • уведомление имеет высокий приоритет и использует рингтоны или + вибрацию.
  • +
+ +

Уведомления на экране блокировки

+ +

С выходом версии Android 5.0 (уровень API 21) уведомления теперь могут отображаться на экране +блокировки. С помощью этой функции можно выводит на экран элементы управления мультимедиа и другие стандартные +действия. В настройках пользователи могут выбрать, следует ли отображать уведомления на экране блокировки, а +вы можете указать, будет ли уведомление из вашего приложения видно на нем.

+ +

Настройка видимости

+ +

Ваше приложение может определять объем информации, отображаемой в уведомлениях, которые выводятся на экране +блокировки. Метод {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()} +вызывается для того, чтобы указать одно из следующих значений:

+ +
    +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC} показывает полное содержимое + уведомления;
  • +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET} не отображает какую-либо часть + этого уведомления на экране блокировки;
  • +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} показывает базовую информацию, + такую как значок уведомления и заголовок его содержимого, но скрывает полное содержимое уведомления.
  • +
+ +

Когда задается значение {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE}, вы также можете +предоставить альтернативную версию содержимого уведомления, в который скрыты некоторые сведения. Например, +приложение по работе с СМС может выводить уведомление, в котором говорится У вас 3 новых текстовых сообщения, а содержимое + и отправители сообщений скрыты. Чтобы предоставить возможность такого альтернативного уведомления, сначала создайте его +с помощью {@link android.support.v4.app.NotificationCompat.Builder}. При создании +частного объекта уведомления прикрепите к нему альтернативное уведомление, воспользовавшись методом +{@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} +.

+ +

Управление воспроизведением мультимедиа на экране блокировки

+ +

В версии Android 5.0 (API уровня 21) на экране блокировки больше не отображаются элементы управления воспроизведением мультимедиа, +основанные на классе {@link android.media.RemoteControlClient}, использование которого прекращено. Вместо этого используйте +шаблон {@link android.app.Notification.MediaStyle} с методом +{@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +, который преобразует действия в доступные для нажатия значки.

+ +

Примечание. Шаблон и метод {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +не входят в состав вспомогательной библиотеки, поэтому эти функции работают только в версии Android 5.0 и +последующих версиях.

+ +

Чтобы отобразить элементы управления мультимедиа на экране блокировки в Android 5.0, задайте для видимости +значение {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC}, выполнив описанную выше процедуру. Затем добавьте +действия и задайте шаблон {@link android.app.Notification.MediaStyle}, как описано в +следующем образце кода:

+ +
+Notification notification = new Notification.Builder(context)
+    // Show controls on lock screen even when user hides sensitive content.
+    .setVisibility(Notification.VISIBILITY_PUBLIC)
+    .setSmallIcon(R.drawable.ic_stat_player)
+    // Add media control buttons that invoke intents in your media service
+    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
+    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
+    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
+    // Apply the media style template
+    .setStyle(new Notification.MediaStyle()
+    .setShowActionsInCompactView(1 /* #1: pause button */)
+    .setMediaSession(mMediaSession.getSessionToken())
+    .setContentTitle("Wonderful music")
+    .setContentText("My Awesome Band")
+    .setLargeIcon(albumArtBitmap)
+    .build();
+
+ +

Примечание. Прекращение использования класса {@link android.media.RemoteControlClient} +имеет и другие последствия для управления мультимедиа. Подробные сведения о новых API-интерфейсах для управления сеансами воспроизведения мультимедиа см. в разделе +Управление воспроизведением мультимедиа +.

+ + + +

Нестандартные макеты уведомлений

+

+ Платформа уведомлений позволяет задавать нестандартные макеты уведомлений, которые + определяют внешний вид уведомлений в объекте {@link android.widget.RemoteViews}. + Уведомления с нестандартным макетом — такие же, как обычные уведомления, но в их основе лежит класс + {@link android.widget.RemoteViews}, определенный в файле XML макета. +

+

+ Высота изображения, которую можно получить с использованием нестандартного макета уведомления, зависит от представления уведомления. Обычные + макеты представления ограничены по высоте 64 пикселами, а расширенные — 256 пикселами. +

+

+ Чтобы определить нестандартный макет уведомления, начните с инициализации объекта + {@link android.widget.RemoteViews}, который загружает файл XML макета. Затем, + вместо вызова методов, например + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()}, + вызовите {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Чтобы задать + сведения о содержимом в нестандартном уведомлении, используйте методы из + {@link android.widget.RemoteViews}, чтобы определить значения для дочерних представлений: +

+
    +
  1. + Создайте макет XML для уведомления в отдельном файле. Можно использовать любое имя файла + по своему усмотрению, однако расширение должно быть .xml +
  2. +
  3. + В своем приложении с помощью методов {@link android.widget.RemoteViews} определите значки и текст + уведомления. Поместите этот объект {@link android.widget.RemoteViews} в свой + {@link android.support.v4.app.NotificationCompat.Builder}, вызвав метод + {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Не следует + задавать фон {@link android.graphics.drawable.Drawable} для объекта + {@link android.widget.RemoteViews}, поскольку цвет текста может стать нечитаемым. +
  4. +
+

+ В классе {@link android.widget.RemoteViews} также имеются методы, с помощью которых можно легко + добавить {@link android.widget.Chronometer} или {@link android.widget.ProgressBar} + в свой макет уведомления. Подробные сведения о создании нестандартных макетов для + уведомлений см. в справочной документации по {@link android.widget.RemoteViews}. +

+

+ Внимание! При использовании нестандартного макета уведомлений следует особое внимание уделять + обеспечению совместимости такого макета с разными вариантами ориентации устройства и разрешения экрана. Этот + совет относится ко всем макетам View, но особенно важен он для уведомлений, поскольку + размер панели уведомлений весьма ограничен. Не следует делать свои нестандартные макеты слишком + сложными и обязательно нужно тестировать их на различных конфигурациях устройств. +

+ +

Использование ресурсов стиля для текста нестандартного уведомления

+

+ Для форматирования текста нестандартного уведомления всегда используйте ресурсы стиля. Цвет фона + уведомления может меняться на разных устройствах и в разных версиях системы, а использование ресурсов стиля + позволяет это учесть. Начиная с версии Android 2.3 система определяет стиль для + текста уведомления со стандартным макетом. Если вы используете тот же стиль в приложениях, предназначенных для версии Android + 2.3 и последующих версий, то вы гарантируете видимость своего текста на фоне экрана. +

diff --git a/docs/html-intl/intl/ru/guide/topics/ui/overview.jd b/docs/html-intl/intl/ru/guide/topics/ui/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..0e9628b6d0162705ac7fa50fee351681c46b8bfc --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=Обзор пользовательского интерфейса +@jd:body + + +

Все элементы интерфейса пользователя в приложении Android создаются с помощью объектов {@link android.view.View} и +{@link android.view.ViewGroup}. Объект {@link android.view.View} формирует +на экране элемент, с которым пользователь может взаимодействовать. Объект {@link android.view.ViewGroup} содержит +другие объекты {@link android.view.View} (и {@link android.view.ViewGroup}) для +определения макета интерфейса.

+ +

Android предоставляет коллекцию подклассов {@link android.view.View} и {@link +android.view.ViewGroup}, которая включает в себя обычные элементы ввода (такие как кнопки и текстовые +поля) и различные модели макет (такие как линейный или относительный макет).

+ + +

Макеты пользовательского интерфейса

+ +

Пользовательский интерфейс для каждого компонента вашего приложения определяется с помощью иерархии объектов {@link +android.view.View} и {@link android.view.ViewGroup}, как показано на рисунке 1. Каждая группа просмотра +представляет собой невидимый контейнер, в котором объединены дочерние виды, причем дочерние виды могут представлять собой элементы +ввода или другие виджеты, которые +составляют часть пользовательского интерфейса. Эта древовидная иерархия может быть настолько простой или сложной, насколько +требуется (чем проще, тем лучше для производительности).

+ + +

Рисунок 1. Иллюстрация иерархии, которая определяет +макет интерфейса.

+ +

Чтобы объявить свой макет, можно создать экземпляры объектов {@link android.view.View} в коде и запустить построение +дерева, но самый простой и наиболее эффективный способ — определение макета с помощью файла XML. +XML позволяет создавать удобочитаемую структуру макета, подобно HTML.

+ +

Имя элемента XML для вида соответствует классу Android, к которому от относится. Так, элемент +<TextView> создает виджет {@link android.widget.TextView} в пользовательском интерфейсе, +а элемент <LinearLayout> создает группу просмотра {@link android.widget.LinearLayout} +.

+ +

Например, простой вертикальный макет с текстом и кнопкой выглядит следующим образом:

+
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent" 
+              android:layout_height="fill_parent"
+              android:orientation="vertical" >
+    <TextView android:id="@+id/text"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="I am a TextView" />
+    <Button android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="I am a Button" />
+</LinearLayout>
+
+ +

При загрузке ресурсов макетов в приложение Android инициализирует каждый узел макета в +объект режима выполнения, который можно использовать для определения дополнительного поведения, запроса состояния объекта или изменения +макета.

+ +

Полное руководство по созданию макета пользовательского интерфейса см. в документе Макеты +XML. + + +

Компоненты пользовательского интерфейса

+ +

Не обязательно создавать все элементы пользовательского интерфейса с помощью объектов {@link android.view.View} и {@link +android.view.ViewGroup}. Android предоставляет несколько компонентов приложений, которые содержат +стандартный макет пользовательского интерфейса, где остается лишь определить содержимое. Каждый из этих компонентов пользовательского интерфейса +содержит уникальный набор API, который описан в соответствующих документах, таких как Строка действий, Диалоги и Уведомления о состоянии.

+ + diff --git a/docs/html-intl/intl/ru/guide/topics/ui/settings.jd b/docs/html-intl/intl/ru/guide/topics/ui/settings.jd new file mode 100644 index 0000000000000000000000000000000000000000..4325439721deb860fac3aca4c3127dce0047d70f --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=Настройки +page.tags=preference,preferenceactivity,preferencefragment + +@jd:body + + +
+
+ +

Содержание документа

+
    +
  1. Обзор +
      +
    1. Предпочтения
    2. +
    +
  2. +
  3. Определение предпочтений в XML +
      +
    1. Создание групп настроек
    2. +
    3. Использование намерений
    4. +
    +
  4. +
  5. Создание операции предпочтений
  6. +
  7. Использование фрагментов предпочтений
  8. +
  9. Значения настроек по умолчанию
  10. +
  11. Использование заголовков предпочтений +
      +
    1. Создание файла заголовков
    2. +
    3. Отображение заголовков
    4. +
    5. Поддержка старых версий посредством заголовков предпочтений
    6. +
    +
  12. +
  13. Чтение предпочтений +
      +
    1. Отслеживание изменений предпочтений
    2. +
    +
  14. +
  15. Контроль использования сети
  16. +
  17. Построение пользовательского предпочтения +
      +
    1. Указание пользовательского интерфейса
    2. +
    3. Сохранение значения настройки
    4. +
    5. Инициализация текущего значения
    6. +
    7. Предоставление значения по умолчанию
    8. +
    9. Сохранение и восстановление состояния предпочтений
    10. +
    +
  18. +
+ +

Основные классы

+
    +
  1. {@link android.preference.Preference}
  2. +
  3. {@link android.preference.PreferenceActivity}
  4. +
  5. {@link android.preference.PreferenceFragment}
  6. +
+ + +

См. также:

+
    +
  1. Руководство по дизайну настроек
  2. +
+
+
+ + + + +

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

+ +

Если вы хотите предоставить настройки для вашего приложения, вы должны использовать +API-интерфейсы {@link android.preference.Preference} системы Android для построения интерфейса, согласованного с +привычным для пользователей других приложений Android (включая системные настройки). В этом документе показано, +как построить настройки вашего приложения посредством API-интерфейсов {@link android.preference.Preference}.

+ +
+

Дизайн настроек

+

Подробную информацию о дизайне настроек см. в руководстве по дизайну настроек.

+
+ + + +

Рисунок 1. Снимки экранов настроек приложения Android +для обмена сообщениями. Выбор элемента, заданного посредством {@link android.preference.Preference}, +открывает интерфейс для изменения значения.

+ + + + +

Обзор

+ +

Вместо использования отображаемых объектов {@link android.view.View} для построения пользовательского интерфейса, настройки создаются +с помощью различных подклассов класса {@link android.preference.Preference}, который вы +объявляете в XML-файле.

+ +

Объект {@link android.preference.Preference} является строительным блоком для отдельной +настройки. Каждый объект {@link android.preference.Preference} отображается в виде элемента в списке и предоставляет +соответствующий пользовательский интерфейс для изменения настройки пользователями. Например, {@link +android.preference.CheckBoxPreference} создает элемент списка, который показывает флажок, а {@link +android.preference.ListPreference} создает элемент, который открывает диалоговое окно со списком вариантов для выбора.

+ +

Каждый добавляемый вами объект {@link android.preference.Preference} имеет соответствующую пару «ключ-значение», +которую система использует для сохранения настройки в файле {@link android.content.SharedPreferences} +значений настроек вашего приложения по умолчанию. Когда пользователь изменяет настройку, система обновляет соответствующее +значение в файле {@link android.content.SharedPreferences}. Вам потребуется +напрямую взаимодействовать с файлом, связанным с {@link android.content.SharedPreferences}, только в случае, +когда нужно прочитать значение для определения поведения вашего приложения на основе пользовательских настроек.

+ +

Значение, сохраненное в {@link android.content.SharedPreferences} для каждой настройки, может относиться к одному из +следующих типов данных:

+ +
    +
  • Логическое значение
  • +
  • Число с плавающей точкой
  • +
  • Целое число
  • +
  • Длинное целое число
  • +
  • Строка
  • +
  • Строка {@link java.util.Set}
  • +
+ +

Поскольку пользовательский интерфейс настроек вашего приложения создается посредством объектов {@link android.preference.Preference}, +а не +объектов {@link android.view.View}, вам потребуется использовать специализированные подклассы {@link android.app.Activity} или +{@link android.app.Fragment} для отображения настроек из списка:

+ +
    +
  • Если ваше приложение поддерживает версии Android старше 3.0 (API уровня 10 и ниже), для построения операции +необходимо наследовать класс {@link android.preference.PreferenceActivity}.
  • +
  • В операционных системах Android 3.0 и более поздних версиях вы должны вместо этого использовать традиционный класс {@link android.app.Activity}, +который содержит объект {@link android.preference.PreferenceFragment} для отображения настроек вашего приложения. +Однако, когда у вас есть несколько групп настроек, вы можете также +использовать {@link android.preference.PreferenceActivity} для создания макета с двумя панелями для больших экранов.
  • +
+ +

Настройка объекта {@link android.preference.PreferenceActivity} и экземпляров {@link +android.preference.PreferenceFragment} описана в разделах Создание операции предпочтения и Использование +фрагментов предпочтений.

+ + +

Предпочтения

+ +

Каждая настройка для вашего приложения представлена конкретным подклассом класса {@link +android.preference.Preference}. Каждый подкласс содержит набор основных свойств, которые позволяют вам +указывать, например, заголовок для настройки и ее значение по умолчанию. Каждый подкласс также содержит +собственные специализированные свойства и пользовательский интерфейс. В качестве примера на рисунке 1 показан снимок экрана настроек +приложения Android для обмена сообщениями. Каждый элемент списка на экране настроек возвращается отдельным объектом {@link +android.preference.Preference}.

+ +

Ниже приведены самые распространенные предпочтения:

+ +
+
{@link android.preference.CheckBoxPreference}
+
Отображает элемент с флажком для настройки, которая может быть включена или выключена. Сохраненное +значение является логическим (true, если флажок установлен).
+ +
{@link android.preference.ListPreference}
+
Открывает диалоговое окно со списком переключателей. Сохраненное значение +может относиться к одному из поддерживаемых типов значений (перечисленных выше).
+ +
{@link android.preference.EditTextPreference}
+
Открывает диалоговое окно с виджетом {@link android.widget.EditText}. Сохраненное значение — {@link +java.lang.String}.
+
+ +

См. класс {@link android.preference.Preference}, который содержит список всех остальных подклассов и их +соответствующих свойств.

+ +

Конечно, встроенные классы не обеспечивают всех потребностей, и вашему приложению может понадобиться +что-либо более специализированное. Например, в настоящее время система не предоставляет класс {@link +android.preference.Preference} для выбора числа или даты. Поэтому вам может потребоваться определить +свой собственный подкласс {@link android.preference.Preference}. См. раздел Построение пользовательского предпочтения.

+ + + +

Определение предпочтений в XML

+ +

Хотя вы можете создавать новые экземпляры объектов {@link android.preference.Preference} в режиме выполнения, вы должны +определить список настроек в файле XML с иерархией +объектов {@link android.preference.Preference}. Использование файла XML для определения вашей коллекции настроек предпочтительней, поскольку файл +обладает удобочитаемой структурой, которую легко обновлять. Кроме того, настройки вашего приложения +обычно определены заранее, хотя у вас сохраняется возможность изменять коллекцию в режиме выполнения.

+ +

Каждый подкласс класса {@link android.preference.Preference} может быть объявлен посредством элемента XML, +который соответствует имени класса, например, {@code <CheckBoxPreference>}.

+ +

Вы должны сохранить файл XML в каталоге {@code res/xml/}. Хотя вы можете назвать файл любым +именем, традиционно его называют {@code preferences.xml}. Обычно вам требуется лишь один файл, +поскольку ветви иерархии (которые открывают собственный список настроек) объявлены с помощью вложенных +экземпляров {@link android.preference.PreferenceScreen}.

+ +

Примечание. Если вы хотите создать макет с несколькими панелями для ваших +настроек, вам потребуются отдельные файлы XML для каждого фрагмента.

+ +

Корневой узел XML-файла должен быть элементом {@link android.preference.PreferenceScreen +<PreferenceScreen>}. Внутри этого элемента вы добавляете каждый элемент {@link +android.preference.Preference}. Каждый дочерний элемент, который вы добавляете внутри элемента +{@link android.preference.PreferenceScreen <PreferenceScreen>}, отображается в виде одного +пункта в списке настроек.

+ +

Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <CheckBoxPreference
+        android:key="pref_sync"
+        android:title="@string/pref_sync"
+        android:summary="@string/pref_sync_summ"
+        android:defaultValue="true" />
+    <ListPreference
+        android:dependency="pref_sync"
+        android:key="pref_syncConnectionType"
+        android:title="@string/pref_syncConnectionType"
+        android:dialogTitle="@string/pref_syncConnectionType"
+        android:entries="@array/pref_syncConnectionTypes_entries"
+        android:entryValues="@array/pref_syncConnectionTypes_values"
+        android:defaultValue="@string/pref_syncConnectionTypes_default" />
+</PreferenceScreen>
+
+ +

В этом примере есть {@link android.preference.CheckBoxPreference} и {@link +android.preference.ListPreference}. Оба содержат следующие три атрибута:

+ +
+
{@code android:key}
+
Этот атрибут необходим для предпочтений, которые сохраняют значение данных. Он задает уникальный +ключ (строку), который использует система при сохранении значения этой настройки в {@link +android.content.SharedPreferences}. +

Этот атрибут не является обязательным только когда предпочтение представляет собой +{@link android.preference.PreferenceCategory} или {@link android.preference.PreferenceScreen}, либо +предпочтение указывает намерение {@link android.content.Intent} для вызова (посредством элемента {@code <intent>}) или фрагмент {@link android.app.Fragment} для отображения (с помощью атрибута {@code +android:fragment}).

+
+
{@code android:title}
+
Этот атрибут предоставляет имя настройки, отображаемое для пользователя.
+
{@code android:defaultValue}
+
Этот атрибут указывает исходное значение, которое система должна установить в файле {@link +android.content.SharedPreferences}. Вы должны указать значения по умолчанию для всех +настроек.
+
+ +

Для получения информации обо всех других поддерживаемых атрибутов см. документацию {@link +android.preference.Preference} (и соответствующий подкласс).

+ + +
+ +

Рисунок 2. Создание категорий + с заголовками.
1. Категория задана элементом {@link +android.preference.PreferenceCategory <PreferenceCategory>}.
2. Заголовок +задан посредством атрибута {@code android:title}.

+
+ + +

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

+ + +

Создание групп настроек

+ +

Если вы представляете список из 10 или более настроек, пользователям +может быть трудно их просматривать, воспринимать и обрабатывать. Это можно исправить, +разделив некоторые или все настройки на группы, что эффективно преобразует один длинный список в несколько +более коротких списков. Группа связанных настроек может быть представлена одним из двух способов:

+ + + +

Вы можете пользоваться одним или обоими из этих методов группировки для организации настроек в вашем приложении. Принимая +решение об используемом варианте и о разделении настроек на группы, вы должны следовать инструкциям в разделе +Настройки руководства «Дизайн для Android».

+ + +

Использование заголовков

+ +

Если вы хотите создать разделители с заголовками между группами настроек (как показано на рисунке 2), +поместите каждую группу объектов {@link android.preference.Preference} внутри {@link +android.preference.PreferenceCategory}.

+ +

Например:

+ +
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory 
+        android:title="@string/pref_sms_storage_title"
+        android:key="pref_key_storage_settings">
+        <CheckBoxPreference
+            android:key="pref_key_auto_delete"
+            android:summary="@string/pref_summary_auto_delete"
+            android:title="@string/pref_title_auto_delete"
+            android:defaultValue="false"... />
+        <Preference 
+            android:key="pref_key_sms_delete_limit"
+            android:dependency="pref_key_auto_delete"
+            android:summary="@string/pref_summary_delete_limit"
+            android:title="@string/pref_title_sms_delete"... />
+        <Preference 
+            android:key="pref_key_mms_delete_limit"
+            android:dependency="pref_key_auto_delete"
+            android:summary="@string/pref_summary_delete_limit"
+            android:title="@string/pref_title_mms_delete" ... />
+    </PreferenceCategory>
+    ...
+</PreferenceScreen>
+
+ + +

Использование подэкранов

+ +

Если вы хотите поместить группу настроек на подэкран (как показано на рисунке 3), поместите каждую группу +объектов {@link android.preference.Preference} внутри {@link +android.preference.PreferenceScreen}.

+ + +

Рисунок 3. Создание подэкранов. Элемент {@code +<PreferenceScreen>} +создает пункт, при выборе которого открывается отдельный список вложенных настроек.

+ +

Например:

+ +
+<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- opens a subscreen of settings -->
+    <PreferenceScreen
+        android:key="button_voicemail_category_key"
+        android:title="@string/voicemail"
+        android:persistent="false">
+        <ListPreference
+            android:key="button_voicemail_provider_key"
+            android:title="@string/voicemail_provider" ... />
+        <!-- opens another nested subscreen -->
+        <PreferenceScreen
+            android:key="button_voicemail_setting_key"
+            android:title="@string/voicemail_settings"
+            android:persistent="false">
+            ...
+        </PreferenceScreen>
+        <RingtonePreference
+            android:key="button_voicemail_ringtone_key"
+            android:title="@string/voicemail_ringtone_title"
+            android:ringtoneType="notification" ... />
+        ...
+    </PreferenceScreen>
+    ...
+</PreferenceScreen>
+
+ + +

Использование намерений

+ +

В некоторых случаях может потребоваться, чтобы элемент предпочтений открывал другую операцию, а не +экран настроек, например, веб-браузер для просмотра веб-страницы. Чтобы вызвать {@link +android.content.Intent}, когда пользователь выбирает элемент предпочтений, добавьте элемент {@code <intent>} +в качестве дочернего элемента соответствующего элемента {@code <Preference>}.

+ +

Например, здесь показано использование элемента предпочтений для открытия веб-страницы:

+ +
+<Preference android:title="@string/prefs_web_page" >
+    <intent android:action="android.intent.action.VIEW"
+            android:data="http://www.example.com" />
+</Preference>
+
+ +

Вы можете создавать неявные и явные намерения с помощью следующих атрибутов:

+ +
+
{@code android:action}
+
Назначаемое действие, как +в методе {@link android.content.Intent#setAction setAction()}.
+
{@code android:data}
+
Назначаемые данные, как в методе {@link android.content.Intent#setData setData()}.
+
{@code android:mimeType}
+
Назначаемый тип MIME, как +в методе {@link android.content.Intent#setType setType()}.
+
{@code android:targetClass}
+
Часть имени компонента, означающая класс, как в методе {@link android.content.Intent#setComponent +setComponent()}.
+
{@code android:targetPackage}
+
Пакетная часть имени компонента, как в методе {@link +android.content.Intent#setComponent setComponent()}.
+
+ + + +

Создание операции предпочтений

+ +

Для отображения ваших настроек в операции наследуйте класс {@link +android.preference.PreferenceActivity}. Это наследование традиционного класса {@link +android.app.Activity}, который отображает список настроек на основе иерархии объектов {@link +android.preference.Preference}. {@link android.preference.PreferenceActivity} +автоматически сохраняет настройки, связанные с каждым объектом {@link +android.preference.Preference}, когда пользователь вносит изменения.

+ +

Примечание. При разработке приложения для версии Android 3.0 +или выше вместо этого следует использовать {@link android.preference.PreferenceFragment}. Прочитайте следующий раздел +Использование фрагментов предпочтений.

+ +

Запомните самое важное: не загружайте макет отображаемых объектов во время обратного вызова {@link +android.preference.PreferenceActivity#onCreate onCreate()}. Вместо этого вызовите {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} для +добавления предпочтений, объявленных в XML-файле для операции. Например, здесь приведен + минимальный код, необходимый для работы {@link android.preference.PreferenceActivity}:

+ +
+public class SettingsActivity extends PreferenceActivity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.preferences);
+    }
+}
+
+ +

Этого кода действительно достаточно для некоторых приложений, поскольку как только пользователь изменяет предпочтение, +система сохраняет изменения в файле {@link android.content.SharedPreferences} по умолчанию, который +другие компоненты вашего приложения могут читать, когда требуется проверить пользовательские настройки. Однако многим приложениям +требуется немного больше кода, чтобы отслеживать изменения, происходящие с предпочтениями. +Информацию об отслеживании изменений в файле {@link android.content.SharedPreferences} +см. в разделе Чтение предпочтений.

+ + + + +

Использование фрагментов предпочтений

+ +

При разработке приложений для Android 3.0 (API уровня 11) и более поздних версий необходимо использовать {@link +android.preference.PreferenceFragment} для отображения списка +объектов {@link android.preference.Preference}. Вы можете добавить {@link android.preference.PreferenceFragment} в любую операцию, при этом +необязательно использовать {@link android.preference.PreferenceActivity}.

+ +

Фрагменты обеспечивают более +универсальную архитектуру для вашего приложения по сравнению с использованием отдельных операций, вне зависимости от типа +создаваемой операции. Фактически, для управления отображением ваших настроек мы предлагаем вам использовать {@link +android.preference.PreferenceFragment} вместо {@link +android.preference.PreferenceActivity} при каждой возможности.

+ +

Ваша реализация {@link android.preference.PreferenceFragment} может содержать просто +определение метода {@link android.preference.PreferenceFragment#onCreate onCreate()} для загрузки +файла предпочтений посредством {@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()}. Например:

+ +
+public static class SettingsFragment extends PreferenceFragment {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Load the preferences from an XML resource
+        addPreferencesFromResource(R.xml.preferences);
+    }
+    ...
+}
+
+ +

Затем вы можете добавить этот фрагмент в операцию {@link android.app.Activity}, как вы сделали бы это для любого другого фрагмента +{@link android.app.Fragment}. Например:

+ +
+public class SettingsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Display the fragment as the main content.
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, new SettingsFragment())
+                .commit();
+    }
+}
+
+ +

Примечание. Фрагмент {@link android.preference.PreferenceFragment} не содержит +собственного объекта {@link android.content.Context}. Если вам требуется объект {@link android.content.Context}, +вы можете вызвать{@link android.app.Fragment#getActivity()}. Однако разработчик должен быть внимательным и вызывать метод +{@link android.app.Fragment#getActivity()} только в том случае, когда фрагмент прикреплен к операции. Если +фрагмент еще не прикреплен или был откреплен в конце его жизненного цикла, метод {@link +android.app.Fragment#getActivity()} вернет null.

+ + +

Установка значений по умолчанию

+ +

Вероятно, создаваемые вами предпочтения определяют важное поведение вашего приложения, поэтому +необходимо инициализировать соответствующий файл {@link android.content.SharedPreferences}, +записав в него значения по умолчанию для каждого предпочтения {@link android.preference.Preference} при первом запуске вашего +приложения пользователем.

+ +

В первую очередь необходимо указать значение по умолчанию для каждого объекта {@link +android.preference.Preference} +в вашем XML-файле посредством атрибута {@code android:defaultValue}. Значение может относиться к любому +типу данных, подходящему для соответствующего объекта {@link android.preference.Preference}. Например: +

+ +
+<!-- default value is a boolean -->
+<CheckBoxPreference
+    android:defaultValue="true"
+    ... />
+
+<!-- default value is a string -->
+<ListPreference
+    android:defaultValue="@string/pref_syncConnectionTypes_default"
+    ... />
+
+ +

Затем из метода {@link android.app.Activity#onCreate onCreate()} основной операции вашего приложения +(и из любой другой операции, через которую пользователь может войти в ваше приложение +в первый раз) вызовите {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}:

+ +
+PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
+
+ +

Вызов этого метода при выполнении {@link android.app.Activity#onCreate onCreate()} гарантирует, что ваше +приложение правильно инициализируется и получит настройки по умолчанию, которые могут потребоваться вашему приложению +для определенного поведения (например, следует ли загружать данные при работе +в сотовой сети).

+ +

Этот метод имеет три аргумента:

+
    +
  • {@link android.content.Context} вашего приложения.
  • +
  • Идентификатор ресурса для XML-файла предпочтений, для которого вы хотите установить значения по умолчанию.
  • +
  • Логическое значение, которое указывает, требуется ли значения по умолчанию устанавливать более одного раза. +

    При значении false система устанавливает значения по умолчанию только в том случае, если этот метод никогда не вызывался ранее +(или атрибут {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} +в файле общих предпочтений по умолчанию имеет значение false).

  • +
+ +

Когда для третьего аргумента установлено значение false, вы можете вызывать этот метод +при каждом запуске операции, не опасаясь перезаписи сохраненных пользовательских предпочтений из-за их сброса в состояние +по умолчанию. Однако, если установить для этого аргумента значение true, вы будете перезаписывать все предыдущие +значения значениями по умолчанию.

+ + + +

Использование заголовков предпочтений

+ +

В редких случаях может потребоваться такая структура настроек, при которой на первом экране отображается только +список подэкранов (например, как в приложении системных настроек, +показанных на рисунках 4 и 5). При разработке такого дизайна для Android 3.0 и более поздних версий вы должны +использовать новую возможность Android 3.0 — «заголовки», вместо создания подэкранов посредством вложенных +элементов {@link android.preference.PreferenceScreen}.

+ +

Чтобы создать настройки с заголовками, выполните следующие действия:

+
    +
  1. Выделите каждую группу настроек в отдельный экземпляр {@link +android.preference.PreferenceFragment}. Таким образом, каждая группа настроек должна иметь отдельный +XML-файл.
  2. +
  3. Создайте XML-файл заголовков, в котором перечислены все группы настроек и объявления, какой фрагмент +содержит соответствующий список настроек.
  4. +
  5. Наследуйте класс {@link android.preference.PreferenceActivity}, который будет содержать ваши настройки.
  6. +
  7. Реализуйте обратный вызов {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}, чтобы указать +файл заголовков.
  8. +
+ +

Огромное преимущество использования этого дизайна состоит в том, что при запуске на больших экранах {@link android.preference.PreferenceActivity} +автоматически создает макет с двумя панелями, показанный на рисунке 4.

+ +

Даже если ваше приложение поддерживает версии Android старше 3.0, вы можете создать +приложение, использующее {@link android.preference.PreferenceFragment} для двухпанельного представления на +новых устройствах и поддерживающее традиционную многоэкранную иерархию на более старых +устройствах (см. раздел Поддержка старых версий посредством +заголовков предпочтений).

+ + +

Рисунок 4. Двухпанельный макет с заголовками.
1. Заголовки +определяются посредством XML-файла заголовков.
2. Каждая группа настроек определяется с помощью фрагмента +{@link android.preference.PreferenceFragment}, который указывается элементом {@code <header>} +в файле заголовков.

+ + +

Рисунок 5. Смартфон с заголовками настроек. При выборе +пункта вместо заголовков отображается соответствующий +фрагмент {@link android.preference.PreferenceFragment}.

+ + +

Создание файла заголовков

+ +

Каждая группа настроек в вашем списке заголовков указывается отдельным элементом {@code <header>} +внутри корневого элемента {@code <preference-headers>}. Например:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+    <header 
+        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one" />
+    <header 
+        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" >
+        <!-- key/value pairs can be included as arguments for the fragment. -->
+        <extra android:name="someKey" android:value="someHeaderValue" />
+    </header>
+</preference-headers>
+
+ +

Посредством атрибута {@code android:fragment} каждый заголовок объявляет экземпляр фрагмента {@link +android.preference.PreferenceFragment}, который должен открываться при выборе этого заголовка пользователем.

+ +

Элемент {@code <extras>} позволяет передавать пары «ключ-значение» фрагменту в объекте {@link +android.os.Bundle}. Фрагмент может извлекать аргументы путем вызова метода {@link +android.app.Fragment#getArguments()}. Вы можете передавать аргументы фрагменту по различным причинам, +но хорошим поводом является повторное использование одного и того же подкласса {@link +android.preference.PreferenceFragment} для каждой группы и использование аргументов для указания +XML-файла предпочтений, который должен быть загружен фрагментом.

+ +

Например, здесь приведен фрагмент, который можно использовать повторно для нескольких групп настроек, когда каждый +заголовок определяет аргумент {@code <extra>} с ключом {@code "settings"}:

+ +
+public static class SettingsFragment extends PreferenceFragment {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        String settings = getArguments().getString("settings");
+        if ("notifications".equals(settings)) {
+            addPreferencesFromResource(R.xml.settings_wifi);
+        } else if ("sync".equals(settings)) {
+            addPreferencesFromResource(R.xml.settings_sync);
+        }
+    }
+}
+
+ + + +

Отображение заголовков

+ +

Чтобы отобразить заголовки предпочтений, вы должны реализовать метод обратного вызова {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} и вызвать +{@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}. Например:

+ +
+public class SettingsActivity extends PreferenceActivity {
+    @Override
+    public void onBuildHeaders(List<Header> target) {
+        loadHeadersFromResource(R.xml.preference_headers, target);
+    }
+}
+
+ +

Когда пользователь выбирает пункт в списке заголовков, система открывает связанный {@link +android.preference.PreferenceFragment}.

+ +

Примечание. При использовании заголовков предпочтений ваш подкласс {@link +android.preference.PreferenceActivity} не должен реализовывать метод {@link +android.preference.PreferenceActivity#onCreate onCreate()}, поскольку единственной обязательной задачей +операции является загрузка заголовков.

+ + +

Поддержка старых версий с заголовками предпочтений

+ +

Если ваше приложение поддерживает версии Android старше 3.0, вы можете использовать заголовки для +предоставления двухпанельного макета при работе на Android 3.0 или более поздней версии. Достаточно создать +дополнительный XML-файл настроек, использующий базовые элементы {@link android.preference.Preference +<Preference>}, которые ведут себя аналогично пунктам заголовка (для использования в более старых версиях +Android).

+ +

Вместо открытия новых экранов {@link android.preference.PreferenceScreen} каждый из элементов {@link +android.preference.Preference <Preference>} отправляет намерение {@link android.content.Intent} в +{@link android.preference.PreferenceActivity} с указанием XML-файла предпочтений +для загрузки.

+ +

В качестве примера приведен XML-файл для заголовков предпочтений, который используется в Android версии 3.0 +и более поздних версий ({@code res/xml/preference_headers.xml}):

+ +
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+    <header 
+        android:fragment="com.example.prefs.SettingsFragmentOne"
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one" />
+    <header 
+        android:fragment="com.example.prefs.SettingsFragmentTwo"
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" />
+</preference-headers>
+
+ +

А здесь представлен файл предпочтений, который содержит те же самые заголовки для версий старше +Android 3.0 ({@code res/xml/preference_headers_legacy.xml}):

+ +
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <Preference 
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one"  >
+        <intent 
+            android:targetPackage="com.example.prefs"
+            android:targetClass="com.example.prefs.SettingsActivity"
+            android:action="com.example.prefs.PREFS_ONE" />
+    </Preference>
+    <Preference 
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" >
+        <intent 
+            android:targetPackage="com.example.prefs"
+            android:targetClass="com.example.prefs.SettingsActivity"
+            android:action="com.example.prefs.PREFS_TWO" />
+    </Preference>
+</PreferenceScreen>
+
+ +

Так как поддержка {@code <preference-headers>} была добавлена в версии Android 3.0, система вызывает +{@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} в методе {@link +android.preference.PreferenceActivity} только при работе в Android версии 3.0 или более поздней версии. Чтобы загрузить +«старый» файл заголовков ({@code preference_headers_legacy.xml}), вы должны проверить версию Android +и, если версия старше Android 3.0 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}), вызвать {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} для +загрузки старого файла заголовков. Например:

+ +
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    ...
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+        // Load the legacy preferences headers
+        addPreferencesFromResource(R.xml.preference_headers_legacy);
+    }
+}
+
+// Called only on Honeycomb and later
+@Override
+public void onBuildHeaders(List<Header> target) {
+   loadHeadersFromResource(R.xml.preference_headers, target);
+}
+
+ +

Остается обработать намерение {@link android.content.Intent}, переданное +в операцию, чтобы идентифицировать файл предпочтений для загрузки. Поэтому извлеките операцию намерения и сравните ее +с известными строками действия, которые вы использовали в тегах {@code <intent>} XML-файла предпочтений:

+ +
+final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
+...
+
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    String action = getIntent().getAction();
+    if (action != null && action.equals(ACTION_PREFS_ONE)) {
+        addPreferencesFromResource(R.xml.preferences);
+    }
+    ...
+
+    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+        // Load the legacy preferences headers
+        addPreferencesFromResource(R.xml.preference_headers_legacy);
+    }
+}
+
+ +

При этом помните, что последующие вызовы {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} будут помещать +все предпочтения в один список, поэтому обязательно используйте операторы else-if, чтобы обеспечить только +однократный вызов метода при изменении условий.

+ + + + + +

Чтение предпочтений

+ +

По умолчанию все предпочтения вашего приложения сохраняются в файле, который доступен из любого места +вашего приложения посредством вызова статического метода {@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()}. Он возвращает объект {@link +android.content.SharedPreferences}, содержащий все пары «ключ-значение», связанные +с объектами {@link android.preference.Preference}, использованными в вашей операции {@link +android.preference.PreferenceActivity}.

+ +

В качестве примера показано чтение одного из значений предпочтений из любой другой операции в вашем +приложении:

+ +
+SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
+
+ + + +

Отслеживание изменений предпочтений

+ +

Существует несколько причин, по которым вы можете захотеть получать уведомления, как только пользователь изменяет одно из +предпочтений. Чтобы получать обратный вызов при изменении любого из предпочтений, +реализуйте интерфейс {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} и зарегистрируйте приемник для объекта +{@link android.content.SharedPreferences} посредством вызова {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}.

+ +

Этот интерфейс содержит только один метод обратного вызова, {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()}, и вы, вероятно, сочтете его самым простым способом реализации интерфейса в составе своей +операции. Например:

+ +
+public class SettingsActivity extends PreferenceActivity
+                              implements OnSharedPreferenceChangeListener {
+    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
+    ...
+
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+        String key) {
+        if (key.equals(KEY_PREF_SYNC_CONN)) {
+            Preference connectionPref = findPreference(key);
+            // Set summary to be the user-description for the selected value
+            connectionPref.setSummary(sharedPreferences.getString(key, ""));
+        }
+    }
+}
+
+ +

В этом примере метод проверяет, выполнено ли изменение настройки для известного ключа предпочтений. Он +вызывает {@link android.preference.PreferenceActivity#findPreference findPreference()} для получения объекта +{@link android.preference.Preference}, который был изменен, поэтому он может изменить сводку пункта +, описывающего выбор пользователя. То есть, когда настройка представляет собой {@link +android.preference.ListPreference} или другую настройку с несколькими вариантами выбора, при изменении этой настройки вы должны вызвать {@link +android.preference.Preference#setSummary setSummary()} для отображения +текущего состояния (например, настройка спящего режима, показанная на рисунке 5).

+ +

Примечание. В соответствии с рекомендациями раздела Настройки руководства «Дизайн для Android», мы рекомендуем вам обновлять +сводку для {@link android.preference.ListPreference} при каждом изменении предпочтения пользователем, +чтобы описать текущую настройку.

+ +

Для правильного управления жизненным циклом в операции мы рекомендуем вам регистрировать или отменять регистрацию +вашего приемника {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener} во время выполнения обратных вызовов {@link +android.app.Activity#onResume} и {@link android.app.Activity#onPause} соответственно:

+ +
+@Override
+protected void onResume() {
+    super.onResume();
+    getPreferenceScreen().getSharedPreferences()
+            .registerOnSharedPreferenceChangeListener(this);
+}
+
+@Override
+protected void onPause() {
+    super.onPause();
+    getPreferenceScreen().getSharedPreferences()
+            .unregisterOnSharedPreferenceChangeListener(this);
+}
+
+ +

Внимание! Когда вы вызываете приемник {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}, диспетчер предпочтений не +сохраняет строгую ссылку на приемник. Вы должны сохранить строгую +ссылку на приемник, в противном случае она будет чувствительной к очистке памяти. Мы +рекомендуем хранить ссылку на приемник в данных экземпляра объекта +, который будет существовать, пока вам нужен приемник.

+ +

Например, в следующем коде вызывающий объект не сохраняет +ссылку на приемник. В результате этого приемник будет удален при очистке памяти +и через некоторое время приведет к сбою:

+ +
+prefs.registerOnSharedPreferenceChangeListener(
+  // Bad! The listener is subject to garbage collection!
+  new SharedPreferences.OnSharedPreferenceChangeListener() {
+  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+    // listener implementation
+  }
+});
+
+ +

Вместо этого сохраните ссылку на приемник в поле данных экземпляра объекта +, который будет существовать, пока нужен приемник:

+ +
+SharedPreferences.OnSharedPreferenceChangeListener listener =
+    new SharedPreferences.OnSharedPreferenceChangeListener() {
+  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+    // listener implementation
+  }
+};
+prefs.registerOnSharedPreferenceChangeListener(listener);
+
+ +

Контроль использования сети

+ + +

Начиная с версии Android 4.0, системное приложение «Настройки» позволяет пользователям просматривать использование +сетевых данных приложениями, работающими на переднем плане и в фоновом режиме. После этого пользователи могут +отключить использование данных в фоновом режиме для отдельных приложений. Для того, чтобы пользователи не отключали доступ вашего приложения к данным +в фоновом режиме, вы должны эффективно использовать подключение в режиме передачи данных и предоставить +пользователям возможность настройки использования данных вашим приложением посредством настроек приложения.

+ +

Например, вы можете позволить пользователям управлять частотой синхронизации данных приложения, выполнением загрузки +только в режиме подключения по Wi-Fi, использованием данных в роуминге и т. д. Когда +эти возможности управления доступны, пользователи с меньшей вероятностью отключат доступ вашего приложения к данным, +когда оно достигает установленных в системных настройках лимитов, поскольку вместо отключения они могут +точно контролировать объем данных, который использует ваше приложение.

+ +

После добавления необходимых предпочтений в вашу операцию {@link android.preference.PreferenceActivity} +для управления поведением вашего приложения в отношении данных вы должны добавить фильтр намерений для {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} в вашем файле манифеста. Например:

+ +
+<activity android:name="SettingsActivity" ... >
+    <intent-filter>
+       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+       <category android:name="android.intent.category.DEFAULT" />
+    </intent-filter>
+</activity>
+
+ +

Этот фильтр манифеста указывает системе, что эта операция управляет использованием +данных вашим приложением. Так, когда пользователь проверяет объем использованных приложением данных в системном приложении +«Настройки», отображается кнопка Просмотреть настройки приложения, которая запускает вашу операцию +{@link android.preference.PreferenceActivity}, чтобы пользователь мог уточнить, сколько данных использует +ваше приложение.

+ + + + + + + +

Построение пользовательского предпочтения

+ +

Система Android содержит множество подклассов {@link android.preference.Preference}, которые +позволяют вам строить пользовательский интерфейс для нескольких различных типов настроек. +Тем не менее, вы можете обнаружить, что для нужной вам настройки нет встроенного решения, например, для выбора +числа или даты. В таком случае вам потребуется создать нестандартное предпочтение путем наследования +класса {@link android.preference.Preference} или одного из других подклассов.

+ +

При наследовании класса {@link android.preference.Preference} нужно выполнить несколько +важных пунктов:

+ +
    +
  • Укажите пользовательский интерфейс, который должен отображаться при выборе этой настройки пользователем.
  • +
  • При необходимости сохраните значение настройки.
  • +
  • Инициализируйте {@link android.preference.Preference} текущим значением (или значением по умолчанию), +когда предпочтение отображается.
  • +
  • Укажите значение по умолчанию в ответ на запрос системы.
  • +
  • Если {@link android.preference.Preference} содержит свой собственный пользовательский интерфейс (например, диалоговое окно), сохраните +и восстановите состояние для обработки изменений жизненного цикла (например, когда пользователь поворачивает экран).
  • +
+ +

В следующих разделах описано выполнение каждой из этих задач.

+ + + +

Указание пользовательского интерфейса

+ +

Если вы наследуете класс {@link android.preference.Preference} непосредственно, вы должны реализовать метод +{@link android.preference.Preference#onClick()}, чтобы задать действие, происходящее при выборе +пункта пользователем. Однако большая часть нестандартных настроек наследует {@link android.preference.DialogPreference}, чтобы +отобразить диалоговое окно, что упрощает процедуру. Когда вы наследуете {@link +android.preference.DialogPreference}, вы должны вызвать {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()}, находясь в конструкторе +класса, чтобы указать макет диалогового окна.

+ +

В качестве примера показан конструктор нестандартного диалогового окна {@link +android.preference.DialogPreference}, в котором объявляется макет и указывается текст для +положительной и отрицательной кнопок диалога по умолчанию:

+ +
+public class NumberPickerPreference extends DialogPreference {
+    public NumberPickerPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        setDialogLayoutResource(R.layout.numberpicker_dialog);
+        setPositiveButtonText(android.R.string.ok);
+        setNegativeButtonText(android.R.string.cancel);
+        
+        setDialogIcon(null);
+    }
+    ...
+}
+
+ + + +

Сохранение значения настройки

+ +

Вы можете сохранить значение настройки в любой момент, вызвав один из методов {@code persist*()} класса {@link +android.preference.Preference}, например, {@link +android.preference.Preference#persistInt persistInt()}, если настройка имеет целое значение, или +{@link android.preference.Preference#persistBoolean persistBoolean()} для сохранения логического значения.

+ +

Примечание. Каждое предпочтение {@link android.preference.Preference} может сохранять только один +тип данных, поэтому вы должны использовать метод {@code persist*()}, соответствующий типу данных, используемых вашим +пользовательским предпочтением {@link android.preference.Preference}.

+ +

Выбор метода сохранения настройки может зависеть от наследованного класса {@link +android.preference.Preference}. Если вы наследуете {@link +android.preference.DialogPreference}, вы должны сохранять значение только при закрытии диалога +с положительным результатом (пользователь нажал кнопку «OK»).

+ +

Когда {@link android.preference.DialogPreference} закрывается, система вызывает метод {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()}. Этот метод содержит +логический аргумент, который указывает, является ли результат пользователя «положительным» — если аргумент имеет значение +true, значит пользователь выбрал положительную кнопку и вы должны сохранить новое значение. Например: +

+ +
+@Override
+protected void onDialogClosed(boolean positiveResult) {
+    // When the user selects "OK", persist the new value
+    if (positiveResult) {
+        persistInt(mNewValue);
+    }
+}
+
+ +

В этом примере mNewValue — это член класса, который содержит текущее значение +настройки. При вызове {@link android.preference.Preference#persistInt persistInt()} значение сохраняется +в файл {@link android.content.SharedPreferences} (с автоматическим использованием ключа, +указанного в XML-файле для этого предпочтения {@link android.preference.Preference}).

+ + +

Инициализация текущего значения

+ +

Когда система добавляет ваше предпочтение {@link android.preference.Preference} на экран, она +вызывает метод {@link android.preference.Preference#onSetInitialValue onSetInitialValue()}, чтобы уведомить вас, +имеет ли настройка сохраненное значение. Если сохраненного значения нет, этот вызов предоставляет вам +значение по умолчанию.

+ +

Метод {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} передает +логическое значение, restorePersistedValue, чтобы показать, было ли уже сохранено значение +для настройки. Если значение равно true, вы должны извлечь сохраненное значение, вызвав +один из методов {@code getPersisted*()} класса {@link +android.preference.Preference}, например, {@link +android.preference.Preference#getPersistedInt getPersistedInt()} для целого значения. Обычно +требуется извлечь сохраненное значение, чтобы можно было правильно обновить пользовательский интерфейс для отражения +ранее сохраненного значения.

+ +

Если restorePersistedValue имеет значение false, вы +должны использовать значение по умолчанию, которое передается во втором аргументе.

+ +
+@Override
+protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
+    if (restorePersistedValue) {
+        // Restore existing state
+        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
+    } else {
+        // Set default state from the XML attribute
+        mCurrentValue = (Integer) defaultValue;
+        persistInt(mCurrentValue);
+    }
+}
+
+ +

Каждый метод {@code getPersisted*()} содержит аргумент, который указывает +значение по умолчанию на случай, когда действительно нет сохраненного значения, или не существует ключ. В +приведенном выше примере локальная константа служит для указания значения по умолчанию на случай, если {@link +android.preference.Preference#getPersistedInt getPersistedInt()} не может вернуть сохраненное значение.

+ +

Внимание! Вы не можете использовать +defaultValue в качестве значения по умолчанию в методе {@code getPersisted*()}, так как +его значение всегда равно null, когда restorePersistedValue имеет значение true.

+ + +

Предоставление значения по умолчанию

+ +

Если экземпляр вашего класса {@link android.preference.Preference} указывает значение по умолчанию +(с помощью атрибута {@code android:defaultValue}), +система вызывает {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()}, когда она создает экземпляр объекта для извлечения значения. Вы должны реализовать +этот метод, чтобы сохранить значение по умолчанию для системы в {@link +android.content.SharedPreferences}. Например:

+ +
+@Override
+protected Object onGetDefaultValue(TypedArray a, int index) {
+    return a.getInteger(index, DEFAULT_VALUE);
+}
+
+ +

Аргументы метода предоставляют все необходимое: массив атрибутов и указатель +положения {@code android:defaultValue}, который вы должны извлечь. Причина, по которой вы должны +реализовать этот метод, чтобы извлечь значение по умолчанию из атрибута, состоит в том, что вы должны указать +локальное значение по умолчанию для атрибута в случае, когда значение не определено.

+ + + +

Сохранение и восстановление состояния предпочтений

+ +

Как и {@link android.view.View} в макете, ваш подкласс {@link android.preference.Preference} +отвечает за сохранение и восстановление своего состояния в случае перезапуска операции или фрагмента +(например, когда пользователь поворачивает экран). Чтобы правильно сохранять и восстанавливать +состояние вашего класса {@link android.preference.Preference}, вы должны реализовать +методы обратного вызова жизненного цикла {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} и {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}.

+ +

Состояние вашего {@link android.preference.Preference} определяется объектом, который реализует +интерфейс {@link android.os.Parcelable}. Система Android предоставляет вам такой объект +в качестве начальной точки для определения вашего объекта состояния: класс {@link +android.preference.Preference.BaseSavedState}.

+ +

Чтобы определить, как ваш класс {@link android.preference.Preference} сохраняет свое состояние, вы должны +наследовать класс {@link android.preference.Preference.BaseSavedState}. Вы должны переопределить лишь + несколько методов и определить объект +{@link android.preference.Preference.BaseSavedState#CREATOR}.

+ +

Для большинства приложений вы можете скопировать следующую реализацию и просто изменить строки, которые +обрабатывают {@code value}, если ваш подкласс {@link android.preference.Preference} сохраняет типы +данных, отличные от целых.

+ +
+private static class SavedState extends BaseSavedState {
+    // Member that holds the setting's value
+    // Change this data type to match the type saved by your Preference
+    int value;
+
+    public SavedState(Parcelable superState) {
+        super(superState);
+    }
+
+    public SavedState(Parcel source) {
+        super(source);
+        // Get the current preference's value
+        value = source.readInt();  // Change this to read the appropriate data type
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        // Write the preference's value
+        dest.writeInt(value);  // Change this to write the appropriate data type
+    }
+
+    // Standard creator object using an instance of this class
+    public static final Parcelable.Creator<SavedState> CREATOR =
+            new Parcelable.Creator<SavedState>() {
+
+        public SavedState createFromParcel(Parcel in) {
+            return new SavedState(in);
+        }
+
+        public SavedState[] newArray(int size) {
+            return new SavedState[size];
+        }
+    };
+}
+
+ +

После добавления показанной выше реализации {@link android.preference.Preference.BaseSavedState} +в ваше приложение (обычно в качестве подкласса вашего подкласса {@link android.preference.Preference}), вам +потребуется реализовать методы {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} и {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} для вашего +подкласса {@link android.preference.Preference}.

+ +

Например:

+ +
+@Override
+protected Parcelable onSaveInstanceState() {
+    final Parcelable superState = super.onSaveInstanceState();
+    // Check whether this Preference is persistent (continually saved)
+    if (isPersistent()) {
+        // No need to save instance state since it's persistent,
+        // use superclass state
+        return superState;
+    }
+
+    // Create instance of custom BaseSavedState
+    final SavedState myState = new SavedState(superState);
+    // Set the state's value with the class member that holds current
+    // setting value
+    myState.value = mNewValue;
+    return myState;
+}
+
+@Override
+protected void onRestoreInstanceState(Parcelable state) {
+    // Check whether we saved the state in onSaveInstanceState
+    if (state == null || !state.getClass().equals(SavedState.class)) {
+        // Didn't save the state, so call superclass
+        super.onRestoreInstanceState(state);
+        return;
+    }
+
+    // Cast state to custom BaseSavedState and pass to superclass
+    SavedState myState = (SavedState) state;
+    super.onRestoreInstanceState(myState.getSuperState());
+    
+    // Set this Preference's widget to reflect the restored state
+    mNumberPicker.setValue(myState.value);
+}
+
+ diff --git a/docs/html-intl/intl/ru/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/ru/guide/topics/ui/ui-events.jd new file mode 100644 index 0000000000000000000000000000000000000000..cd2b4813cf83461610203b1e8ea8f6647e5b9677 --- /dev/null +++ b/docs/html-intl/intl/ru/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=События ввода +parent.title=Пользовательский интерфейс +parent.link=index.html +@jd:body + + + +

В системе Android предусмотрено несколько способов перехвата событий взаимодействия пользователя с вашим приложением. +Что касается событий в пользовательском интерфейсе, подход состоит в том, чтобы захватывать события из +определенного представления объекта, с которым взаимодействует пользователь. Класс отображаемых объектов (View) содержит средства, позволяющие делать это.

+ +

В различных классах View, которые вы будете использовать для создания макета, вы заметите несколько общедоступных методов обратного вызова, +полезных для работы с событиями пользовательского интерфейса. Эти методы вызываются платформой Android, когда +с этим объектом выполняется соответствующее действие. Например, когда пользователь касается отображаемого объекта (кнопки), +вызывается метод onTouchEvent() этого объекта. Однако для перехвата этого события вы должны +наследовать класс и переопределить метод. Однако наследование каждого отображаемого объекта +для обработки такого события было бы неудобно. Именно поэтому класс View также содержит +набор вложенных интерфейсов с обратными вызовами, которые можно задать значительно проще. Эти интерфейсы, +которые называются приемниками событий, и служат перехватчиками действий пользователя с вашим пользовательским интерфейсом.

+ +

Несмотря на то, что вы будете чаще использовать приемники событий для перехвата действий пользователя, может +наступить момент, когда вам не захочется наследовать класс View, чтобы создать нестандартный компонент. +Возможно, вы захотите наследовать класс {@link android.widget.Button}, +чтобы сделать нечто более необычное. В этом случае вы сможете определить поведение события по умолчанию для своего +класса с помощью обработчиков событий класса.

+ + +

Приемники событий

+ +

Приемник событий — это интерфейс в классе {@link android.view.View}, который содержит один +метод обратного вызова. Эти методы будут вызываться платформой Android, когда в результате взаимодействия пользователя с объектом +пользовательского интерфейса срабатывает отображаемый объект View, в котором зарегистрирован приемник.

+ +

Интерфейсы, включенные в приемник события, представляют собой следующие методы обратного вызова:

+ +
+
onClick()
+
Из объекта {@link android.view.View.OnClickListener}. + Этот метод вызывается, когда пользователь касается элемента + (в режиме касания), или переводит фокус на элемент с помощью клавиш перемещения или трекбола и +нажимает соответствующую клавишу «ввода» или нажимает на трекбол.
+
onLongClick()
+
Из объекта {@link android.view.View.OnLongClickListener}. + Этот метод вызывается, когда пользователь касается элемента и удерживает его (в режиме касания), +или переводит фокус на элемент с помощью клавиш перемещения или трекбола и +нажимает и удерживает соответствующую клавишу «ввода» или трекбол (в течение одной секунды).
+
onFocusChange()
+
Из объекта {@link android.view.View.OnFocusChangeListener}. + Этот метод вызывается, когда пользователь перемещается в элемент или из него с помощью клавиш перемещения или трекбола.
+
onKey()
+
Из объекта {@link android.view.View.OnKeyListener}. + Этот метод вызывается, когда пользователь переносит фокус на элемент и нажимает или отпускает аппаратную клавишу на устройстве.
+
onTouch()
+
Из объекта {@link android.view.View.OnTouchListener}. + Этот метод вызывается, когда пользователь выполняет действие, считающееся событием касания, например, нажимает, отпускает +или выполняет любой жест на экране (в пределах границ элемента).
+
onCreateContextMenu()
+
Из объекта {@link android.view.View.OnCreateContextMenuListener}. +Этот метод вызывается, когда создается контекстное меню (в результате длительного «длительного нажатия»). См. обсуждение +контекстных меню в руководстве +для разработчиков Меню.
+
+ +

Эти методы являются единственными составными частями соответствующих интерфейсов. Чтобы определить один из этих методов +и обрабатывать события, реализуйте вложенный интерфейс в вашем процесс или определите его, как анонимный класс. +Затем передайте экземпляр реализации +в соответствующий метод View.set...Listener(). (Например, вызовите +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()} +и передайте ему свою реализацию {@link android.view.View.OnClickListener OnClickListener}.)

+ +

В следующем примере показано, как зарегистрировать приемник события «по клику» (on-click) для кнопки.

+ +
+// Create an anonymous implementation of OnClickListener
+private OnClickListener mCorkyListener = new OnClickListener() {
+    public void onClick(View v) {
+      // do something when the button is clicked
+    }
+};
+
+protected void onCreate(Bundle savedValues) {
+    ...
+    // Capture our button from layout
+    Button button = (Button)findViewById(R.id.corky);
+    // Register the onClick listener with the implementation above
+    button.setOnClickListener(mCorkyListener);
+    ...
+}
+
+ +

Возможно, еще удобнее реализовать OnClickListener как часть вашей операции. +При этом исключается дополнительная нагрузка класса и выделение объекта. Например:

+
+public class ExampleActivity extends Activity implements OnClickListener {
+    protected void onCreate(Bundle savedValues) {
+        ...
+        Button button = (Button)findViewById(R.id.corky);
+        button.setOnClickListener(this);
+    }
+
+    // Implement the OnClickListener callback
+    public void onClick(View v) {
+      // do something when the button is clicked
+    }
+    ...
+}
+
+ +

Обратите внимание, что обратный вызов onClick() в примере выше не +содержит возвращаемого значения, но некоторые другие методы приемника события должны возвращать логическое значение. Причина +зависит от события. Для некоторых методов, которые возвращают значения, причина описана ниже:

+
    +
  • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()} — +этот метод возвращает логическое значение, указывающее, что вы обработали это событие и его более не следует хранить. +А именно, верните значение true, чтобы указать, что вы обработали событие и его следует остановить; +верните значение false, если вы не обработали его и/или событие должно продолжаться для любых других +приемников события on-click.
  • +
  • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()} — +этот метод возвращает логическое значение, указывающее, что вы обработали это событие и его более не следует хранить. + А именно, верните значение true, чтобы указать, что вы обработали событие и его следует остановить; +верните значение false, если вы не обработали его и/или событие должно продолжаться для любых других +приемников события on-click.
  • +
  • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()} — +этот метод возвращает логическое значение, указывающее, обработал ли ваш приемник это событие. Важно, что +это событие может повлечь несколько действий, следующих друг за другом. Поэтому, если вы возвращаете ложь при приеме + события нажатия, вы указываете, что вы не обработали событие и не +интересуетесь последующими действиями в результате этого события. Соответственно, этот метод не будет вызываться для любых других действий +в рамках этого события, таких как жесты или возможное действие отпускания.
  • +
+ +

Помните, что события аппаратных клавиш всегда попадают в отображаемый объект View, который находится в фокусе. Они отправляются, начиная с верха +в иерархии отображаемых объектов, затем ниже, пока не достигнут соответствующего назначения. Если ваш отображаемый объект (или дочерний объект вашего отображаемого объекта) +находится в фокусе, вы можете видеть, как событие перемещается сквозь метод {@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()}. В качестве альтернативы к перехвату событий клавиш через отображаемый объект View можно также получать +все события внутри вашей операции с помощью методов {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}{@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}.

+ +

Кроме того, размышляя о вводе текста для приложения, помните, что многие устройства поддерживают только программные методы +ввода. Такие методы не обязательно основаны на нажатиях клавиш. Некоторые могут использовать голосовой ввод, рукописный ввод и т. д. Даже если +метод ввода представлен интерфейсом, подобным клавиатуре, он обычно не приводит к запуску семейства +событий{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}. Не следует создавать +пользовательский интерфейс, для управления которым требуется нажимать определенные клавиши, кроме случаев, когда вы хотите ограничить использование приложения только устройствами +с аппаратной клавиатурой. В частности, не полагайтесь на эти методы для подтверждения ввода, когда пользователь нажимает +клавишу «Ввод». Вместо этого используйте такие действия, как {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE}, чтобы подать методу ввода сигнал +о том, какой реакции ожидает приложение, чтобы он мог соответственно изменить свой пользовательский интерфейс. Избегайте предположений +о работе программного метода ввода и доверьте ему ввод уже отформатированного текста в ваше приложение.

+ +

Примечание. Система Android сначала будет вызывать обработчики событий, а затем соответствующие обработчики по умолчанию +из определения класса. Поэтому возврат значения истина из этих приемников событий будет останавливать +передачу события остальным приемникам событий, а также будет блокировать обратный вызов +обработчика событий по умолчанию в отображаемом объекте. Поэтому проверьте, что вы хотите прервать событие, когда вы возвращаете значение true.

+ + +

Обработчики событий

+ +

Если вы создаете нестандартный компонент из отображаемого объекта, вы сможете определить несколько методов обратного вызова, +используемых как обработчики события по умолчанию. +В документе Нестандартные +компоненты можно узнать о некоторых общих обратных вызовах, используемых для обработки событий, +включая следующие:

+
    +
  • {@link android.view.View#onKeyDown} — вызывается при возникновении нового события клавиши.
  • +
  • {@link android.view.View#onKeyUp} — вызывается при возникновении события отпускания клавиши.
  • +
  • {@link android.view.View#onTrackballEvent} — вызывается при возникновении события перемещения трекбола.
  • +
  • {@link android.view.View#onTouchEvent} — вызывается при возникновении события жеста на сенсорном экране.
  • +
  • {@link android.view.View#onFocusChanged} — вызывается, когда отображаемый объект получает или теряет фокус.
  • +
+

Существует несколько других методов, о которых следует знать и которые не являются частью класса View, +но могут напрямую влиять на доступные вам способы обработки событий. Поэтому, управляя более сложными событиями внутри +макета, учитывайте и другие методы:

+
    +
  • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)} — этот метод позволяет вашей операции {@link + android.app.Activity} перехватывать все события касаний перед их отправкой в окно.
  • +
  • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)} — этот метод позволяет объекту {@link + android.view.ViewGroup} просматривать события перед их отправкой в дочерние отображаемые объекты.
  • +
  • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)} — вызовите этот метод +в родительском отображаемом объекте, чтобы указать ему, что он не должен перехватывать события касания с помощью {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)}.
  • +
+ +

Режим касания

+

+Когда пользователь перемещается по пользовательскому интерфейсу с помощью клавиш перемещения или трекбола, необходимо +передавать фокус действующим элементам (таким как кнопки), чтобы пользователь мог видеть, +какой элемент будет принимать ввод. Однако, если устройство поддерживает сенсорный ввод, и пользователь +начинает взаимодействовать с интерфейсом, прикасаясь к его элементам, исчезает необходимость +выделять элементы или передавать фокус определенному отображаемому объекту. Следовательно, существует режим +взаимодействия, который называется «режимом касания». +

+

+Как только пользователь касается экрана, устройство, поддерживающее сенсорный ввод, +переходит в режим касания. Начиная с этого момента, фокус передается только тем отображаемым объектам, для которых +{@link android.view.View#isFocusableInTouchMode} имеет значение true, таким как виджеты редактирования текста. +Другие отображаемые объекты, которых можно коснуться, например, кнопки, не будут получать фокус при касании. Нажатие будет +просто запускать их приемники событий on-click. +

+

+В любой момент, когда пользователь нажимает клавишу перемещения или выполняет прокрутку трекболом, устройство +выходит из режима касания и находит отображаемый объект, которому передается фокус. Теперь пользователь может возобновить взаимодействие +с пользовательским интерфейсом без касания экрана. +

+

+Состояние режима касания поддерживается во всей системе (для всех окон и операций). +Чтобы узнать текущее состояние, можно вызвать +{@link android.view.View#isInTouchMode} и посмотреть, находится ли устройство в режиме касания. +

+ + +

Фокус обработки

+ +

В ответ на пользовательский ввод система обрабатывает обычное перемещение фокуса. +Сюда относится изменение фокуса, когда отображаемые объекты удаляются или скрываются, а также когда становятся доступными +новые отображаемые объекты. Отображаемые объекты сообщают о своей готовности получить фокус +с помощью метода {@link android.view.View#isFocusable()}. Чтобы изменить способность объекта View получать +фокус, вызовите {@link android.view.View#setFocusable(boolean) setFocusable()}. В режиме касания +можно узнать способность отображаемого объекта View получать фокус с помощью метода {@link android.view.View#isFocusableInTouchMode()}. +Для изменения вызовите {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()}. +

+ +

Перемещение фокуса основано на алгоритме, который находит ближайший элемент +в заданном направлении. В редких случаях алгоритм по умолчанию может не совпадать +с поведением, которое предполагает разработчик. В этих ситуациях можно задать +явное переопределение с помощью следующих атрибутов XML в файле макета: +nextFocusDown, nextFocusLeft, nextFocusRightи +nextFocusUp. Добавьте один из этих атрибутов в отображаемый объект View, из которого +выходит фокус. Задайте значение атрибута для идентификатора объекта View, +в который следует передать фокус. Например:

+
+<LinearLayout
+    android:orientation="vertical"
+    ... >
+  <Button android:id="@+id/top"
+          android:nextFocusUp="@+id/bottom"
+          ... />
+  <Button android:id="@+id/bottom"
+          android:nextFocusDown="@+id/top"
+          ... />
+</LinearLayout>
+
+ +

Обычно в этом вертикальном макете перемещение вверх из первой кнопки не должно приводить к перемещению, +как и перемещение вниз из второй кнопки. Теперь, когда верхняя кнопка +задает для нижней кнопки атрибут nextFocusUp (и наоборот), фокус будет перемещаться +циклически сверху вниз и снизу вверх.

+ +

Если вы хотите объявить, что отображаемый объект в вашем пользовательском интерфейсе способен получать фокус (тогда как обычно он не может получать фокус), +добавьте XML-атрибут android:focusable в объект View в объявлении макета. +Установите для него значение true. Можно также объявить объект View, +способным получать фокус в режиме касания с помощью android:focusableInTouchMode.

+

Чтобы запросить передачу фокуса определенному отображаемому объекту, вызовите {@link android.view.View#requestFocus()}.

+

Чтобы перехватывать события фокуса (получать уведомления, когда отображаемый объект получает или теряет фокус), используйте метод +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}, +который обсуждается в разделе Приемники событий выше.

+ + + + diff --git a/docs/html-intl/intl/ru/index.jd b/docs/html-intl/intl/ru/index.jd index 3da0b9e23360ee9a2e6bb778fb76b4e81987dcb9..e917a8dba7893905a907f589fba7ef6a00253c7a 100644 --- a/docs/html-intl/intl/ru/index.jd +++ b/docs/html-intl/intl/ru/index.jd @@ -5,43 +5,81 @@ page.customHeadTag= -
--> - +
-
+ + + -
+

Build Beautiful Apps

Resources to get you started with designing and developing for Android. diff --git a/docs/html-intl/intl/ru/preview/api-overview.jd b/docs/html-intl/intl/ru/preview/api-overview.jd deleted file mode 100644 index aa4a057b0be992baaaa8019ebabae21c914ad01a..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/api-overview.jd +++ /dev/null @@ -1,521 +0,0 @@ -page.title=Обзор API-интерфейсов -page.keywords=предварительная версия,пакет sdk,совместимость -page.tags=previewresources, androidm -sdk.platform.apiLevel=22-mnc -page.image=images/cards/card-api-overview_16-9_2x.png -@jd:body - - - - -

M Developer Preview — ознакомительная версия предстоящего выпуска платформы Android, -в котором реализованы новые возможности как для пользователей, так и для -разработчиков приложений. В этой статье мы расскажем о наиболее интересных API-интерфейсах.

- -

Версия M Developer Preview предназначена для тех, кто хочет первым ознакомиться с новой платформой, -а также для тестировщиков. M Developer Preview — прекрасная возможность -повлиять на вектор развития -платформы Android. -Мы с нетерпением ждем ваших отзывов!

- -

Внимание! Не публикуйте в магазине Google Play приложения, -предназначенные для M Developer Preview.

- -

Примечание. В этой статье часто упоминаются классы и методы, -для которых на сайте developer.android.com пока еще нет справочных материалов. Такие элементы API-интерфейса обозначаются здесь следующим образом: -{@code code style} (без гиперссылок). Чтобы ознакомиться с предварительной документацией по этим элементам, -загрузите справочное руководство по предварительной версии.

- -

Важные изменения в работе приложений

- -

Если вы ранее публиковали приложения для Android, то примите во внимание, что изменения в платформе могут повлиять -на работу опубликованных приложений.

- -

Подробные сведения представлены в статье Изменения в работе.

- -

Связывание приложений

-

В M Preview улучшена система намерений Android за счет более эффективного связывания приложений. -Теперь у вас есть возможность связывать приложения с вашими собственными веб-доменами. Благодаря этому -платформа может сама, без вмешательства пользователя, определить приложение, которое будет использоваться по умолчанию -для обработки определенной веб-ссылки. О том, как реализовать такую возможность, можно почитать в статье -Связывание приложений. - -

Автоматическое резервное копирование для приложений

-

Теперь система автоматически выполняет полное резервное копирование и восстановление данных ваших приложений. Для приложений, -написанных под M Preview, эта функция включена по умолчанию — вам даже не нужно добавлять отдельный код. Если пользователь решит удалить свою учетную запись Google, все резервные копии его данных также будут удалены. - О том, как работает эта функция и как настроить параметры резервного копирования элементов файловой системы, - рассказано в статье -Автоматическое резервное копирование для приложений.

- -

Авторизация

-

В M Preview представлены новые API-интерфейсы, которые позволяют авторизовать пользователей по отпечатку пальца -(на устройствах, поддерживающих такую возможность), а также подтвердить учетные данные, если пользователь недавно авторизовался через механизмы разблокировки устройства -(например, вводил пароль для разблокировки экрана). Эти API-интерфейсы рекомендуется использовать совместно с -системой хранилища ключей Android.

- -

Авторизация по отпечатку пальца

- -

Чтобы авторизовать пользователя по отпечатку пальца, получите экземпляр нового класса -{@code android.hardware.fingerprint.FingerprintManager} и вызовите метод -{@code FingerprintManager.authenticate()}. Эта функция доступна для совместимых -устройств, оснащенных сканером отпечатков пальцев. Прежде всего, вам необходимо реализовать в своем приложении пользовательский интерфейс проверки -подлинности по отпечатку пальца, а также использовать для него стандартный значок отпечатка пальца Android. -Этот значок ({@code c_fp_40px.png}) вы можете найти в -примере приложения. При разработке нескольких приложений, использующих функцию авторизации по отпечатку пальца, -помните, что каждое из них должно самостоятельно выполнять проверку подлинности. -

- -

Чтобы реализовать эту функцию, сначала добавьте в файл манифеста разрешение {@code USE_FINGERPRINT}. -

- -
-<uses-permission
-        android:name="android.permission.USE_FINGERPRINT" />
-
- - - -

Пример такой проверки подлинности вы найдете в -примере -диалогового окна авторизации по отпечатку пальца.

- -

Если вы тестируете эту функцию, выполните следующие действия:

-
    -
  1. Установите инструменты SDK Android (версию 24.3), если у вас их еще нет.
  2. -
  3. Зарегистрируйте в эмуляторе новый отпечаток пальца (в разделе -Настройки > Безопасность > Отпечаток пальца) и следуйте дальнейшим инструкциям.
  4. -
  5. Воспользовавшись указанной ниже командой, сымитируйте в эмуляторе события касания для проверки отпечатка пальца. - С помощью этой же команды сымитируйте события касания для проверки отпечатка пальца на экране блокировки или в -своем приложении. -
    -adb -e emu finger touch <finger_id>
    -
    -

    В Windows, возможно, потребуется выполнить команду {@code telnet 127.0.0.1 }, а затем -{@code finger touch }. -

    -
  6. -
- -

Подтверждение учетных данных

-

Для авторизации пользователей ваше приложение может обратиться к сведениям о том, как давно они разблокировали свое устройство в последний раз. Эта функция -избавляет пользователя от необходимости запоминать отдельные пароли для каждого приложения, а вас — от необходимости -реализовывать собственный пользовательский интерфейс авторизации. В приложении эту функцию следует -использовать совместно с реализацией открытого или секретного ключа для авторизации пользователей.

- -

Чтобы задать временя ожидания, в течение которого после успешной -авторизации можно использовать ключ повторно, вызовите новый метод -{@code android.security.keystore.KeyGenParameterSpec.setUserAuthenticationValidityDurationSeconds()} -при настройке {@link javax.crypto.KeyGenerator} или -{@link java.security.KeyPairGenerator}. На сегодняшний день эта функция совместима с симметричными криптографическими -операциями.

- -

Не стоит слишком часто отображать диалоговое окно повторной авторизации — ваше -приложение должно сначала попробовать использовать криптографический объект и только потом, если окажется, что время ожидания истекло, обратиться к методу -{@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence) createConfirmDeviceCredentialIntent()} -для повторной авторизации пользователя. -

- -

Как можно реализовать эту функцию, показано в -примерепроверки учетных данных. -

- -

Прямой обмен контентом

- - - -

M Preview содержит API-интерфейсы для интуитивно понятного и быстрого обмена контентом между пользователями. Теперь вы можете -определить целевые объекты прямого обмена, которые будут запускать определенную операцию в приложении -и открываться в меню Поделиться. С помощью данной функции пользователи могут обмениваться контентом -с целевыми объектами, такими как контакты, в других приложениях. Например, целевой объект прямого обмена может запустить операцию -в другом приложении социальной сети, что позволит пользователю напрямую делиться контентом с другом -или членами сообщества через это приложение.

- -

Чтобы включить использование целевых объектов прямого обмена, необходимо определить класс, который наследует класс -{@code android.service.}
-{@code chooser.ChooserTargetService}. Объявите -{@code ChooserTargetService} в манифесте. В этом объявлении укажите разрешение -{@code BIND_CHOOSER_TARGET_SERVICE} и фильтр намерений с помощью действия -{@code SERVICE_INTERFACE}.

-

В примере ниже показано, как объявить {@code ChooserTargetService} - в вашем манифесте.

-
-<service android:name=".ChooserTargetService"
-        android:label="@string/service_name"
-        android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
-    <intent-filter>
-        <action android:name="android.service.chooser.ChooserTargetService" />
-    </intent-filter>
-</service>
-
- -

Для каждого действия, которое необходимо сделать доступным для {@code ChooserTargetService}, добавьте в манифест вашего приложения элемент -{@code } с именем -{@code "android.service.chooser.chooser_target_service"}. -

- -
-<activity android:name=".MyShareActivity”
-        android:label="@string/share_activity_label">
-    <intent-filter>
-        <action android:name="android.intent.action.SEND" />
-    </intent-filter>
-<meta-data
-        android:name="android.service.chooser.chooser_target_service"
-        android:value=".ChooserTargetService" />
-</activity>
-
- -

Голосовой интерфейс

-

-В M Preview представлен новый голосовой API-интерфейс, который, наряду с -голосовыми командами, -позволяет встраивать в приложение диалоговое голосовое взаимодействие. Вызовите метод -{@code android.app.Activity.isVoiceInteraction()}, чтобы определить, была ли операция запущена -в ответ на голосовую команду. Если это так, ваше приложение может использовать класс -{@code android.app.VoiceInteractor}, чтобы получить голосовое подтверждение от пользователя, предложить ему список вариантов -на выбор и многое другое. Подробнее о реализации голосовых команд можно почитать в -руководстве к голосовому интерфейсу. -

- -

API-интерфейс помощника

-

-M Preview предлагает новый способ взаимодействия пользователей с приложением — взаимодействие посредством помощника. Для этого -пользователю необходимо разришить помощнику использовать текущий контекст, после чего он сможет вызывать помощника -в любом приложении, удерживая кнопку Домой.

-

Разработчик может также запретить приложению сообщать помощнику текущий контекст. Для этого установите флаг -{@link android.view.WindowManager.LayoutParams#FLAG_SECURE}. Кроме стандартного набора -информации, который платформа передает помощнику, ваше приложение может сообщать ему дополнительные -сведения посредством нового класса {@code android.app.Activity.AssistContent}.

- -

Чтобы предоставить помощнику дополнительный контекст из вашего приложения, выполните следующие действия:

- -
    -
  1. Реализуйте интерфейс {@link android.app.Application.OnProvideAssistDataListener}.
  2. -
  3. Зарегистрируйте этот приемник с помощью -{@link android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()}.
  4. -
  5. Для предоставления контекста, относящего к конкретной операции, переопределите метод обратного вызова -{@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()}, -а также, при желании, новый метод обратного вызова{@code Activity.onProvideAssistContent()}. -
- -

Уведомления

-

В API-интерфейс уведомлений внесены следующие изменения:

-
    -
  • {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} — новый уровень фильтрации, -соответствующий новому режиму «Не беспокоить» Только будильник.
  • -
  • {@code Notification.CATEGORY_REMINDER} — новое значение категории, позволяющее отличить -установленные пользователем напоминания от других событий -({@link android.app.Notification#CATEGORY_EVENT}) и будильников -({@link android.app.Notification#CATEGORY_ALARM}).
  • -
  • {@code android.graphics.drawable.Icon} — новый класс, который можно добавить к уведомлениям -с помощью методов {@code Notification.Builder.setSmallIcon(Icon)} и -{@code Notification.Builder.setLargeIcon(Icon)}.
  • -
  • {@code NotificationManager.getActiveNotifications()} — новый метод, с помощью которого ваше приложение -может определить, какие из уведомлений в настоящее время активны. Если хотите взглянуть, как может быть реализовано приложение -с этой функцией, пройдите по ссылке: Пример активных уведомлений.
  • -
- -

Поддержка пера Bluetooth

-

В M Preview улучшена поддержка ввода с помощью пера Bluetooth. Пользователи могут подключить -совместимое перо Bluetooth к своему смартфону или планшету. Когда перо подключено, данные о его -положении объединяются со сведениями о степени нажима и нажатия кнопки на пере, -и таким образом при сенсорном вводе используются не только возможности сенсорного экрана. Ваше приложение может отслеживать -нажатия кнопки пера и выполнять дополнительные действия, регистрируя в операции новые методы обратного вызова -{@code View.onStylusButtonPressListener} и -{@code GestureDetector.OnStylusButtonPressListener}.

- -

Для определения нажатий кнопок пера используйте методы и константы {@link android.view.MotionEvent}: -

-
    -
  • Если пользователь касается пером кнопки на экране приложения, метод -{@link android.view.MotionEvent#getToolType(int) getTooltype()} возвращает -{@link android.view.MotionEvent#TOOL_TYPE_STYLUS}.
  • -
  • В приложениях, разработанных для M Preview, при нажатии пользователем на основную кнопку на пере, метод -{@link android.view.MotionEvent#getButtonState() getButtonState()} -возвращает{@code MotionEvent.STYLUS_BUTTON_PRIMARY}. - Если перо оснащено дополнительной кнопкой, то при нажатии на нее этот метод возвращает -{@code MotionEvent.STYLUS_BUTTON_SECONDARY}. При нажатии пользователем -на обе кнопки одновременно, метод возвращает оба значения через оператор «OR» -({@code STYLUS_BUTTON_PRIMARY|STYLUS_BUTTON_SECONDARY}).
  • -
  • -В приложениях, разработанных для более ранних версий платформы, метод -{@link android.view.MotionEvent#getButtonState() getButtonState()} возвращает -{@link android.view.MotionEvent#BUTTON_SECONDARY} (при нажатии на основную кнопку на пере), -{@link android.view.MotionEvent#BUTTON_TERTIARY} (при нажатии дополнительной кнопки на пере) или оба этих значения. -
  • -
- -

Улучшенное сканирование Bluetooth с низким потреблением энергии

-

-Если ваше приложение выполняет сканирование Bluetooth с низким потреблением энергии, вы можете при помощи нового метода -{@code android.bluetooth.le.ScanSettings.Builder.setCallbackType()} указать, что обратные вызовы следует уведомлять только в случае, если прежде обнаружен пакет объявления, - совпадающий с заданным фильтром -{@link android.bluetooth.le.ScanFilter}, или если он не обнаруживается в течение заданного периода времени. - Такой подход к сканированию обеспечивает еще большую экономию энергии, чем это было возможно в предыдущей -версии платформы. -

- -

Поддержка Hotspot 2.0, выпуск 1

-

-В M Preview включена поддержка Hotspot 2.0 (выпуск 1) для устройств Nexus 6 и Nexus 9. Для -предоставления учетных данных Hotspot 2.0 в вашем приложении используйте новые методы класса -{@link android.net.wifi.WifiEnterpriseConfig}, такие как {@code setPlmn()} и -{@code setRealm()}. В объекте {@link android.net.wifi.WifiConfiguration} можно задать поля -{@link android.net.wifi.WifiConfiguration#FQDN} и {@code providerFriendlyName}. -Новое свойство{@code ScanResult.PasspointNetwork} показывает, является ли обнаруженная -сеть точкой доступа Hotspot 2.0. -

- -

Режим отображения в формате 4K

-

Теперь платформа позволяет приложениям запрашивать увеличение разрешения экрана до 4K при обработке изображения -на совместимом устройстве. Чтобы запросить текущее физическое разрешение, используйте новые API-интерфейсы -{@code android.view.Display.Mode}. Если элементы пользовательского интерфейса, отрисованные в более низком логическом разрешении, масштабируются до -более высокого физического разрешения, обратите внимание, что метод физического разрешения -{@code Display.Mode.getPhysicalWidth()} возвращает результат, который может отличаться от результата выполнения метода логического разрешения -{@link android.view.Display#getSize(android.graphics.Point) getSize()} .

- -

Можно отправить запрос системе на изменение физического разрешения в активном приложении, задав для окна вашего приложения свойство -{@code WindowManager.LayoutParams.preferredDisplayModeId}. Эта -функция особенно полезна в случаях, когда требуется переключиться на разрешение экрана 4K. В режиме отображения в формате 4K элементы -пользовательского интерфейса отрисовываются в исходном разрешении (например, 1080p) и масштабируются до 4K, однако содержимое -{@link android.view.SurfaceView} объектов может отображаться с использованием основного разрешения.

- -

Метод ColorStateLists для работы с темами

-

Теперь метод -{@link android.content.res.ColorStateList} для устройств под управлением M Preview поддерживает атрибуты темы. Методы -{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} и -{@link android.content.res.Resources#getColor(int) getColor()} уже неактуальны. Если вы -вызываете эти API-интерфейсы, вызовите вместо них новые методы {@code Context.getColorStateList()} или -{@code Context.getColor()}, Доступные также в библиотеке v4 appcompat -посредством {@link android.support.v4.content.ContextCompat}.

- -

Работа с аудио

- -

В M Preview реализован ряд улучшений в области обработки аудиофайлов системой Android, включая следующие:

-
    -
  • Новые API-интерфейсы {@code android.media.midi}, поддерживающие протокол MIDI - для отправки и получения событий -MIDI.
  • -
  • Новые классы: {@code android.media.AudioRecord.Builder} позволяет создавать объекты захвата цифрового аудио, а {@code android.media.AudioTrack.Builder} — объекты воспроизведения, -а также осуществлять настройку источника -аудио и свойств приемника, переопределяя исходные системные настройки.
  • -
  • Обработчики API-интерфейсов предназначены для связывания аудиофайлов и устройств ввода. Это особенно полезно в случае, если ваше приложение -предоставляет возможность выполнять голосовой поиск с помощью игрового контроллера или пульта дистанционного управления, подключенного к Android -TV. Когда пользователь запустит поиск, система вызовет новый метод обратного вызова {@code android.app.Activity.onSearchRequested()}. - Чтобы определить, оснащено ли устройство ввода встроенным микрофоном, получите из этого метода обратного вызова объект -{@link android.view.InputDevice} и вызовите новый метод -{@code InputDevice.hasMic()}.
  • -
  • Новый класс {@code android.media.AudioDevicesManager}, позволяющий пользователю получить список всех -подключенных источников аудио и приемников. Также можно указать объект -{@code android.media.OnAudioDeviceConnectionListener}, если требуется, чтобы приложение -получало уведомления о подключении или отключении аудиоустройств.
  • -
- -

Работа с видео

-

В M Preview вы обнаружите новые возможности API-интерфейсов для обработки видео, в том числе:

-
    -
  • Новый класс {@code android.media.MediaSync} обеспечивает синхронную обработку потоков -аудио и видео. Буферы аудио отправляются неблокируемым способом и возвращаются посредством -обратного вызова. Также имеется поддержка динамической скорости воспроизведения. -
  • -
  • Новое событие {@code MediaDrm.EVENT_SESSION_RECLAIMED} указывает на то, что сеанс, запущенный -приложением, освобожден диспетчером ресурсов. Если в вашем приложении используются сеансы DRM, то это событие -необходимо обработать и убедиться в том, что освобожденный сеанс не используется. -
  • -
  • Новый код ошибки {@code MediaCodec.CodecException.ERROR_RECLAIMED} указывает на то, что -диспетчер ресурсов освободил ресурс мультимедиа, используемый кодеком. Во всех остальных случаях кодек должен -быть освобожден, поскольку его необходимо перевести в конечное состояние. -
  • -
  • Новый интерфейс {@code MediaCodecInfo.CodecCapabilities.getMaxSupportedInstances()} позволяет узнать максимальное количество - экземпляров кодека, которые могут выполняться одновременно. -
  • -
  • Новый {@code MediaPlayer.setPlaybackParams()} метод задает скорость при ускоренном или -замедленном воспроизведении мультимедиа. Он также позволяет автоматически замедлять или ускорять воспроизведение аудио синхронно с -видео.
  • -
- -

Возможности камеры

-

В M Preview представлены следующие API-интерфейсы для вспышки камеры и повторной обработки -изображений камерой.

- -

API-интерфейс вспышки

-

Если камера оснащена вспышкой, вы можете вызвать метод{@code CameraManager.setTorchMode()}, -чтобы включить или отключить режим фонарика, не запуская камеру. Приложение -не может заполучить вспышку или камеру в единоличное пользование. Режим фонарика отключается -и становится недоступен, когда камера недоступна или ресурсов камеры недостаточно для использования вспышки в качестве -фонарика. Кроме того, другие приложения могут вызывать метод {@code setTorchMode()} -для отключения режима фонарика. При закрытии приложения, которое включило режим фонарика, этот режим -отключается.

- -

Чтобы зарегистрировать обратный вызов для уведомления о режиме фонарика, вызовите метод -{@code CameraManager.registerTorchCallback()}. При первой регистрации обратного вызова -он сразу же вызывается со сведениями о состоянии режима фонарика для всех известных к настоящему моменту устройств, -оснащенных вспышкой. При успешном включении или отключении фонарика вызывается метод -{@code CameraManager.TorchCallback.onTorchModeChanged()}.

- -

API-интерфейс повторной обработки

-

API-интерфейс {@link android.hardware.camera2 Camera2} теперь поддерживает повторную обработку цветовых моделей YUV и изображений в собственном -формате непрозрачности. Чтобы определить, возможна ли повторная обработка, приложение использует метод -{@code CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES}. Если устройство поддерживает повторную обработку, -вы можете создать сеанс захвата изображения с камеры с возможностью повторной обработки, вызвав метод -{@code CameraDevice.createReprocessableCaptureSession()}, а затем создать запросы на повторную обработку буфера -входных данных.

- -

Чтобы подключить поток буфера входных данных к модулю повторной обработки изображения с камеры, используйте класс -{@code ImageWriter}. Чтобы получить пустой буфер, выполните следующие действия:

- -
    -
  1. Вызовите метод {@code ImageWriter.dequeueInputImage()}.
  2. -
  3. Заполните буфер входными данными.
  4. -
  5. Отправьте буфер в камеру, вызвав метод {@code ImageWriter.queueInputImage()}.
  6. -
- -

Если вы используете объект {@code ImageWriter} вместе с изображением -{@code android.graphics.ImageFormat.PRIVATE}, вашему приложению не удастся получить доступ к изображению -напрямую. Вместо этого передайте изображение {@code ImageFormat.PRIVATE} прямо в -{@code ImageWriter}, вызвав метод {@code ImageWriter.queueInputImage()} без копии -буфера.

- -

Класс {@code ImageReader} теперь поддерживает потоки изображений в формате {@code android.graphics.ImageFormat.PRIVATE}. - Благодаря этому ваше приложение может организовать циклическую очередь выходных изображений -{@code ImageReader}, выбрать одно или несколько изображений и отправить их в -{@code ImageWriter} для повторной обработки камерой.

- -

Возможности Android for Work

-

В M Preview представлены новые API-интерфейсы для Android for Work:

-
    -
  • Улучшенные элементы управления для корпоративных специализированных устройств. Владельцу устройства -теперь доступны новые возможности управления -корпоративными специализированными устройствами при помощие следующих настроек: -
      -
    • Отключение или повторное включение блокировки клавиатуры с помощью метода -{@code DevicePolicyManager.setKeyguardEnabledState()}.
    • -
    • Отключение или повторное включение строки состояния (включая быстрые настройки, уведомления и -запуск Google Now путем проведения пальцем по экрану) с помощью метода -{@code DevicePolicyManager.setStatusBarEnabledState()}.
    • -
    • Отключение и повторное включение безопасной загрузки с помощью константы -{@code DISALLOW_SAFE_BOOT} {@link android.os.UserManager}.
    • -
    • Предотвращение отключения экрана, если устройство питается от сети, с помощью константы -{@code STAY_ON_WHILE_PLUGGED_IN} {@link android.provider.Settings.Global}.
    • -
    -
  • -
  • Автоматические установка и удаление приложений владельцами устройств. Владелец устройства теперь может автоматически -устанавливать и удалять приложения с помощью API-интерфейсов {@link android.content.pm.PackageInstaller}, -независимо от Google Play for Work. Теперь вы можете подготавливать устройства с помощью компонента «Владелец устройства», который -получает и устанавливает приложения без участия пользователя. Такая возможность особенно полезна тем, что позволяет подготавливать -киоски или иные подобные устройства одним касанием, не активируя аккаунт Google.
  • -
  • Автоматический доступ к корпоративному сертификату. Теперь, когда приложение вызывает метод -{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()}, -владелец профиля или устройства, прежде чем ему будет предложено выбрать сертификат, может вызывать метод -{@code DeviceAdminReceiver.onChoosePrivateKeyAlias()}, который автоматически предоставит предложению псевдоним. - С помощью этой возможности вы можете предоставлять управляемым приложениям доступ к сертификатам без -вмешательства пользователя.
  • -
  • Автоматическое принятие обновлений системы. Настройка параметров обновления системы с помощью -{@code DevicePolicyManager.setSystemUpdatePolicy()} теперь позволяет компоненту «Владелец устройства» автоматически принять обновление системы -(например, в киоске) или отложить его на период до 30 дней, причем пользователь -тоже не сможет установить обновление до истечения этого срока. Более того, администратор может задать ежедневный временной интервал, когда устройство будет принимать -обновления (например, во те часы, когда киоск не используется). При наличии доступного -обновления система проверяет, заданы ли параметры обновления системы приложением «Параметры работы» -, и выполняет соответствующие действия. -
  • -
  • -Делегированная установка сертификата. Владелец профиля или устройства теперь может предоставлять -стороннему приложению возможность вызывать следующие API-интерфейсы управления сертификатом -{@link android.app.admin.DevicePolicyManager}: -
      -
    • {@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName) -getInstalledCaCerts()}
    • -
    • {@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[]) -hasCaCertInstalled()}
    • -
    • {@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[]) -installCaCert()}
    • -
    • {@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[]) -uninstallCaCert()}
    • -
    • {@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName) -uninstallAllUserCaCerts()}
    • -
    • {@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String) -installKeyPair()}
    • -
    -
  • -
  • Корпоративная настройка защиты от сброса параметров до заводских настроек. При подготовке компонента «Владелец устройства» теперь -имеется возможность настроить параметры разблокировки защиты от сброса параметров до заводских настроек (FRP) путем задания пакета -{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS}. Приложение NFC Programmer -может после перезагрузки устройства предоставить эти параметрыс целью разблокировки FRP и его подготовки -(без необходимости запрашивать ранее настроенную учетную запись Google). Если не изменить эти параметры, -заводские настройки сохранятся не позволят активировать устройство без ввода активированных ранее -учетных данных Google. -

    Кроме того, задав ограничения на использование служб Google Play, компонент «Владелец устройства» может указать альтернативные учетные записи -Google для разблокировки FRP и замены учетных записей, активированных на устройстве.

    -
  • - -
  • Отслеживание использования данных. Владелец профиля или устройства теперь может запрашивать статистику использования -данных, которая отображается в разделе Настройки > Использование данных, с помощью новых методов -{@code android.app.usage.NetworkStatsManager}. Владельцам профилей автоматически предоставляется -разрешение запрашивать данные профиля, которым они управляют, тогда как владельцам устройств предоставляется доступ к сведениям об использовании данных -основного управляемого пользователя.
  • -
  • Управление разрешениями на выполнение. -

    Владелец профиля или устройства может задавать политику разрешений -для всех запросов на использование среды выполнения любых приложений с помощью -{@code DevicePolicyManager.setPermissionPolicy()}. Это позволяет выбрать, будет ли система предлагать пользователю предоставить разрешение -как обычно, или она будет автоматически предоставлять разрешения или отказывать в них. В последнем случае -пользователю не удастся изменить выбор, сделанный владельцем профиля или устройства на экране -разрешений приложения в разделе Настройки.

  • -
  • VPN в разделе «Настройки». Приложения VPN теперь отображаются в разделе -Настройки > Другие сети > VPN. -Кроме того, сведения в уведомлениях об использовании VPN теперь зависят от настроек самой VPN. - Для владельца профиля уведомления зависят от того, настроена ли VPN для -управляемого профиля, личного профиля или же того и другого. Для владельца устройства уведомления зависят от того, настроена ли VPN для -всего устройства.
  • -
  • Уведомление о состоянии «В работе». Теперь, когда приложения -из управляемого профиля выполняет операцию в фоновом режиме, в строке состояния появляется значок портфеля. Кроме того, если после разблокировки устройства -открывается операция приложения из управляемого профиля, отображается всплывающее уведомление о том, -что операция выполняется в рабочем профиле. -
  • -
- -

- Подробные сведения обо всех изменениях в API-интерфейсах в версии M Developer Preview представлены в отчете о различиях между API-интерфейсами. -

diff --git a/docs/html-intl/intl/ru/preview/backup/index.jd b/docs/html-intl/intl/ru/preview/backup/index.jd deleted file mode 100644 index 1f9dacc15867d8ca0e4745168658292c56f95b7a..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/backup/index.jd +++ /dev/null @@ -1,327 +0,0 @@ -page.title=Автоматическое резервное копирование для приложений -page.tags=backup, previewresources, androidm -page.keywords=резервное копирование, автоматическое резервное копирование, предварительная версия -page.image=images/cards/card-auto-backup_2x.png -@jd:body - - - -

- Пользователи зачастую тратят очень много времени и усилий на создание данных и настройку параметров в приложениях. - Поэтому одна из важнейших задач при разработке платформы — сделать так, чтобы пользователь не терял свои данные, если его устройство вышло из строя или он решил перейти на новое. - Устройства под управлением Android M Preview -отлично справляются с этой задачей, автоматически выполняя резервное копирование данных -приложения в Google Drive. Если пользователь перейдет на новое устройство, данные приложений будут автоматически перенесены -на него. -

- -

- Автоматическое резервное копирование предусмотрено для всех приложений, установленных на устройствах под управлением Android M Preview. При этом -вам не нужно добавлять в приложение дополнительный код. Система сама предложит пользователю возможность включить или отключить -автоматическое резервное копирование данных. Вы также можете указать, для каких данных приложения следует создавать резервные копии, а для каких — нет. -

- -

- В этой статье описывается новое поведение системы и порядок указания данных приложения, -подлежащих резервному копированию. -

- -

Обзор

- -

- При автоматическом резервном копировании данные, создаваемые в вашем приложении на устройстве пользователя, отпраляются в -Google Drive пользователя и происходит их шифрование, что позволяет предотварить потерю данных. Хранилище для ваших данных предоставляется совершенно -бесплатно. Более того, данные резервного копирования засчитываются вне личной квоты пользователя на использование ресурсов Google Drive. В период использования M Developer Preview -пользователи могут хранить в облаке до 25 МБ данных для каждого приложения Android. -

- -

- Автоматическое резервное копирование выполняется каждые 24 часа, когда устройство не используется, находится в режиме зарядки и подключено к сети -Wi-Fi. При соблюдении необходимых условий служба диспетчера резервного копирования отправляет все доступные резервные копии данных -в облако. Когда пользователь переходит на использование нового устройства или удаляет и заново устанавливает -приложение, для которого имеется резервная копия, выполняется операция восстановления и резервные копии данных переносятся в каталог -переустановленного приложения. -

- -

- Примечание. Если ваше приложение использует устаревшую -службу резервного копирования Android, то -вместо нового поведения системы будет применяться существующий механизм резервного копирования. -

- - -

Автоматически исключаемые файлы данных

- -

- Не все данные приложения подлежат резервному копированию. В частности, временные файлы и кэш сохранять не нужно. Поэтому служба -автоматического резервного копирования по умолчанию исключает из этого процесса определенные файлы данных: -

- -
    -
  • файлы в каталогах, на которые ссылаются методы {@link android.content.Context#getCacheDir -getCacheDir()} и {@link android.content.ContextWrapper#getCodeCacheDir getCodeCacheDir()}; - -
  • - -
  • файлы во внешнем хранилище, кроме файлов, находящихся в каталогах, на которые ссылается метод -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()}; - -
  • - -
  • файлы в каталоге, на который ссылается метод -{@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()}. -
  • -
- -

Настройка резервного копирования данных

- -

- Резервные копии создаются для данных, создаваемых любым приложением, которое установлено на устройстве под управлением M Preview, исключение составляют только -файлы, обозначенные в предыдущем разделе. Если вы хотите самостоятельно определить, -какие данные вашего приложения подлежат резервному копированию, а какие нет, это можно сделать с помощью соответствующих настроек в манифесте приложения. -

- -

Включение или исключение данных

- -

- В зависимости о того, какие данные требуются вашему приложению и как вы их сохраняете, вам может потребоваться задать определенные правила -для включения или исключения определенных файлов или каталогов. Служба автоматического резервного копирования -поддерживает настройку таких правил посредством XML-файла конфигурации и -манифеста приложения. В манифесте приложения можно указать файл конфигурации схемы резервного копирования, как показано в примере -ниже. -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample">
-    <uses-sdk android:minSdkVersion="MNC"/>
-    <uses-sdk android:targetSdkVersion="MNC"/>
-    <app ...
-        android:fullBackupContent="@xml/mybackupscheme">
-    </app>
-    ...
-</manifest>
-
- -

- В этом примере кода атрибут android:fullBackupContent задает XML-файл, -расположенный в каталоге res/xml/ проекта приложения, который называется -mybackupscheme.xml. В этом файле конфигурации содержатся правила резервного -копирования файлов. В следующем примере кода демонстрируется файл конфигурации, в котором из процесса резервного копирования исключается определенный -файл: -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<full-backup-content>
-    <exclude domain="database" path="device_info.db"/>
-</full-backup-content>
-
- -

- В этом примере конфигурации резервного копирования из операции исключается только определенный файл базы данных. - Для всех остальных файлов будут создаваться резервные копии. -

- -

Синтаксис конфигурации резервного копирования

- -

- Служба резервного копирования предоставляет вам возможность указать, какие файлы следует включить в процесс резервного копирования или -исключить из него. Синтаксис XML-файла конфигурации резервного копирования данных следующий: -

- -
-<full-backup-content>
-    <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
-    <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
-</full-backup-content>
-
- -

- С помощью перечисленных ниже элементов и атрибутов можно указать файлы, которые вы хотите включить в процесс резервного копирования или исключить из -него: -

- -
    -
  • - <include>: используйте этот элемент, если хотите, чтобы система не создавала резервные копии всех данных в вашем приложении, как это происходит по умолчанию, а копировала только определенный набор данных. - Если указан тег -<include>, система создает резервные копии только для ресурсов -с этим элементом. -
  • - -
  • - <exclude>: используйте этот элемент, чтобы указать набор ресурсов, которые -следует исключить из процесса резервного копирования. В таком случае система будет создавать резервные копии всех данных в приложении, кроме ресурсов с этим -элементом; -
  • - -
  • - domain.: тип ресурса, который необходимо включить в резервное копирование или исключить из него. Допустимые -значения для этого атрибута могут быть следующие: -
  • - -
  • -
      -
    • - root: указывает на то, что ресурс находится в корневом каталоге приложения; -
    • - -
    • - file: соответствует ресурсу в каталоге, возвращаемом методом -{@link android.content.Context#getFilesDir getFilesDir()}; -
    • - -
    • - database: соответствует базе данных, возвращаемой методом -{@link android.content.Context#getDatabasePath getDatabasePath()} или классом -{@link android.database.sqlite.SQLiteOpenHelper}; -
    • - -
    • - sharedpref: соответствует объекту {@link android.content.SharedPreferences}, -возвращаемому методом -{@link android.content.Context#getSharedPreferences getSharedPreferences()}; -
    • - -
    • - external: указывает на то, что ресурс находится во внешнем хранилище, и соответствует -файлу в каталоге, возвращаемом методом -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()}; -
    • - -
    • - path: путь к ресурсу, который необходимо включить в резервное копирование или исключить -из него. -
    • -
    -
  • -
- - -

Запрет резервного копирования данных

- -

- Можно запретить автоматическое резервное копирование любых данных приложения. Для этого в манифесте в элементе приложения задайте для атрибута -android:allowBackup значениеfalse. - Как это делается, видно в примере кода ниже: -

- -
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample">
-    <uses-sdk android:minSdkVersion="MNC"/>
-    <uses-sdk android:targetSdkVersion="MNC"/>
-    <app ...
-        android:allowBackup="false">
-    </app>
-    ...
-</manifest>
-
- - -

Тестирование конфигурации резервного копирования

- -

- После создания конфигурации резервного копирования необходимо ее протестировать и убедиться, что ваше приложение сохраняет данные и впоследствии -они могут быть должным образом восстановлены. -

- - -

Включение журнала резервного копирования

- -

- Чтобы упростить для себя анализ обработки вашего XML-файла функцией резервного копирования, прежде чем приступать к тестированию, -включите ведение журнала: -

- -
-$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE
-
- -

Тестирование резервного копирования

- -

Чтобы вручную запустить процесс резервного копирования, сначала активируйте диспетчер резервного копирования, выполнив следующую -команду: -

- -
-$ adb shell bmgr run
-
- -

- Затем вручную выполните резервное копирование вашего приложения с помощью следующей команды, указав название -пакета в виде параметра <PACKAGE>: -

- -
-$ adb shell bmgr fullbackup <PACKAGE>
- - -

Тестирование восстановления

- -

- Чтобы вручную запустить процесс восстановления после резервного копирования данных приложения, выполните следующую команду, указав название -пакета в виде параметра <PACKAGE>: -

- -
-$ adb shell bmgr restore <PACKAGE>
-
- -

- Предупреждение. При выполнении этого действия перед операцией -восстановления работа приложения будет остановлена и все его данные удалены. -

- -

- Чтобы запустить процесс восстановления, удалите ваше приложение и снова установите его. Данные -приложения будут автоматически восстановлены из облака сразу по завершении установки. -

- - -

Устранение неполадок резервного копирования

- -

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

- -
$ adb shell bmgr wipe <TRANSPORT> <PACKAGE>
- -

- Перед значением <TRANSPORT> должен быть префикс com.google.android.gms. - Чтобы получить список транспорта, выполните следующую команду: -

- -
$ adb shell bmgr list transports
- -

Известные проблемы

- -

Ниже перечислены известные проблемы в работе службы автоматического резервного копирования.

- -
    -
  • Google Cloud Messaging -– существует проблема с приложениями, использующими Google Cloud Messaging для отправки push-уведомлений. Она связана с тем, -что при резервном копировании кода регистрации, возвращаемого Google Cloud Messaging, может нарушиться отправка -push-уведомлений для восстановленного приложения. Важно запросить у API-интерфейса новый код -регистрации после установки приложения на новое устройство, что не выполняется в случае резервного копирования -старого кода регистрации. Чтобы избежать этого, исключите код регистрации из списка файлов -для резервного копирования. -
  • -
diff --git a/docs/html-intl/intl/ru/preview/behavior-changes.jd b/docs/html-intl/intl/ru/preview/behavior-changes.jd deleted file mode 100644 index 0623ea71c7540639d2ae1ddf94d9bc84e672b6e6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/behavior-changes.jd +++ /dev/null @@ -1,402 +0,0 @@ -page.title=Изменения в работе -page.keywords=предварительная версия,пакет sdk,совместимость -sdk.platform.apiLevel=MNC -@jd:body - - - -

Наряду с новыми функциями и возможностями, версия M Developer Preview также включает в себя ряд -системных изменений и изменений в работе API-интерфейсов. В этом документе рассматриваются -определенные ключевые изменения, о которых следует знать, чтобы учитывать их при разработке приложений.

- -

Если вы ранее публиковали приложения для Android, то примите во внимание, -что эти изменения в платформе могут повлиять на работу ваших уже опубликованных приложений.

- -

Разрешения на выполнение

-

В M Preview представлена новая модель разрешений: теперь пользователи могут управлять разрешениями приложений -напрямую во время работы с ними. Такая модель обеспечивает улучшенное управление -разрешениями и одновременно позволяет оптимизировать установку и автоматическое обновление для разработчиков. -Пользователи могут предоставлять разрешения или отзывать их отдельно для каждого установленного приложения.

- -

В ваших приложениях, предназначенных для M Preview, следует в обязательном порядке проверить такие разрешения и при необходимости -запросить их. Чтобы определить, было ли вашему приложению предоставлено разрешение, вызовите новый метод -{@code Context.checkSelfPermission()}. Чтобы запросить разрешение, вызовите новый метод -{@code Activity.requestPermission()}. Даже если ваше приложение не предназначено для использования в версии M, вам все равно следует -протестировать его с использованием новой модели разрешений.

- -

Подробные сведения о поддержке новой модели -разрешений вашим приложением представлены в статье -Разрешения. Как можно проверить влияние новой модели на ваше приложение, читайте в -руководстве по тестированию

- -

Оптимизация экономии энергии

-

В M Preview реализован ряд новых функций оптимизации экономии энергии для неактивных устройств и приложений.

- -

Режим «Doze»

-

Если устройство отключено от сети и остается неактивным (и с выключенным экраном) в течение определенного времени, -оно переходит в режим Doze и старается, чтобы система не выходила из спящего состояния. В этому режиме -устройство периодически возобновляет свою обычную работу на краткие промежутки времени, чтобы произвести -синхронизацию и позволить системе выполнить какие-либо отложенные операции.

- -

Когда устройство находится в режиме «Doze», к приложениям применяются следующие ограничения:

-
    -
  • Доступ к сети отключен до тех пор, пока от службы Google Cloud Messaging -не будет получен высокоприоритетный сигнал побуждения приложения к действию.
  • -
  • Механизмы WakeLock игнорируются.
  • -
  • Будильники, установленные с помощью класса {@link android.app.AlarmManager}, отключаются, кроме -тех, которые установлены с помощью методов {@link android.app.AlarmManager#setAlarmClock setAlarmClock()} -и {@code AlarmManager.setAndAllowWhileIdle()}.
  • -
  • Поиск сетей Wi-Fi не производится.
  • -
  • Операции синхронизации и заданий для адаптеров синхронизации и {@link android.app.job.JobScheduler} блокируются. -
  • -
-

-

После выхода из режима «Doze», устройство приступает к выполнению любых отложенных заданий и операций синхронизации.

-

Чтобы протестировать эту функцию, подключите устройство под управлением M Preview к своему компьютеру -для разработки и выполните следующие команды: -

-
-$ adb shell dumpsys battery unplug
-$ adb shell dumpsys deviceidle step
-$ adb shell dumpsys deviceidle -h
-
-

Примечание. В предстоящем выпуске службы -Google Cloud Messaging -реализована возможность назначения сообщений -с высоким приоритетом. При получении вашим приложением сообщений GCM с высоким приоритетом ему на короткое время предоставляется -доступ к сети, даже если устройство находится в режиме «Doze». -

- -

Советы по тестированию работы приложений в режиме «Doze» представлены -в руководстве -по тестированию.

- -

Ждущий режим для приложений

-

В M Preview система может определять, что приложения неактивны, когда они -не используются. Приложение считается неактивным по прошествии определенного периода времени до тех пор, пока система -не обнаружит один из следующих сигналов:

- -
    -
  • приложение явным образом запущено пользователем;
  • -
  • у приложения имеется процесс, который в настоящее время выполняется на переднем плане (это может быть как сама операция или служба переднего плана, -так и процесс, используемый другой операцией или службой переднего плана).
  • -
  • приложение создает уведомление, которое пользователи видят на экране блокировки или в -области уведомлений.
  • -
  • пользователь явным образом исключает приложение из списка для оптимизации -в разделе Настройки.
  • -
- -

Если устройство отключено от электросети, для приложений, которые считаются неактивными, закрывается доступ к Интернету, -а их операции синхронизации и задания приостанавливаются. После подключения устройства к электросети доступ к Интернету для приложений возобновится -и они смогут приступить к выполнению любых ожидающих заданий и операций синхронизации. Если устройство -неактивно в течение длительного времени, неактивным приложениям раз в день предоставляется доступ к сети.

- -

Чтобы протестировать эту функцию, подключите устройство под управлением M Preview к своему компьютеру -для разработки и выполните следующие команды: -

-
-$ adb shell dumpsys battery unplug
-$ adb shell am set-idle <packageName> true
-$ adb shell am set-idle <packageName> false
-$ adb shell am get-idle <packageName>
-
- -

Примечание. В предстоящем выпуске службы -Google Cloud Messaging (GCM) -реализована возможность назначения сообщений -с высоким приоритетом. При получении вашим приложением сообщений GCM с высоким приоритетом ему на короткий промежуток времени предоставляется -доступ к сети, даже если приложение находится в неактивном состоянии. -

- -

Советы по тестированию работы приложений в ждущем режиме представлены -в руководстве -по тестированию.

- -

Подключаемые устройства хранения

-

-В M Preview пользователи могут подключать внешние устройства хранения, такие как SD-карты, и использовать их как внутреннее хранилище, -зашифровав и отформатировав требуемым образом. Благодаря этому - пользователи могут переносить как сами приложения, так свои файлы для этих приложений с одного устройства хранения на другое. При переносе -приложений система руководствуется настройкой -{@code android:installLocation} -в манифесте.

- -

Если ваше приложение работает с указанными ниже API-интерфейсами или полями, следует помнить, что возвращаемые ими пути к файлам -будут динамически изменяться при перемещении приложения с внутреннего хранилища на внешнее устройство или наоборот. -При создании путей к файлам настоятельно рекомендуется всегда вызывать эти API-интерфейсы динамическим образом. -Не используйте жестко запрограммированные пути к файлам и не указывайте созданные ранее полные пути к файлам.

- -
    -
  • Методы {@link android.content.Context}: -
      -
    • {@link android.content.Context#getFilesDir() getFilesDir()}
    • -
    • {@link android.content.Context#getCacheDir() getCacheDir()}
    • -
    • {@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}
    • -
    • {@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}
    • -
    • {@link android.content.Context#getDir(java.lang.String,int) getDir()}
    • -
    • {@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}
    • -
    • {@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}
    • -
    • {@link android.content.Context#getPackageCodePath() getPackageCodePath()}
    • -
    • {@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}
    • -
    -
  • -
  • Поля {@link android.content.pm.ApplicationInfo}: -
      -
    • {@link android.content.pm.ApplicationInfo#dataDir dataDir}
    • -
    • {@link android.content.pm.ApplicationInfo#sourceDir sourceDir}
    • -
    • {@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}
    • -
    • {@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}
    • -
    • {@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}
    • -
    • {@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}
    • -
    -
  • -
- -

Для отладки этой функции в Developer Preview подключите USB-устройство, - к устройству Android посредством USB-кабеля On-The-Go (OTG) и выполните следующую команду:

- -
-$ adb shell sm set-force-adoptable true
-
- -

Отказ от HTTP-клиента Apache

-

В M Preview удалена поддержка HTTP-клиента Apache. Если ваше приложение, разработанное для -Android 2.3 (уровень API 9) или более поздней версии, использует этот клиент, вам необходимо воспользоваться вместо него классом {@link java.net.HttpURLConnection}. - Этот API-интерфейс более эффективный, поскольку он сокращает использование сетевого трафика за счет прозрачного сжатия -и кэширования ответов, а также сводит к минимуму потребление энергии. Чтобы продолжить использовать API-интерфейсы HTTP Apache, -сначала объявите в своем файле {@code build.gradle} следующую зависимость compile-time: -

-
-android {
-    useLibrary 'org.apache.http.legacy'
-}
-
-

Вместо OpenSSL в Android теперь будет использоваться библиотека -BoringSSL -. Если вы используете в ваших приложениях Android NDK, не связывайте его с криптографическими библиотеками, -которые не входят в состав API-интерфейса NDK, такими как {@code libcrypto.so} и {@code libssl.so}. Эти библиотеки -не являются общедоступными API-интерфейсами, и в разных выпусках или на разных устройствах они могут быть изменены или разбиты без предварительного уведомления. -Кроме того, этим вы можете создать уязвимости в системе безопасности вашего продукта. Вместо этого внесите изменения в -собственный код для вызова криптографических API-интерфейсов Java посредством JNI или статической ссылки на -библиотеку криптографии по своему выбору.

- -

Изменения в классе AudioManager

-

Теперь в платформе не поддерживается настройка уровня громкости напрямую или отключение звука определенных потоков с помощью класса {@link android.media.AudioManager}. - Метод {@link android.media.AudioManager#setStreamSolo(int,boolean) -setStreamSolo()} больше не используется. Вместо него следует вызывать метод -{@code AudioManager.requestAudioFocus()}. Также больше не используется метод -{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()}; -вместо него следует вызывать метод {@code AudioManager.adjustStreamVolume()} -и передавать в него значение направления {@code ADJUST_MUTE} или {@code ADJUST_UNMUTE}.

- -

Выделение текста

- - - -

Теперь, когда пользователь выделяет текст в приложении, такие дополнительные действия как -Вырезать, Копировать и Вставить, можно отображать на -перемещаемой панели инструментов. Реализация взаимодействия пользователя с текстом аналогична той, которая используется для -контекстного меню, -как описано в статье -Запуск контекстного меню для отдельных представлений.

- -

Чтобы реализовать перемещаемую панель инструментов для выделения текста, внесите в ваши существующие приложения -следующие изменения.

-
    -
  1. В объекте {@link android.view.View} или {@link android.app.Activity} измените вызовы -{@link android.view.ActionMode} с -{@code startActionMode(Callback)} на {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}.
  2. -
  3. Возьмите свою реализацию класса {@code ActionMode.Callback} и сделайте его наследуемой от класса -{@code ActionMode.Callback2}.
  4. -
  5. Переопределите метод {@code Callback2.onGetContentRect()} для указания координат объекта -{@link android.graphics.Rect} контента (например, прямоугольника выделения текста) в представлении.
  6. -
  7. Если расположение прямоугольника больше недействительно и это единственный элемент, который не подлежит пересчитыванию, -вызовите метод {@code ActionMode.invalidateContentRect()}.
  8. -
- -

Если вы используете -вспомогательную библиотеку Android версии 22.2, то помните, что перемещаемым панелям инструментов -не свойственна обратная совместимость и по умолчанию для управления объектами {@link android.view.ActionMode} -используется библиотека appcompat. Это означает, что перемещаемые панели инструментов не будут отображаться. Чтобы включить поддержку -{@link android.view.ActionMode} в -{@link android.support.v7.app.AppCompatActivity}, вызовите метод -{@code android.support.v7.app.AppCompatActivity.getDelegate()}, затем вызовите -{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()} для возвращенного объекта -{@link android.support.v7.app.AppCompatDelegate} и задайте для параметра input -значение {@code false}. Этот вызов возвращает управление объектами {@link android.view.ActionMode} -платформе. На устройствах под управлением M Preview платформа поддерживает как режимы -{@link android.support.v7.app.ActionBar}, так и режимы перемещаемых панелей инструментов, тогда как на устройствах под управлением более ранних версий -поддерживаются только режимы {@link android.support.v7.app.ActionBar}.

- -

Изменения в хранилище ключей Android

-

В M Preview -поставщик хранилища ключей Android больше не поддерживает -DSA. ECDSA по-прежнему поддерживается.

- -

Ключи, для которых не требуется шифрование в хранилище, больше не будут удаляться при отключении или сбросе защищенного экрана блокировки -(например, пользователем или администратором устройства). Ключи, для которых требуется шифрование -в хранилище, во время таких событий будут удалены.

- -

Изменения в работе с Wi-Fi и сетями

- -

Ниже перечислены изменения в API-интерфейсах для работы с Wi-Fi и сетями, реализованные в M Preview.

-
    -
  • Теперь ваши приложения могут изменять состояние объектов {@link android.net.wifi.WifiConfiguration} -только в том случае, если эти объекты вы создали сами. Вам запрещено изменять или удалять объекты -{@link android.net.wifi.WifiConfiguration}, созданные пользователем или другими приложениями. -
  • -
  • -Ранее, если приложение принудительно подключало устройство к определенной сети Wi-Fi с помощью метода -{@link android.net.wifi.WifiManager#enableNetwork(int,boolean) enableNetwork()} и настройки -{@code disableAllOthers=true}, то устройство отключалось от других сетей, например, от -сотовой сети. В M Preview устройство больше не отключается от других сетей. Если -в вашем приложении для параметра {@code targetSdkVersion} выбрано значение {@code “20”} или меньше, оно закрепляется за выбранной сетью -Wi-Fi. Если же параметр {@code targetSdkVersion} имеет значение {@code “21”} или больше, следует использовать -API-интерфейсы для работы с несколькими сетями (такие как -{@link android.net.Network#openConnection(java.net.URL) openConnection()}, -{@link android.net.Network#bindSocket(java.net.Socket) bindSocket()} и новый метод -{@code ConnectivityManager.bindProcessToNetwork()}), чтобы обеспечить отправку -сетевого трафика приложения в выбранную сеть.
  • -
- -

Изменения в службе камеры

-

В M Preview изменена модель получения доступа к ресурсам в службе камеры. - Если раньше запросы доступа обрабатывались в порядке поступления, то теперь процессы с высоким приоритетом имеют преимущество. - В работе службы камеры произошли следующие изменения:

-
    -
  • Доступ к ресурсам подсистемы камеры, включая открытие и настройку устройства камеры, -предоставляется в зависимости от того, какой приоритет имеет процесс клиентского приложения. Процессы приложений с операциями, которые -видны пользователю или выполняются на переднем плане, обычно имеют более высокий приоритет, что повышает возможность доступа к ресурсам камеры и их -использования.
  • -
  • Когда приложение с более высоким приоритетом пытается использовать камеру, активные клиенты камеры для приложений с более низким приоритетом могут быть исключены из очереди доступа. - В устаревшем API-интерфейсе {@link android.hardware.Camera} -это приводит к вызову -{@link android.hardware.Camera.ErrorCallback#onError(int,android.hardware.Camera) onError()} -для исключенного клиента. В API-интерфейсе {@link android.hardware.camera2 Camera2} в таких случаях для исключенного клиента вызывается -{@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected(android.hardware.camera2.CameraDevice) onDisconnected()}. -
  • -
  • На устройствах, где камера позволяет такое, отдельным процессам -приложения предоставляется возможность одновременно и независимо друг от друга открывать и использовать ресурсы камеры. При этом служба камеры теперь отслеживает и исключает ситуации с использованием ресурсов несколькими процессами, -когда одновременный доступ вызывает значительное снижение производительности или негативно влияет на возможности -всех открытых камер. Это изменение -может привести к отключению доступа для клиентов с более низким приоритетом, даже если к этой же камере не пытаются получить доступ другие приложения. - -
  • -
  • -Смена пользователя приводит к исключению активных клиентов камеры в приложениях, относящихся к предыдущей учетной записи, -из очереди доступа. Доступ к камере предоставлен только профилям, которыми владеет активный пользователь устройства. -Фактически это означает, что если, например, пользователь переключился с гостевой учетной записи на другую, гостевой аккаунт не сможет покинуть запущенные процессы -, использующие ресурсы подсистемы камеры. -
  • -
- -

Среда выполнения ART

-

Среда выполнения ART теперь должным образом реализует правила доступа для метода -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()}. Это -изменение позволило устранить проблему, связанную с тем, что в предыдущих версиях система Dalvik неправильно проверяла правила доступа. -Если ваше приложение использует метод -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} и вы -хотите переопределить проверки доступа, вызовите метод -{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()}, присвоив параметру input -значение {@code true}. Если ваше приложение использует -библиотеку appcompat v7 или -библиотеку recyclerview v7, -вам необходимо установить актуальные версии этих библиотек. В противном случае убедитесь в том, -что любые настраиваемые классы, на которые имеются ссылки из XML, обновлены и их конструкторы классов доступны.

- -

В M Preview обновлено поведение динамического компоновщика. Теперь он распознает разницу между -{@code soname} библиотеки и путем к ней -(ошибка 6670, о которой сообщалось в открытых источниках), -и позволяет выполнять поиск по {@code soname}. - Приложения, которые ранее работали, но содержали неправильные записи {@code DT_NEEDED} -(обычно абсолютные пути в файловой системе компьютера для сборки), теперь при загрузке могут выдавать ошибки.

- -

В M Preview правильно реализован флаг {@code dlopen(3) RTLD_LOCAL}. Обратите внимание, что -{@code RTLD_LOCAL} используется по умолчанию, поэтому будут затронуты вызовы {@code dlopen(3)}, которые явно не используют -{@code RTLD_LOCAL} (за исключением случаев, когда ваше приложение явным образом использует{@code RTLD_GLOBAL}). Поскольку используется -{@code RTLD_LOCAL}, символы не будут доступны для библиотек, загруженных с использованием последующих вызовов -{@code dlopen(3)} (в противоположность ссылкам из записей {@code DT_NEEDED}).

-

- -

Проверка пакетов APK

-

Теперь платформа более строго подходит к проверке пакетов APK. Пакет APK считается поврежденным, если файл объявлен -в манифесте, но отсутствует в самом пакете APK. Пакет APK подлежит повторной подписи в случае удаления любого его -содержимого.

- -

Изменения в Android for Work

-

В M Preview представлены следующие изменения работы Android for Work:

-
    -
  • Рабочие контакты в контексте личных сведений. Теперь при просмотре пользователем прошлых звонков в журнале вызовов Google Dialer -в нем отображаются и рабочие контакты. -Чтобы скрыть эти сведения в журнале вызовов, установите для параметра {@code DevicePolicyManager.setCrossProfileCallerIdDisabled()} значение {@code true}. - Рабочие контакты могут отображаться на устройствах через Bluetooth вместе с -личными контактами только в том случае, если -для параметра {@code DevicePolicyManager.setBluetoothContactSharingDisabled()} задано значение {@code false}. По -умолчанию для этого параметра установлено значение {@code true}. -
  • -
  • Удаление конфигурации Wi-Fi. Конфигурации Wi-Fi, добавленные владельцем профиля -(например, посредством вызова метода -{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration) -addNetwork()}), теперь удаляются при удалении соответствующего рабочего профиля.
  • -
  • Блокировка конфигурации Wi-Fi. Пользователю больше не удастся изменить или удалить любые конфигурации Wi-Fi, созданные активным -владельцем устройства. Пользователь по-прежнему может создавать -и изменять свои собственные конфигурации Wi-Fi, если для этого пользователя не задана константа -{@link android.os.UserManager#DISALLOW_CONFIG_WIFI} {@link android.os.UserManager}.
  • -
  • Загрузка приложения «Контроллер политики работы» путем добавления учетной записи Google. Теперь при добавлении на устройство учетной записи Google, -для управления которой требуется приложение «Контроллер политики работы», -вне управляемого контекста пользователю предлагается установить соответствующее приложение. -Это также применимо к учетным записям, добавляемым посредством мастера первоначальной настройки устройства в разделе -Настройки > Учетные записи.
  • -
  • Изменения, касающиеся работы API-интерфейса DevicePolicyManager. -Вызов метода {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName,boolean) setCameraDisabled()} -затрагивает только использование камеры пользователем, который его вызвал; вызов этого метода из управляемого профиля -не влияет на работу приложений камеры, выполняющихся для основного пользователя. Кроме того, метод -{@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,int) setKeyguardDisabledFeatures()} -теперь доступен как для владельцев устройства, так и для владельцев профиля. Владелец профиля может задать -следующие ограничения для блокировки клавиатуры: -
      -
    • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} и -{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_FINGERPRINT}, которые влияют на настройки -блокировки клавиатуры для родительского пользователя профиля;
    • -
    • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, которое -влияет только на уведомления, создаваемые приложениями в управляемом профиле.
    • -
    -
  • -
diff --git a/docs/html-intl/intl/ru/preview/download.jd b/docs/html-intl/intl/ru/preview/download.jd deleted file mode 100644 index 9afc62bb4ce4b31b9099e351b2c1d7842d51b270..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/download.jd +++ /dev/null @@ -1,360 +0,0 @@ -page.title=Загрузки -page.image=images/cards/card-download_16-9_2x.png - -@jd:body - -
- - - - -
- - - - -

- В состав SDK Android M Preview входят инструменты для разработки, системные файлы Android и файлы библиотеки, призванные -помочь вам в тестировании ваших приложений и новых API-интерфейсов, которые будут реализованы в предстоящем выпуске платформы. В этом документе -рассказывается, как загрузить необходимые компоненты Preview для тестирования ваших приложений. -

- - -

Android 6.0 SDK

- -

- Загрузить SDK Preview можно с помощью менеджера SDK Android. Дополнительные сведения -о загрузке и настройке SDK Preview представлены в статье Настройка SDK Preview. -

- - -

Документация для разработчиков

- -

- В пакете документации для разработчиков, который доступен для загрузки, представлены подробные сведения об API-интерфейсах, а также о различиях между API-интерфейсами для Preview. -

- - - - - - - - - - -
DescriptionDownload / Checksums
Android M Preview 3
Developer Docs
m-preview-3-developer-docs.zip
- MD5: d99b14b0c06d31c8dfecb25072654ca3
- SHA-1: 9cefeeda07676130da606a1796e1c00fffc667c1 -
- - -

Системные образы оборудования

- -

- С помощью этих системных образов можно установить предварительную версию платформы на физическое устройство для -тестирования. Используя один из этих образов для настройки устройства, вы можете установить и протестировать ваше приложение, чтобы узнать, как оно будет работать в будущей версии -платформы. В процессе установки системного образа -на устройстве удаляются все данные, поэтому перед установкой образа обязательно сделайте резервное копирование данных. - -

- -

- Предупреждение. Перечисленные ниже системные образы Android являются предварительными и могут быть изменены. Использование вами -этих образов регулируется Лицензионным соглашением на использование пакета SDK Android Preview. Системные образы предварительной версии Android -не являются стабильными и могут содержать ошибки и дефекты, -способные повредить ваши компьютеры, устройства и данные. Системные образы предварительной версии Android не проходят такое же тестирование, -как заводская ОС. Поэтому в результате использования этих образов ваш телефон и установленные на нем приложения -и службы могут перестать работать. -

- - - - - - - - - - - - - - - - - - - - - - - - -
DeviceDownload / Checksums
Nexus 5 (GSM/LTE)
"hammerhead"
hammerhead-MPA44I-preview-2ebbc049.tgz
- MD5: 91a924fb0c9f8e716e3b4c9954fd0dbb
- SHA-1: 2ebbc049b68c4da8baeee3e42bb94d7a965ba4a3 -
Nexus 6
"shamu"
shamu-MPA44I-preview-62b9c486.tgz
- MD5: ac6e58da86125073d9c395257fd42664
- SHA-1: 62b9c486fd7a5020e228d53ca5acd5c1857e48ff -
Nexus 9
"volantis"
volantis-MPA44I-preview-5c30a6e2.tgz
- MD5: 7f83768757913d3fea945a661020d185
- SHA-1: 5c30a6e2acd11a81f4105b12d23ff654f534f699 -
Nexus Player
"fugu"
fugu-MPA44I-preview-2860040a.tgz
- MD5: 438da8d37da9e341a69cfb16a4001ac5
- SHA-1: 2860040a326582f1ff5f702bf9a1ef002717fc98 -
- -

Установка образа на устройство

- -

- Чтобы воспользоваться образом устройства для тестирования, установите его на совместимое устройство. Следуйте -инструкциям по установке системного образа. -

- -
    -
  1. Загрузите и распакуйте один из указанных выше пакетов с системным образом.
  2. -
  3. Создайте резервные копии данных, которые хотите сохранить.
  4. -
  5. Следуйте инструкциям, приведенным на сайте -developers.google.com/android, -чтобы прошить ваше устройство с использованием выбранного образа.
  6. -
- -

- Примечание. После прошивки устройства для разработки с использованием системного образа с предварительной версией платформы -она будет автоматически обновлена до следующего выпуска Preview по беспроводной сети. -

- -

Сброс параметров устройства до заводских настроек

- -

- Чтобы удалить Preview и восстановить заводские настройки устройства, перейдите на сайт -developers.google.com/android -и загрузите образ, который требуется использовать для прошивки. Следуйте инструкциям, приведенным на странице, -чтобы прошить ваше устройство с использованием выбранного образа. -

- -
- -
- - - - diff --git a/docs/html-intl/intl/ru/preview/features/app-linking.jd b/docs/html-intl/intl/ru/preview/features/app-linking.jd deleted file mode 100644 index f991717b1ea6e093efbe530008dfa9cb1b6264ce..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/features/app-linking.jd +++ /dev/null @@ -1,123 +0,0 @@ -page.title=Связи приложений -page.image=images/cards/card-app-linking_2x.png -page.keywords=связывание приложений, прямые ссылки, намерения -@jd:body - - - -

- Система намерений Android представляет собой гибкий механизм, позволяющий приложениям обрабатывать контент и запросы. - Многие приложения могут объявлять в своих фильтрах намерений соответствующие шаблоны URI. При нажатии пользователем на веб-ссылку -, у которой нет обработчика запуска по умолчанию, платформа может выдать ему диалоговое окно со списком доступных - приложений, объявивших соответствующие фильтры намерений. -

- -

- В Android M Developer Preview представлена поддержка связей приложений. Она выгодно отличается от существующих методов обработки ссылок, - предоставляя разработчикам возможность связывать приложение с принадлежащим им веб-доменом. Благодаря этой связи -платформа автоматически, не спрашивая у пользователя, определит приложение, которое следует использовать по умолчанию для обработки определенной -веб-ссылки. -

- - -

Объявление связи с веб-сайтом

- -

- Чтобы установить связь, владельцу сайта следует объявить связь с приложением. Для этого владелец -размещает в домене файл JSON с именем {@code statements.json}. Местопложение файла должно быть хорошо известно. - -

- -
http://<domain>:<optional port>/.well-known/statements.json
- -

- Примечание. - В период использования M Developer Preview проверка файла JSON выполняется по протоколу HTTP. В официальном -выпуске платформы она будет осуществляться по зашифрованному протоколу HTTPS. -

- -

- В этом файле JSON содержатся сведения о приложении Android, которое следует использовать в качестве обработчика по умолчанию для URL -в этом домене. Для определения приложения используются следующие поля: -

- -
    -
  • {@code package_name}: имя пакета, объявленное в манифесте приложения;
  • - -
  • {@code sha256_cert_fingerprints}: контрольная сумма SHA256 сертификата, который использовался для подписи вашего приложения. - Чтобы сгенерировать контрольную сумму, можно выполнить следующую команду в программе Java keytool: -
    keytool -list -v -keystore my-release-key.keystore
    -
  • -
- -

- Ниже представлен пример содержимого и формата файла -{@code statements.json}. -

- -
-[{
-  "relation": ["delegate_permission/common.handle_all_urls"],
-  "target": {
-    "namespace": "android_app",
-    "package_name": "<package name>",
-    "sha256_cert_fingerprints": ["6C:EC:C5:0E:34:AE....EB:0C:9B"]
-  }
-}]
-
- - - - -

- Приложение может отправить платформе запрос об автоматической проверке любых связей приложений, определенных в именах узлов в элементах данных -его фильтров намерений, с использованием файлов {@code statements.json}, размещенных в -соответствующих веб-доменах. Чтобы запросить проверку связи приложений, добавьте к каждому необходимому фильтру намерений в манифесте атрибут {@code android:autoVerify}, -как показано в следующем фрагменте кода -манифеста: -

- -
-<activity ...>
-    <intent-filter android:autoVerify="true">
-        <action android:name="android.intent.action.VIEW" />
-        <category android:name="android.intent.category.DEFAULT" />
-        <category android:name="android.intent.category.BROWSABLE" />
-        <data android:scheme="http" android:host="www.android.com" />
-        <data android:scheme="https" android:host="www.android.com" />
-    </intent-filter>
-</activity>
-
- -

- Если в манифесте приложения присутствует атрибут {@code android:autoVerify}, платформа пытается -проверить связи приложений при установке приложения. Если платформе не -удается сделать это, приложение не выбирается в качестве предпочтительного для обработки веб-ссылок. В следующий раз, когда пользователь -нажмет на одну из ссылок, платформа снова отобразит для него -соответствующее диалоговое окно. -

- -

- Примечание. При тестировании существует вероятность ложных срабатываний, когда проверка завершается -сбоем, но при этом пользователь установил в системном приложении «Настройки» ваше приложение как используемое по умолчанию при вызове поддерживаемых ссылок -. В таком случае диалоговое окно не отображается, а ссылка ведет напрямую в ваше приложение, -однако это происходит из-за настроек пользователя и не означает, что проверка пройдена успешно. -

- - -

Настройки связи приложений

- -

- Пользователи могут изменить настройки связи приложений и выбрать предпочтительный для них способ обработки URL. Для просмотра связей приложений и управления ими -можно воспользоваться системным приложением «Настройки» (в разделе Настройки > Приложения > Информация о приложении > -Открывать по умолчанию. -

diff --git a/docs/html-intl/intl/ru/preview/features/runtime-permissions.jd b/docs/html-intl/intl/ru/preview/features/runtime-permissions.jd deleted file mode 100644 index 7d12b2d3efe1a47f881b0d52639df78f92335424..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/features/runtime-permissions.jd +++ /dev/null @@ -1,794 +0,0 @@ -page.title=Разрешения -page.tags=previewresources, androidm -page.keywords=разрешения, среда выполнения, предварительная версия -page.image={@docRoot}preview/features/images/permissions_check.png -@jd:body - - -
-
-

Краткое описание

-
    -
  • Если ваше приложение предназначено для пакета SDK M Preview, пользователю будет предложено предоставить для него разрешения - не в процессе установки, а при работе с приложением.
  • -
  • Пользователь также может в любой момент отозвать разрешения, воспользовавшись экраном приложения «Настройки» -.
  • -
  • Ваше приложение должно проверять наличие соответствующих разрешений при каждом запуске. -
  • -
- -

Содержание документа

-
    -
  1. Обзор
  2. -
  3. Добавление в код разрешений на выполнение
  4. -
  5. Тестирование разрешений на выполнение
  6. -
  7. Советы и рекомендации
  8. -
- - - - -
-
- - -

- В версии M Developer Preview представлена новая модель разрешений для приложений, которая -оптимизирует для пользователей процесс установки и обновления приложений. Если приложение, -работающее в M Preview, поддерживает новую модель разрешений, пользователю не нужно предоставлять какие-либо -разрешения при установке приложения или его обновлении. Вместо этого приложение -запрашивает разрешения, когда в них возникает необходимость, — в таких случаях система отображает для пользователя диалоговое окно с просьбой предоставить -соответствующее разрешение. -

- -

- Если приложение поддерживает новую модель разрешений, его, тем не менее, можно установить и -запустить на устройстве под управлением одной из более ранних версий Android, и оно будет использовать старую модель -разрешений. -

- -

- Обзор -

- -

- В версии M Developer Preview представлена новая -модель разрешений. Ниже приводится краткий обзор ее ключевых компонентов: -

- -
    -
  • - Объявление разрешений. Все разрешения, которые требуются -приложению, объявляются в его манифесте, как и в более ранних версиях платформы Android. -
  • - -
  • - Группы разрешений. Разрешения -группируются по типу их функциональных возможностей. Например, в группе разрешений -CONTACTS находятся разрешения на чтение и запись -контактов пользователя и информации из его профиля. -
  • - -
  • -

    Ограниченные разрешения, предоставляемые во время установки. Когда -пользователь устанавливает или обновляет приложение, система предоставляет такому приложению все запрашиваемые им -разрешения, соответствующие константе {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}. - Например, разрешения для будильника и подключения к Интернету соответствуют константе {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}, -поэтому они предоставляются автоматически во время установки. -

    - -

    Система также может предоставить приложению подпись и системные разрешения, как описано в разделе -Предоставление приложениям системных разрешений и -подписи. Пользователю не предлагается предоставить какие-либо разрешения -во время установки.

    -
  • - -
  • - Предоставление пользователем разрешений во время выполнения. Когда приложение запрашивает разрешение, -система отображает для пользователя соответствующее диалоговое окно, а затем вызывает функцию обратного вызова приложения -с целью уведомить его о том, предоставлены ли необходимые разрешения. Если -пользователь предоставляет разрешение, приложение получает весь набор разрешений в рамках данной функциональной области -разрешения, который был объявлен в манифесте приложения. -
  • - -
- -

- Новая модель разрешений влияет на поведение приложений при работе с функциями, для -которых требуются разрешения. При использовании этой модели в разработке - обратите внимание на следующие рекомендации: -

- -
    - -
  • - Всегда проверяйте наличие разрешений. Когда приложению необходимо выполнить -какое-либо действие, для которого требуется разрешение, оно должно сначала проверить, -имеется ли у него такое разрешение. Если разрешение отсутствует, приложение -должно запросить его. -
  • - -
  • - Если вам не дают разрешения, выходите из положения красиво. Если у приложения -нет нужного разрешения, оно должно обработать эту ошибку разумно. - Например, если разрешение требуется лишь для необязательной функции, приложение может -просто отключить ее. Если же приложение не может работать без данного разрешения, - оно может отключить все свои функциональные возможности и проинформировать пользователя, -что для работы ему требуется разрешение. -
  • - -
    - -

    - Рисунок 1. Экран разрешений в настройках приложения. -

    -
    - -
  • - Разрешения можно отзывать. Пользователи могут в любое время отозвать разрешения -приложения. Когда пользователь отзывает разрешения, -приложение не уведомляется об этом. Снова повторим, что приложение должно всегда проверять наличие -разрешений, прежде чем выполнять любые действия, для которых они необходимы. -
  • -
- -

- Примечание. Если приложение предназначено для M Developer Preview, оно -должно в обязательном порядке использовать новую модель разрешений. -

- -

- На момент выпуска M Developer Preview не все приложения Google в полной мере реализуют - новую модель разрешений. Google обновляет эти приложения -по мере разработки M Developer Preview, чтобы они должным образом поддерживали настройки -разрешений. -

- -

- Примечание. Если у вашего приложения имеется собственная поверхность API-интерфейса, прежде чем проксировать -разрешения, убедитесь сначала в том, что у вызывающей операции имеются надлежащие -разрешения на доступ к данным. -

- -

- Предоставление приложениям системных разрешений и подписи -

- -

- Как правило, когда пользователь устанавливает приложение, система предоставляет такому приложению только те -разрешения, которые соответствуют константе {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL -PROTECTION_NORMAL}. Однако в определенных ситуациях система предоставляет -приложению больше разрешений: -

- -
    -
  • если приложение является частью системного образа, ему автоматически предоставляются все -разрешения, обозначенные в его манифесте; -
  • - -
  • если в манифесте приложения запрашиваются разрешения, соответствующие константе {@link -android.content.pm.PermissionInfo#PROTECTION_SIGNATURE PROTECTION_SIGNATURE}, -а для подписи приложения использовался то же сертификат, что и для приложения, -объявившего эти разрешения, система предоставляет запрашивающему приложению необходимые разрешения при -установке. -
  • -
- -

- В обоих случаях пользователь по-прежнему может в любое время отозвать разрешения, обратившись к -экрану Настройки и выбрав Приложения -> название_приложения > Разрешения. Поэтому приложение все равноу -должно проверять наличие разрешений во время выполнения и при необходимости запрашивать их. - -

- -

- Совместимость с предыдущими и последующими версиями -

- -

- Если приложение не предназначено для M Developer Preview, оно -продолжает использовать старую модель разрешений даже на устройствах под управлением M Preview. В таком случае при установке приложения - система предлагает пользователю предоставить все разрешения, -указанные в манифесте приложения. -

- -

- Примечание. На устройствах под управлением M Developer Preview пользователь может отключить -разрешения для любого приложения (включая устаревшие приложения) на экране «Настройки». - Если пользователь решит отключить разрешения для устаревших приложений, система -автоматически отключит соответствующие функциональные возможности. Когда приложение пытается -выполнить операцию, для которой требуется такое разрешение, это -не обязательно приведет к возникновению исключения. Вместо этого оно может выдать пустой набор данных, -сигнал об ошибке или иным образом обозначить непредвиденное поведение. Например, если запросить -календарь, не имея соответствующего разрешения, метод возвратит пустой набор данных. -

- -

- При установке приложения с использованием новой модели разрешений на устройство -под управлением другой версии ОС, отличной от M Preview, -система рассматривает такое приложение как любое другое и предлагает -пользователю предоставить все объявленные разрешения уже во время установки. -

- -

- Примечание. В случае с предварительной версией в качестве минимальной версии пакета SDK -следует задать версию SDK M Preview, чтобы получить возможность компилировать код с помощью пакета SDK предварительной версии. Это означает, -что вы не сможете протестировать такие приложения на старых платформах -во время использования предварительной версии для разработчиков. -

- -

Разрешения и намерения

- -

- Во многих случаях при разработке приложения у вас есть выбор между двумя способами выполнения задачи: - вы можете настроить приложение таким образом, чтобы оно самостоятельно запрашивало соответствующие разрешения на выполнение -операции, или же можно указать ему использовать намерение, чтобы задачу выполнило -другое приложение. -

- -

- Например, предположим, что вашему приложению требуется возможность делать снимки с помощью камеры устройства. - Ваше приложение может запросить разрешение -android.permission.CAMERA, которое позволит ему напрямую получить доступ -к камере. Затем ваше приложение использует API-интерфейсы камеры -для управления камерой и получения снимков. Благодаря такому подходу ваше приложение получает -полный контроль над процессом фотографирования. Кроме того, это позволяет вам вставить пользовательский интерфейс камеры -в свое приложение. -

- -

- Если же вам не требуется такой контроль, просто воспользуйтесь намерением {@link -android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} -для запроса изображения. Когда вы запускаете намерение, пользователю предлагается выбрать -приложение камеры (если оно отличается от приложения камеры по умолчанию), после чего -это приложение делает снимок. Приложение камеры возвращает полученное изображение в метод {@link -android.app.Activity#onActivityResult onActivityResult()} вашего приложения. -

- -

- Аналогичным образом, если вам необходимо позвонить, получить доступ к контактам пользователя и так далее, -можно создать соответствующее намерение или запросить -разрешение и напрямую получить доступ к нужным объектам. У каждого подхода есть -как преимущества, так и недостатки. -

- -

- При использовании разрешений: -

- -
    -
  • Ваше приложение получает полный контроль над взаимодействием пользователя с интерфейсом во время выполнения -операции. Однако такой широкий контроль усложняет вашу задачу, -требуя разработать подходящий пользовательский интерфейс. -
  • - -
  • Пользователю предлагается предоставить разрешения только один раз, при первом -выполнении операции. После этого ваше приложение может выполнять операцию без вмешательства -со стороны пользователя. Однако если пользователь не -предоставит разрешение (или отзовет его позже), ваше приложение не сможет выполнить -операцию. -
  • -
- -

- При использовании намерений: -

- -
    -
  • Вам не нужно разрабатывать пользовательский интерфейс для выполнения операции, его предоставляет приложение, которое обрабатывает -намерение. Однако это также означает, что у вас отсутствует контроль над -взаимодействием пользователя с интерфейсом. Возможно, пользователю придется взаимодействовать с -приложением, которое вы даже не видели. -
  • - -
  • Если у пользователя нет приложения по умолчанию для выполнения операции, система -предлагает ему выбрать приложение. Если пользователь не назначит обработчик -по умолчанию, то при каждом выполнении операции для него может -отображаться дополнительное диалоговое окно. -
  • -
- -

Добавление в код разрешений на выполнение

- -

- Если ваше приложение предназначено для новой версии M Developer Preview, вы должны в обязательном порядке использовать -новую модель разрешений. Это означает, что кроме объявления требуемых разрешений -в манифесте, вам следует проверять наличие этих разрешений -во время выполнения, а также запрашивать их, если у вас -еще нет необходимых разрешений. -

- -

- Активация новой модели разрешений -

- -

- Чтобы активировать новую модель разрешений M Developer Preview, задайте для атрибута -targetSdkVersion приложения значение "MNC", а для атрибута -compileSdkVersion – значение "android-MNC". Это позволит -включить все функции новой модели разрешений. -

- -

- В случае с предварительной версией необходимо задать для параметра minSdkVersion значение -"MNC", чтобы получить возможность компилировать код с помощью пакета SDK предварительной версии. -

- -

- Назначение разрешений только для M Preview -

- -

- В манифесте приложения можно использовать новый элемент <uses-permission-sdk-m>, -чтобы указать, что разрешение требуется только для M Developer Preview. Если объявить -разрешение таким способом, то при установке приложения на устройство с более старой версией платформы -система не будет отправлять запрос пользователю или предоставлять разрешение приложению. -С помощью элемента <uses-permission-sdk-m> -вы можете добавлять новые разрешения -в обновленные версии вашего приложения без принудительного запроса у пользователей разрешений при -установке обновления. -

- -

- Если приложение запущено на устройстве под управлением M Developer Preview, -поведение элемента <uses-permission-sdk-m> аналогично поведению -<uses-permission>. - Система не запрашивает у пользователей предоставление каких-либо разрешений, когда они устанавливают -приложение. Вместо этого приложение само запрашивает разрешения, когда они требуются. -

- -

- Запрос разрешений -

- -

- Если ваше приложение использует новую модель разрешений M Developer Preview, то при первом запуске приложения -на устройстве под управлением -M Preview пользователю не предлагается предоставить все разрешения. Вместо этого приложение само запрашивает разрешения, когда они -требуются. Когда приложение запрашивает разрешение, система отображает для пользователя соответствующее диалоговое -окно. -

- -

- Если ваше приложение запущено на устройстве с пакетом SDK уровня 22 или более низкого, то приложение использует старую -модель разрешений. То есть при каждой устновке приложения пользователю будет предложено предоставить приложению все разрешения, -запрашиваемые в манифесте приложения, кроме -отмеченных элементом <uses-permission-sdk-m>. -

- -

Проверка платформы, на которой выполняется приложение

- -

- Новая модель разрешений поддерживается только на устройствах под управлением M Developer -Preview. Прежде чем вызывать любые из этих методов, приложению следует проверить, -на какой платформе оно выполняется, -обратившись к значению параметра {@link android.os.Build.VERSION#CODENAME -Build.VERSION.CODENAME}. Если устройство работает под управлением M Developer Preview, -то значение параметра{@link android.os.Build.VERSION#CODENAME CODENAME} будет "MNC". -

- -

Проверка наличия у приложения необходимого разрешения

- -

Когда пользователи пытаются выполнить какое-либо действие, для которого требуется разрешение, приложение -проверяет, имеется ли у него в настоящее время разрешение на выполнение этой операции. Для этого -приложение вызывает метод -Context.checkSelfPermission(permission_name). Приложению -следует выполнять такую проверку даже в том случае, если ему известно, что пользователь уже предоставил -необходимое разрешение, -поскольку пользователь может в любое время отозвать разрешения приложения. Например, если пользователь -хочет получить снимок с помощью приложения, то приложение вызывает метод -Context.checkSelfPermission(Manifest.permission.CAMERA).

- -

- Таблица 1. Разрешения и группы разрешений.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Группа разрешенийРазрешения
android.permission-group.CALENDAR -
    -
  • - android.permission.READ_CALENDAR -
  • -
-
    -
  • - android.permission.WRITE_CALENDAR -
  • -
-
android.permission-group.CAMERA -
    -
  • - android.permission.CAMERA -
  • -
-
android.permission-group.CONTACTS -
    -
  • - android.permission.READ_CONTACTS -
  • -
  • - android.permission.WRITE_CONTACTS -
  • -
  • - android.permission.READ_PROFILE -
  • -
  • - android.permission.WRITE_PROFILE -
  • -
-
android.permission-group.LOCATION -
    -
  • - android.permission.ACCESS_FINE_LOCATION -
  • -
  • - android.permission.ACCESS_COARSE_LOCATION -
  • -
-
android.permission-group.MICROPHONE -
    -
  • - android.permission.RECORD_AUDIO -
  • -
-
android.permission-group.PHONE -
    -
  • - android.permission.READ_PHONE_STATE -
  • -
  • - android.permission.CALL_PHONE -
  • -
  • - android.permission.READ_CALL_LOG -
  • -
  • - android.permission.WRITE_CALL_LOG -
  • -
  • - com.android.voicemail.permission.ADD_VOICEMAIL -
  • -
  • - android.permission.USE_SIP -
  • -
  • - android.permission.PROCESS_OUTGOING_CALLS -
  • -
-
android.permission-group.SENSORS -
    -
  • - android.permission.BODY_SENSORS -
  • -
-
    -
  • - android.permission.USE_FINGERPRINT -
  • -
-
android.permission-group.SMS -
    -
  • - android.permission.SEND_SMS -
  • -
  • - android.permission.RECEIVE_SMS -
  • -
  • - android.permission.READ_SMS -
  • -
  • - android.permission.RECEIVE_WAP_PUSH -
  • -
  • - android.permission.RECEIVE_MMS -
  • -
  • - android.permission.READ_CELL_BROADCASTS -
  • -
-
- -

Запрос разрешений при необходимости

- -

Если у приложения нет требуемого разрешения, оно вызывает метод -Activity.requestPermissions(String[], int), чтобы запросить его. - Приложение передает в него -требуемые разрешения, а также целочисленный код запроса. - Этот метод выполняется асинхронно: он возвращает результат сразу же, а после того как пользователь -предоставляет ответ в диалоговом окне, система вызывает метод обратного вызова -приложения с результатами и передает в него тот же код запроса, который приложение передало в метод -requestPermissions().

- -

Ниже представлен пример кода для проверки наличия у приложения разрешения на чтение контактов -пользователя и запроса соответствующего разрешения при необходимости.

- -
-if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
-        != PackageManager.PERMISSION_GRANTED) {
-    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
-            MY_PERMISSIONS_REQUEST_READ_CONTACTS);
-
-    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
-    // app-defined int constant
-
-    return;
-}
-
- -

Обработка ответа на запрос разрешений

- -

- Когда приложение запрашивает разрешения, система отображает для пользователя соответствующее диалоговое -окно. После получения ответа от пользователя система вызывает метод -Activity.onRequestPermissionsResult(int, String[], int[]) -вашего приложения и передает в него ответ пользователя. Вашему приложению необходимо переопределить этот метод. В обратном -вызове передается тот же код запроса, который вы передали в метод -requestPermissions(). Например, если приложение запрашивает доступ на -READ_CONTACTS, то метод обратного вызова -может быть следующим: -

- -
-@Override
-public void onRequestPermissionsResult(int requestCode,
-        String permissions[], int[] grantResults) {
-    switch (requestCode) {
-        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
-            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-
-                // permission was granted, yay! do the
-                // calendar task you need to do.
-
-            } else {
-
-                // permission denied, boo! Disable the
-                // functionality that depends on this permission.
-            }
-            return;
-        }
-
-        // other 'switch' lines to check for other
-        // permissions this app might request
-    }
-}
-
- -

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

- -

- Когда система предлагает пользователю предоставить разрешение, он может указать системе, -чтобы она больше не запрашивала это разрешение. В этом случае, -когда приложение использует метод requestPermissions() для запроса такого разрешения, -система сразу же отклоняет запрос. При этом система вызывает ваш метод -onRequestPermissionsResult() так же, как если бы пользователь повторно -отклонил ваш запрос. Поэтому ваше приложение -не считает, что имело место прямое взаимодействие с пользователем. -

- -

Тестирование разрешений на выполнение

- - -

- Если ваше приложение предназначено для новой версии M Developer Preview, то вы должны протестировать -его и убедиться в том, что оно должным образом обрабатывает разрешения. Не надо исходить из того, что к началу работы ваше приложение уже имеет -любые определенные разрешения. При первом запуске приложения оно, -скорее всего, не будет обладать разрешениями, а в дальнейшем пользователь может в любой момент отозвать или восстановить -разрешения. -

- -

- Вам следует протестировать ваше приложение и убедиться в том, что оно ведет себя должным образом в -любых ситуациях, касающихся разрешений. Мы включили в состав пакета SDK M Preview SDK новые команды -Android -Debug Bridge (ADB), чтобы вы могли протестировать ваше приложение с любыми настройками разрешений, -которые вы хотите попробовать в действии. -

- -

- Новые команды и параметры ADB -

- -

- В состав пакета инструментов SDK M Preview входит ряд новых команд, позволяющих протестировать обработку разрешений -вашим приложением. -

- -

- Установка с разрешениями -

- -

- Можно воспользоваться новым параметром -g команды adb - install, который служит для установки -приложения и предоставления всех разрешений, указанных в его манифесте: -

- -
-$ adb install -g <path_to_apk>
-
- -

- Предоставление разрешений и их отзыв -

- -

- Для предоставления разрешений установленному приложению и их отзыва можно использовать новые команды диспетчера пакетов -ADB. -Такая функциональная возможность может быть полезна для автоматизированного тестирования. -

- -

- Для представления разрешения используйте команду grant диспетчера пакетов: -

- -
-$ adb pm grant <package_name> <permission_name>
-
- -

- Например, чтобы представить пакету com.example.myapp разрешение на запись -аудио, воспользуйтесь следующей командой: -

- -
-$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO
-
- -

- Чтобы отозвать разрешение, используйте команду revoke диспетчера пакетов: -

- -
-$ adb pm revoke <package_name> <permission_name>
-
- -

Советы и рекомендации

- -

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

- - -

Запрашивайте только те разрешения, которые необходимы

- -

- Каждый раз, когда вы запрашиваете разрешение, вы заставляете пользователя делать выбор. - Если пользователь отклоняет запрос, соответствующая функциональная возможность приложения отключается. - Вам следует сократить количество таких запросов. -

- -

- Например, во многих случаях можно предоставить приложению доступ к нужной функции посредством -намерения вместо -запроса разрешений. Если вашему приложению необходимо получить снимки с камеры -телефона, можно использовать намерение {@link -android.provider.MediaStore#ACTION_IMAGE_CAPTURE -MediaStore.ACTION_IMAGE_CAPTURE}. Когда ваше приложение выполняет намерение, система -предлагает пользователю выбрать уже установленное приложение камеры для -получения снимков. -

- -

- Не перегружайте пользователя запросами -

- -

- Если на пользователя обрушивается сразу много запросов разрешений, он может -решить, что все это слишком сложно, и просто закрыть приложение. Поэтому разрешения следует -запрашивать только тогда, когда это необходимо. -

- -

- Иногда приложению жизненно необходимы одно или несколько разрешений. -В таких случаях имеет смысл запросить все разрешения -сразу при запуске приложения. Например, если вы разрабатываете приложение для фотографирования, то ему -однозначно потребуется доступ к камере устройства. Когда пользователь в первый раз запускает приложение, -он не удивится, если приложение запросит у него разрешение на -использование камеры. Однако если в этом же приложении имеется функция обмена фотографиями с -контактами пользователя, возможно, не следует запрашивать соответствующее разрешение при -первом запуске приложения. Лучше дождаться, когда пользователю потребуется функция обмена контентом, -и уже тогда запросить разрешение. -

- -

- Если в вашем приложении имеются обучающие материалы, может оказаться целесообразным запросить необходимые разрешения -после того, как пользователь изучит материалы. -

- -

- Всегда поясняйте, для чего требуются те или иные разрешения -

- -

- В диалоговом окне запроса разрешений, которое система отображает при вызове вами метода -requestPermissions(), обозначается требуемое для приложения разрешение, -однако не указывается, для чего оно необходимо. В некоторых случаях это может озадачить пользователя. - Поэтому, прежде чем вызывать метод -requestPermissions(), стоит пояснить пользователю, для чего вашему приложению требуются разрешения. -

- -

- Например, приложению для фотографирования может потребоваться доступ к службам геолокации, чтобы фотографии можно было снабдить -геотегами. Не все пользователи знают, что -фотография может содержать данные о месте съемки, и им может показаться странным, что приложение для фотографирования запрашивает данные -о местоположении. В этом случае полезно -рассказать пользователю о такой возможности, прежде чем вызывать метод -requestPermissions(). -

- -

- Это можно сделать, в частности, внедрив такие запросы в обучающие материалы приложения. В обучающих -материалах могут содержаться описания каждой из функций приложения с пояснением, -какие разрешения необходимы для их использования. Например, в обучающий материал по работе с приложением для фотографирования -можно включить описание функции обмена контентом с контактами, после чего -пояснить, что для этого пользователю следует предоставить приложению доступ к его контактам. - После этого приложение может вызвать метод requestPermissions() для запроса -доступа. Конечно, не все пользователи обращаются к обучающим материалам, -поэтому вам по-прежнему следует проверять наличие разрешений и при необходимости запрашивать -их во время обычной работы приложения. -

diff --git a/docs/html-intl/intl/ru/preview/index.jd b/docs/html-intl/intl/ru/preview/index.jd deleted file mode 100644 index 18174a9f8d6a36cc19069016874855ef0f057be5..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/index.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Android M Developer Preview -page.tags="preview", -meta.tags="preview, M preview", androidm -fullpage=true -section.landing=true -header.hide=1 -footer.hide=1 -@jd:body - -
-
-
-
- -
-
-

Android M Developer Preview

-

- Подготовьтесь к выходу следующей версии платформы Android. Протестируйте ваши приложения на устройствах Nexus 5, 6, 9 и -Player. Узнайте о новых возможностях:— разрешениях на выполнение, -новых функциях энергосбережения — режим «Doze» и ждущий режим для приложений, о новом -помощнике и о многом другом. -

- - - - Итак, приступим! -
- - - Developer Preview 3 (final SDK) -
-
-
-
-
-
-
- -
-

Ресурсы

-
- Важная информация, которая поможет вам подготовить ваши приложения для работы в Android M. -
- -
- - -
-
- diff --git a/docs/html-intl/intl/ru/preview/license.jd b/docs/html-intl/intl/ru/preview/license.jd deleted file mode 100644 index fe9901b0a61ed6cac6cae1a1ba23610c17279be0..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/license.jd +++ /dev/null @@ -1,143 +0,0 @@ -page.title=Лицензионное соглашение - -@jd:body - -

-Чтобы приступить к работе с пакетом SDK Android Preview, примите указанные ниже положения и условия. -Как описано ниже, обратите внимание, что это предварительная версия пакета SDK Android, которая может быть изменена и которую вы используете на свой страх и риск. Пакет SDK Android Preview не является стабильным и может содержать ошибки и дефекты, которые могут повредить ваши компьютеры, устройства и данные. -

- -

-Это лицензионное соглашение для пакета SDK Android Preview (далее «Лицензионное соглашение»). -

-
-1. Введение - -1.1. Лицензия на пакет SDK Android Preview (далее по тексту настоящего Лицензионного соглашения – «Preview», который включает системные файлы Android, пакеты API-интерфейсов и файлы библиотеки Preview, если такие доступны) передается в соответствии с положениями настоящего Лицензионного соглашения. Настоящее Лицензионное соглашение является юридически обязывающим договором между компанией Google и любым лицом, использующим Preview. - -1.2. В настоящем Лицензионном соглашении термин «Android» означает набор программного обеспечения Android для устройств, предлагаемый к использованию в рамках проекта Android Open Source Project, который доступен на веб-сайте http://source.android.com/ (сведения, размещенные на этом сайте, могут периодически обновляться). - -1.3. Под термином «Google» понимается корпорация Google Inc., главный офис которой находится по адресу 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States (США). - -2. Принятие лицензионного соглашения - -2.1. Использование Preview возможно только после принятия условий настоящего Лицензионного соглашения. Запрещается использовать Preview, если вы не согласны с указанными в настоящем документе условиями и положениями. - -2.2. Нажатие кнопки принятия условий и/или использование Preview означает, что вы согласны с положениями настоящего Лицензионного соглашения. - -2.3. Вы не вправе использовать Preview и принимать условия данного Лицензионного соглашения, если по законам США или иных стран, включая страну вашего проживания или использования Preview, запрещается передавать Preview в ваш адрес. - -2.4. Если вы используете Preview в рамках своей компании или организации, вы соглашаетесь взять на себя обязательства по соблюдению настоящего Лицензионного соглашения от имени своего работодателя или другого юридического лица, и вы тем самым подтверждаете и гарантируете, что обладаете полными юридическими полномочиями связать вашего работодателя или иное подобное юридическое лицо обязательствами по настоящему Лицензионному соглашению. Если вы не обладаете требуемыми полномочиями, вы не вправе принимать указанные в настоящем документе условия и положения или использовать Preview от имени вашего работодателя или другого юридического лица. - -3. Лицензия на Preview от Google - -3.1. В соответствии с условиями настоящего Лицензионного соглашения Google предоставляет вам ограниченную, бесплатную и неэксклюзивную лицензию без права передачи и подлежащую отмене, на использование Preview, лично или в рамках своей компании или организации, исключительно в целях разработки приложений для платформы Android. - -3.2. Вы соглашаетесь с тем, что Google или третьим сторонам принадлежат все юридические и имущественные права, а также правовой интерес в отношении Preview, в том числе любые права на объекты интеллектуальной собственности, которые имеются в Preview. Термин «Права на интеллектуальную собственность» означает все возможные права в рамках патентного права, авторского права, закона о коммерческой тайне, закона о товарных знаках, а также иные возможные имущественные права. Google оставляет за собой все права, не предоставленные вам в явном виде. - -3.3. Вам запрещается использовать Preview в любых целях, которые однозначно не определены в настоящем Лицензионном соглашении. За исключением случаев, предусмотренных применимыми сторонними лицензиями, вам запрещается: (a) копировать (за исключением резервного копирования), изменять, адаптировать, повторно распространять, декомпилировать, осуществлять инженерный анализ, деассемблировать или создавать производные элементы Preview или иной его части; а также (b) загружать любую часть Preview в мобильные телефоны или иные устройства, помимо персонального компьютера, объединять любые части Preview с другим программным обеспечением, распространять любое программное обеспечение или устройства, содержащие части Preview. - -3.4. Вы соглашаетесь с тем, что не будете предпринимать никаких действий, которые прямо или косвенно могут привести к фрагментированию платформы Android, включая помимо прочего распространение набора средств разработки программного обеспечения, полученных из Preview, участие в создании таких средств и содействие их продвижению в любой форме. - -3.5. Использование, воспроизведение и распространение компонентов Preview, на которые распространяется лицензия на программное обеспечение с открытым исходным кодом, регулируются исключительно положениями и условиями такой лицензии на программное обеспечение с открытым исходным кодом, а не настоящим Лицензионным соглашением. Вы соглашаетесь обеспечивать хорошую репутацию получателя лицензии в отношении таких лицензии на программное обеспечение с открытым исходным кодом в рамках всех предоставленных ему прав, а также не допускать каких-либо действий, которые могут привести к аннулированию, приостановлению или нарушению таких прав - -3.6. Вы соглашаетесь с тем, что форма и содержание Preview , предоставляемого Google, могут быть изменены без предварительного уведомления, а также с тем, что будущие версии Preview могут оказаться несовместимыми с приложениями, разработанными в предыдущих версиях Preview. Вы соглашаетесь с тем, что Google вправе на свое собственное усмотрение и без предварительного уведомления прекратить (временно или навсегда) предоставление Preview (или любых функций в составе Preview) вам или пользователям. - -3.7. Ни одна из частей настоящего Лицензионного соглашения не предусматривает предоставления вам права использовать любые торговые наименования, товарные знаки, знаки обслуживания, логотипы, имена доменов или иные отличительные фирменные знаки, принадлежащие Google. - -3.8. Вы соглашаетесь с тем, что обязуетесь не удалять, не скрывать или не изменять любые уведомления об имущественных правах (включая уведомления об авторских правах и товарных знаках), которые могут сопровождать Preview или содержаться в нем. - -4. Использование Preview - -4.1. Компания Google выражает согласие с тем, что ни по какому положению настоящего Лицензионного соглашения не получает от вас (или ваших лицензиаров) каких-либо юридических и имущественных прав, а также правового интереса в отношении любых программных приложений, разработанных вами с помощью Preview, включая любые права на объекты интеллектуальной собственности, которые имеются в таких приложениях. - -4.2. Вы соглашаетесь использовать Preview и создавать приложения исключительно в целях, предусмотренных (a) настоящим Лицензионным соглашением и (b) любым применимым законом, нормативным актом или общепринятыми правилами или рекомендациями в соответствующей юрисдикции (включая любые законы, касающиеся экспорта данных или программного обеспечения из США или иных соответствующих стран, а также импорта в них).4.3. Вы соглашаетесь с тем, что при использовании Preview для разработки приложений вы обязуетесь обеспечивать конфиденциальность данных и защищать юридические права пользователей. - -4.3. Вы соглашаетесь с тем, что при использовании SDK для разработки приложений для неопределенного круга пользователей вы обязуетесь обеспечивать конфиденциальность данных и защищать юридические права таких пользователей. В случае если пользователи предоставляют вам свои имена, пароли или иные данные для входа либо свои персональные сведения, вы обязуетесь уведомить пользователей о том, что такая информация будет присутствовать в вашем приложении, и вы также обязуетесь предоставить таким пользователям юридически соответствующее уведомление о конфиденциальности и средства правовой защиты. Если в вашем приложении хранится персональная или конфиденциальная информация, предоставленная пользователями, вы обязуетесь обеспечить ее надлежащую защиту. Если пользователь предоставляет вам сведения о своей учетной записи Google, то ваше приложение может использовать такую информацию для доступа к учетной записи Google пользователя только тогда, когда пользователь предоставил вам разрешение на это, и только в тех целях, которые обозначил пользователь. - -4.4. Вы соглашаетесь с тем, что обязуетесь не использовать Preview для любого рода деятельности, в том числе для разработки или распространения приложений, в целях нарушения работы и повреждения серверов, сетей или иной собственности или служб Google или любой третьей стороны. - -4.5. Вы соглашаетесь с тем, что несете единоличную ответственность (и признаете, что компания Google не несет ответственности ни перед вами, ни перед любой третьей стороной) за любые данные, содержимое или ресурсы, которые вы создаете, передаете или демонстрируете посредством Android и/или приложений для Android, а также за любые последствия ваших действий, связанных с этим (в том числе за любые убытки и любой ущерб, которые могут быть причинены Google). - -4.6. Вы соглашаетесь с тем, что несете единоличную ответственность (и признаете, что компания Google не несет ответственности ни перед вами, ни перед любой третьей стороной) за любое несоблюдение обязательств по настоящему Лицензионному соглашению, обязательств по любому применимому договору с третьей стороной или предусмотренных Условиями и положениями, за нарушение любых применимых законов или нормативных актов, а также за любые последствия ваших действий, связанных с таким нарушением (в том числе за любые убытки и любой ущерб, которые могут быть причинены Google). - -4.7 Preview находится на стадии разработки, поэтому ваши отзывы и результаты тестирования являются важной частью процесса разработки. Используя Preview, вы признаете, что реализация некоторых функций по-прежнему находится на этапе разработки и вам не следует рассчитывать на полную функциональность стабильной версии. Вы соглашаетесь не распространять или предоставлять любые приложения, использующие Preview, поскольку поддержка Preview будет прекращена после выпуска официальной версии пакета SDK Android. - -5. Ваши учетные данные разработчика - -5.1. Вы соглашаетесь с тем, что несете ответственность за обеспечение конфиденциальности любых учетных данных разработчика, которые компания Google может вам предоставить или которые вы можете самостоятельно выбрать, а также с тем, что вы несете единоличную ответственность за все приложения, разработанные с использованием ваших учетных данных разработчика. - -6. Конфиденциальность и личная информация - -6.1. В целях постоянного совершенствования и улучшения Preview компания Google вправе собирать определенные статистические данные об использовании программного обеспечения, включая уникальный идентификатор, связанный IP-адрес, номер версии программного обеспечения, а также сведения об используемых в Preview инструментах и/или службах и способах их применения. Перед тем как любые из таких данных будут отправлены в Google, в Preview отобразится соответствующее уведомление с просьбой дать свое согласие. В случае вашего отказа предоставить такие сведения соответствующие данные собираться не будут. - -6.2. Собранные данные изучаются в обобщенном виде с целью улучшения Preview и хранятся в соответствии с Политикой конфиденциальности Google, которая опубликована на веб-сайте по адресу http://www.google.com/policies/privacy/. - -7. Сторонние приложения - -7.1. Если вы используете Preview для запуска приложений, разработанных третьими сторонами или получающих доступ к данным, содержимому или ресурсам, предоставляемым третьей стороной, вы соглашаетесь с тем, что Google не несет ответственности за такие приложения, данные, содержимое или ресурсы. Вы осознаете, что единоличную ответственность за все данные, содержимое или ресурсы, доступ к которым вы можете получить посредством таких приложений третьих сторон, несет лицо, предоставившее их, а также то, что Google не несет ответственности за любые убытки или любой ущерб, которые могут возникнуть в результате использования вами любых таких сторонних приложений, данных, содержимого или ресурсов и в результате обращения к ним. - -7.2. Вы должны быть осведомлены о том, что данные, содержимое и ресурсы, предоставляемые вам посредством таких сторонних приложений, могут быть защищены правами на объекты интеллектуальной собственности, принадлежащие предоставляющим их лицам (или иным лицам либо компаниям от их имени). Вам запрещается изменять, сдавать в аренду, передавать, продавать, распространять такие данные, содержимое или ресурсы (полностью или частично), а также создавать на их основе производные элементы, если у вас нет на это разрешения от соответствующих владельцев. - -7.3. Вы осознаете, что использование вами таких сторонних приложений, данных или ресурсов может регулироваться отдельными условиями, заключенными между вами и соответствующей третьей стороной. - -8. Использование API-интерфейсов Google - -8.1. API-интерфейсы для получения данных Google. - -8.1.1. В случае использования вами любых API для получения данных из Google вы осознаете, что такие данные могут быть защищены правами на объекты интеллектуальной собственности, принадлежащие Google или предоставляющим их сторонам (или иным лицам либо компаниям от их имени). Использование вами подобных API может регулироваться дополнительными Условиями использования. Вам запрещается изменять, сдавать в аренду, передавать, продавать, распространять такие данные (полностью или частично), а также создавать на их основе производные элементы, если это не разрешено соответствующими Условиями использования. - -8.1.2. Если вы используете какие-либо API-интерфейсы для получения данных пользователя из Google, вы осознаете и соглашаетесь с тем, что вы обязуетесь получать такие данные исключительно с прямого согласия пользователя и только в тех целях, которые обозначил пользователь. - -9. Прекращение действия Лицензионного соглашения - -9.1 Настоящее Лицензионное соглашение остается в силе до тех пор, пока его действие не будет прекращено вами или Google, как указано ниже. - -9.2. Если вы желаете прекратить действие настоящего Лицензионного соглашения, вы вправе сделать это, прекратив использование Preview и любых соответствующих учетных данных разработчика. - -9.3. Google вправе в любое время прекратить действие настоящего Лицензионного соглашения, отправив предварительное уведомление или без него. - -9.4 Действие настоящего Лицензионного соглашения автоматически прекращается без предварительного уведомления или выполнения иных действий сразу после следующего: -(A) компания Google прекращает предоставление Preview или определенных частей Preview пользователям в той стране, в которой вы проживаете или используете услуги компании; -(B) компания Google выпускает окончательную версию SDK Android. - -9.5 В случае прекращения действия настоящего Лицензионного соглашения прекращается действие лицензии, предоставленной в рамках Лицензионного соглашения, и вам следует незамедлительно прекратить любое использование Preview, тогда как положения, изложенные в разделах 10, 11, 12 и 14 продолжают действовать бессрочно. - -10. ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ - -10.1. ВЫ ЧЕТКО ОСОЗНАЕТЕ И БЕЗОГОВОРОЧНО СОГЛАШАЕТЕСЬ С ТЕМ, ЧТО ВЫ ИСПОЛЬЗУЕТЕ PREVIEW ИСКЛЮЧИТЕЛЬНО НА СВОЙ СТРАХ И РИСК И ЧТО PREVIEW ПРЕДОСТАВЛЯЕТСЯ ВАМ НА УСЛОВИЯХ «КАК ЕСТЬ» И «КАК ДОСТУПНО» БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ СО СТОРОНЫ КОМПАНИИ GOOGLE. - -10.2 ИСПОЛЬЗОВАНИЕ ВАМИ PREVIEW И ЗАГРУЗКА ЛЮБЫХ МАТЕРИАЛОВ И ИХ ПОЛУЧЕНИЕ ИНЫМ СПОСОБОМ С ПОМОЩЬЮ PREVIEW ВЫПОЛНЯЕТСЯ ПО ВАШЕМУ СОБСТВЕННОМУ УСМОТРЕНИЮ НА СВОЙ СТРАХ И РИСК. ВСЯ ОТВЕТСТВЕННОСТЬ ЗА ЛЮБОЙ УЩЕРБ, ПРИЧИНЕННЫЙ ВАШЕЙ ВЫЧИСЛИТЕЛЬНОЙ СИСТЕМЕ ИЛИ ДРУГОМУ ОБОРУДОВАНИЮ, А ТАКЖЕ ЗА ПОТЕРЮ ДАННЫХ, ВЫЗВАННУЮ ПОДОБНЫМ ИСПОЛЬЗОВАНИЕМ, ВОЗЛАГАЕТСЯ НА ВАС. НЕ ОГРАНИЧИВАЯ ВЫШЕСКАЗАННОЕ, ВЫ ПОНИМАЕТЕ, ЧТО PREVIEW НЕ ЯВЛЯЕТСЯ СТАБИЛЬНЫМ ВЫПУСКОМ И МОЖЕТ СОДЕРЖАТЬ ОШИБКИ, ДЕФЕКТЫ И УЯЗВИМОСТИ В СИСТЕМЕ БЕЗОПАСНОСТИ, КОТОРЫЕ МОГУТ ПРИВЕСТИ К СЕРЬЕЗНЫМ ПОВРЕЖДЕНИЯМ, ВКЛЮЧАЯ ПОЛНУЮ И БЕЗВОЗВРАТНУЮ ПОТЕРЮ РАБОТОСПОСОБНОСТИ ВАШЕГО КОМПЬЮТЕРА ИЛИ ИНОГО УСТРОЙСТВА. - -10.3. КОМПАНИЯ GOOGLE БЕЗОГОВОРОЧНО ОТКАЗЫВАЕТСЯ ОТ ЯВНЫХ И НЕЯВНЫХ ГАРАНТИЙ И УСЛОВИЙ ЛЮБОГО РОДА, ВКЛЮЧАЯ ПОМИМО ПРОЧЕГО НЕЯВНЫЕ ГАРАНТИИ И УСЛОВИЯ ТОВАРНОГО СОСТОЯНИЯ, ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ И ОТСУТСТВИЯ НАРУШЕНИЙ ПРАВ СОБСТВЕННОСТИ. - -11. ОГРАНИЧЕНИЕ ОТВЕТСТВЕННОСТИ - -11.1. ВЫ ЧЕТКО ОСОЗНАЕТЕ И БЕЗОГОВОРОЧНО СОГЛАШАЕТЕСЬ С ТЕМ, ЧТО КОМПАНИЯ GOOGLE, ЕЕ ДОЧЕРНИЕ И АФФИЛИРОВАННЫЕ КОМПАНИИ И ЛИЦЕНЗИАРЫ НЕ НЕСУТ ПЕРЕД ВАМИ ОТВЕТСТВЕННОСТИ, НЕЗАВИСИМО ОТ ЕЕ ПРИЧИНЫ И ВИДА, ЗА КАКИЕ-ЛИБО ПРЯМЫЕ, КОСВЕННЫЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ, ОПОСРЕДОВАННЫЕ И ШТРАФНЫЕ УБЫТКИ, ПОНЕСЕННЫЕ ВАМИ, ВКЛЮЧАЯ ПОМИМО ПРОЧЕГО ПОТЕРЮ ДАННЫХ, ВНЕ ЗАВИСИМОСТИ ОТ ТОГО, БЫЛА ЛИ КОМПАНИЯ GOOGLE ИЛИ ЕЕ ПРЕДСТАВИТЕЛИ ИЗВЕЩЕНЫ О ВОЗМОЖНОСТИ ТАКОГО УЩЕРБА. - -12 Освобождение от ответственности - -12.1. В максимально допустимой законом степени вы соглашаетесь защищать, освобождать от ответственности и возможных претензий компанию Google, ее аффилированные компании и их соответствующих руководителей, служащих, сотрудников и агентов от всех возможных правовых требований, действий, судебных исков или разбирательств, а также от всех возможных убытков, обязательств, ущерба, издержек и расходов (включая обоснованные вознаграждения для адвокатов), возникающих (a) в связи с использованием вами Preview, (b) в связи с любыми приложениями, разрабатываемыми вами с помощью Preview и нарушающими любые права на объекты интеллектуальной собственности любого лица, а также порочащие любое лицо либо нарушающие права таких лиц на публичность и конфиденциальность, а также (c) в связи с любым несоблюдением вами положений настоящего Лицензионного соглашения. - -13. Изменения в Лицензионном соглашении - -13.1. Компания Google вправе вносить изменения в настоящее Лицензионное соглашение по мере выхода новых версий Preview. При внесении изменений Google создает новую версию Лицензионного соглашения и размещает ее на веб-сайте, на котором размещена Preview. - -14. Общие правовые условия - -14.1. Настоящее Лицензионное соглашение составляет полный текст юридического соглашения между вами и компанией Google, регулирует использование вами Preview (за исключением услуг, которые Google предоставляет на основании отдельного письменного соглашения) и полностью заменяет собой все предыдущие соглашения между вами и компанией Google в отношении Preview. - -14.2. Вы соглашаетесь с тем, что отсутствие каких-либо действий или судебных исков со стороны Google, направленных на соблюдение каких-либо правовых норм или исполнение средств правовой защиты, установленных настоящим Лицензионным соглашением (или которыми Google обладает в соответствии с каким-либо действующим законом), не означает отказ компании Google от своих прав и не препятствует компании Google использовать эти права или средства защиты. - -14.3. Если какой-либо судебный орган, уполномоченный рассматривать этот вопрос, признает недействительность какого-либо положения данного Лицензионного соглашения, то соответствующее положение будет исключено из Лицензионного соглашения с сохранением действия всех остальных его положений. Остальные положения Лицензионного соглашения по-прежнему будут действовать, и их соблюдение может обеспечиваться в судебном порядке. - -14.4. Вы признаете и соглашаетесь с тем, что все участники группы компаний, среди которых Google является материнской компанией, являются сторонними бенефициарами Лицензионного соглашения и что эти компании имеют право пользоваться привилегиями (или правами), предоставляемыми по настоящему Лицензионному соглашению, и напрямую требовать их соблюдения в судебном порядке. Все остальные физические и юридические лица не являются сторонними бенефициарами Лицензионного соглашения. - -14.5. ОГРАНИЧЕНИЯ НА ЭКСПОРТ. ИСПОЛЬЗОВАНИЕ PREVIEW РЕГУЛИРУЕТСЯ ЗАКОНАМИ И НОРМАТИВНЫМИ АКТАМИ США, КАСАЮЩИМИСЯ ЭКСПОРТА. ВЫ ОБЯЗУЕТЕСЬ СОБЛЮДАТЬ ВСЕ НАЦИОНАЛЬНЫЕ И МЕЖДУНАРОДНЫЕ ЗАКОНЫ ОБ ЭКСПОРТЕ, ПРИМЕНИМЫЕ К PREVIEW. ДАННЫЕ ЗАКОНЫ НАЛАГАЮТ ОГРАНИЧЕНИЯ НА РЕГИОНЫ, КРУГ ЛИЦ И СПОСОБ КОНЕЧНОГО ИСПОЛЬЗОВАНИЯ. - -14.6. Вы не вправе переуступать либо передавать права, предоставляемые по настоящему Лицензионному соглашению, без предварительного письменного согласия Google; любые попытки переуступки без такого согласия считаются недействительными. Вы обязуетесь не делегировать свои полномочия или обязательства по настоящему Лицензионному соглашению без предварительного письменного согласия Google. - -14.7. Лицензионное соглашение, а также взаимоотношения между вами и компанией Google в рамках настоящего Лицензионного соглашения регулируются законодательством штата Калифорния за исключением его норм коллизионного права. Вы и компания Google признаете, что урегулирование любых правовых вопросов, связанных с данным Лицензионным соглашением, относится исключительно к юрисдикции судов округа Санта-Клара, штат Калифорния. Несмотря на это, вы соглашаетесь с тем, что компания Google по-прежнему имеет право обращаться за наложением судебного запрета (или за получением аналогичного вида неотложной судебной защиты) в суды любой юрисдикции. - - -
\ No newline at end of file diff --git a/docs/html-intl/intl/ru/preview/overview.jd b/docs/html-intl/intl/ru/preview/overview.jd deleted file mode 100644 index d1345a2564002ad4082cafcafeafe27132b145bc..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/overview.jd +++ /dev/null @@ -1,389 +0,0 @@ -page.title=Обзор программы -page.metaDescription=Добро пожаловать в программу Android M Developer Preview, участники которой получают всё необходимое для тестирования и оптимизации своих приложений для следующей версии платформы Android. -page.image=images/cards/card-preview_16-9_2x.png -page.tags="preview", "developer", "android" - -@jd:body - -
-

- Developer Preview 2 is now available -

- - -
- -

- Добро пожаловать в программу Android M Developer Preview, участники которой получают всё необходимоедля тестирования и оптимизации своих приложений для следующей версии платформы Android. - - Это бесплатная программа, и приступить к ее использованию можно прямо сейчас, загрузив -инструменты M Developer Preview. -

- -
-
-
-
-
- Системные образы эмулятора и оборудования -
- -

- Запускайте и тестируйте ваши приложения на устройствах Nexus 5, 6, 9 и Player (для Android TV), а также в эмуляторе. - -

-
- -
-
- Самый актуальный код платформы -
- -

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

-
- -
-
- Получение обновлений по беспроводной связи -
- -

- После прошивки своего устройства для работы с предварительной версией платформы вы сможете получать обновления по беспроводной связи. - -

-
-
- -
- - -
-
- Новые возможности и новые функции -
- -

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

-
- -
-
- Приоритетная обработка отчетов об ошибках от разработчиков -
- -

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

-
- -
-
- Отзывы и поддержка -
- -

- Отправляйте нам отчеты об ошибках и предоставляйте свои отзывы с помощью нашей системы отслеживания проблем. - Обменивайтесь идеями и предложениями с другими разработчиками в сообществе разработчиков Android M. - -

-
-
-
-
- - - - -

- График и обновления -

-Preview program timeline -

- Тестирование версии M Developer Preview запланировано на период с 28 мая до выпуска окончательной версии пакета SDK Android M, который -состоится незадолго до публикации новой платформы в открытом доступе в -третьем квартале 2015 г. -

- -

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

- -
    -
  • - Preview 1 (первоначальный выпуск версии Preview, конец мая) -
  • - -
  • - Preview 2 (конец июня/начало июля) -
  • - -
  • - Preview 3 (почти окончательный выпуск, конец июля) -
  • -
- -

- Завершающим этапом обновлений станет выход окончательной версии пакета SDK (в третьем квартале), -где будут представлены официальные API-интерфейсы для новой версии Android, а также -окончательные версии функций и поведений системы. -

- -

- Мы настоятельно рекомендуем вам в ходе тестирования и разработки приложений для Android M постоянно -обновлять вашу среду разработки по мере выхода обновлений для версии Preview. - Чтобы упростить этот процесс, мы будем отправлять на устройства, которые уже прошиты для работы с предварительной версией платформы, обновления по беспроводной сети. -Мы также будем предоставлять вам системные образы, которые можно -загрузить и использовать для прошивки устройства вручную. -

-

- Примечание. Окончательные версии пакета SDK и системных образов не будут отправляться по беспроводной сети, -их придется вручную установить на -тестовые устройства. -

- -

- Мы будем сообщать о появлении обновлений для версии Preview в блоге разработчиков Android, а -также на этом сайте и в -сообществе разработчиков Android M. -

- -

- Что входит в состав Preview? -

- -

- M Developer Preview содержит всё, что вам необходимо для тестирования ваших существующих приложений на экранах различных размеров, -тестирования с использованием различных сетевых технологий, чипсетов ЦП и графических процессоров, - а также на различных архитектурах оборудования. -

- -

- Инструменты SDK -

- -

- С помощью менеджера SDK в Android Studio вы можете загрузить следующие компоненты: -

- -
    -
  • Инструменты SDK для M Developer Preview. -
  • - -
  • Системный образ эмулятора (32- и -64-разрядная версии) для M Developer Preview. -
  • - -
  • Системный образ эмулятора для Android TV (32- и -32-разрядная версии) -
  • -
- -

- Системные образы оборудования -

- -

- На странице -Загрузки можно скачать следующие системные образы оборудования: -

- -
    -
  • - Системный образ устройства Nexus 5 (GSM/LTE) («hammerhead») -
  • - -
  • - Системный образ устройства Nexus 6 («shamu») -
  • - -
  • - Системный образ устройства Nexus 9 (Wi-Fi) («volantis») -
  • - -
  • - Системный образ устройства Nexus Player (Android TV) («fugu») -
  • -
- -

- Документация и примеры кода -

- -

- Здесь вы можете найти документацию, где представлены подробные сведения о версии Preview: -

- - - -

- Ресурсы поддержки -

- -

- При тестировании и разработке приложений для M -Developer Preview рекомендуем пользоваться следующими ресурсами поддержки: -

- -
    -
  • Система отслеживания проблем M -Developer Preview — это ваш основной канал для предоставления своих -отзывов. С ее помощью вы можете сообщать нам об обнаруженных ошибках, проблемах производительности, а также предоставлять общие отзывы. - Также можно ознакомиться с известными проблемами -и действиями по их устранению. -
  • - -
  • Сообщество разработчиков Android -M — это сообщество Google+, где можно общаться с другими -разработчиками, работающими с Android M. Делитесь в сообществе своими наблюдениями -и идеями, а также находите ответы на вопросы об Android M. -
  • -
- - -

- Выбор целевого уровня, предварительные версии API-интерфейсов и публикация приложений -

- -

- Выпуск Android M Developer Preview предназначен исключительно для разработки и -не имеет стандартного уровня API. Если вы не хотите -проверять свое приложение на совместимость (хотя мы настоятельно рекомендуем делать это), -выберите целевой уровень M Developer Preview, задав для параметра targetSdkVersion -своего приложения значение “MNC”. -

- -

- В Android M Developer Preview представлены предварительные версии API-интерфейсов -—. Такие API-интерфейсы не будут официально опубликованы до выпуска окончательной версии пакета SDK, - намеченого на третий квартал 2015 г. Это означает, что в будущем можно -ожидать незначительных изменений в API-интерфейсах, особенно в -первые недели действия программы. Каждое обновление -Android M Developer Preview будет включать обзор изменений. -

- -

- Обратите внимание, что несмотря на возможные изменения в предварительных версиях API-интерфейсов, соответствующие расширения функциональности системы, -такие как разрешения на выполнение и функции сбережения энергии, работают стабильно и уже готовы для -тестирования. -

- -

- Что касается публикации приложений, то политика Google Play однозначно запрещает публикацию приложений, -разработанных для M Developer Preview. После выхода окончательной версии пакета SDK Android M -вы сможете выбрать официальный целевой уровень API Android M и приступить -к публикации ваших приложений в магазине Google Play. Тем временем, если вы хотите распространить приложение, предназначенное для -тестировщиков Android M, то используйте для этого электронную почту или разместите такие приложения на своем сайте -для прямой загрузки. -

- -

- Начало работы -

- -

- Чтобы приступить к тестированию своего приложения, выполните указанные ниже действия. -

- -
    -
  1. Ознакомьтесь с обзором API-интерфейсов -и сведениями об изменениях в работе, чтобы получить -представление о новых возможностях платформы и о том, как это может повлиять на ваши приложения. В частности, узнайте подробнее о новой модели -разрешений на -выполнение, функциях сбережения энергии и автоматическом резервном копировании. -
  2. - -
  3. Настройте свою среду, руководствуясь инструкциями по -настройке пакета SDK Preview -и конфигурированию тестовых устройств. -
  4. - -
  5. Выполните -инструкции по прошивке, чтобы прошить устройства -Nexus 5, 6, 9 и Player с использованием последнего системного образа M Developer Preview. После прошивки вашего устройства для разработки -обновления Preview на него будут приходить по беспроводной сети. -
  6. - -
  7. Загрузите справочник по API-интерфейсам M Previewпримеры кода M Preview -, чтобы узнать больше о новых возможностях API-интерфейсов и о том, как использовать их в ваших -приложениях. -
  8. - -
  9. Присоединяйтесь к сообществу разработчиков Android -M, чтобы всегда быть в курсе последних новостей и общаться с другими -разработчиками, работающими с новой платформой. -
  10. -
- -

- Благодарим за участие в программе Android M Developer! -

diff --git a/docs/html-intl/intl/ru/preview/samples.jd b/docs/html-intl/intl/ru/preview/samples.jd deleted file mode 100644 index f8115e7167afa6ce185a29bec265da2532c3bf3d..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/samples.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Примеры -page.image=images/cards/samples-new_2x.png -@jd:body - -

- Ниже представлены примеры кода для M Developer Preview. Чтобы загрузить примеры в -Android Studio, выберите File > Import Samples. -

- -

- Примечание. Эти проекты предназначены для использования -в Gradle и Android Studio. -

- - -

Разрешения на выполнение

- -

- В Android M изменился подход к использованию системных разрешений. Пользователям теперь предлагается предоставить разрешения -во время выполнения, а не во время установки приложения. В примере ниже показано, как запрашивать такие разрешения. - -

- -

Загрузить с сайта GitHub

- -

Подтверждение учетных данных

- -

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

- -

Загрузить с сайта -GitHub

- -

Диалоговое окно авторизации по отпечатку пальца

- -

- В этом примере показано, как распознавать зарегистрированные отпечатки пальцев для авторизации пользователя в -приложении. -

- -

Загрузить с сайта GitHub

- -

Автоматическое резервное копирование для приложений

- -

- В Android M представлена функция автоматического резервного копирования настроек приложений. В примере кода ниже показано, -как добавить в приложение правила фильтрации для управления резервным копированием настроек. -

- -

Загрузить с сайта GitHub

- -

Camera 2 RAW

- -

- В этом примере кода можно увидеть порядок использования API-интерфейса Camera2 для получения буферов камеры RAW и сохранения их -в виде файлов DNG. -

- -

Загрузить с сайта GitHub

- -

Активные уведомления

- -

- В примере кода ниже показано, как -NotificationManager -может сообщить о количестве уведомлений, которое в настоящее время отображает ваше приложение. -

- -

Загрузить с сайта GitHub

diff --git a/docs/html-intl/intl/ru/preview/setup-sdk.jd b/docs/html-intl/intl/ru/preview/setup-sdk.jd deleted file mode 100644 index 1ffc2cddf5e7406c8231775b0875e7f7404cfc70..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/setup-sdk.jd +++ /dev/null @@ -1,207 +0,0 @@ -page.title=Настройка пакета SDK Preview -page.image=images/cards/card-set-up_16-9_2x.png - -@jd:body - - - - -

Пакет SDK M Developer Preview можно загрузить с помощью менеджера SDK Android. -В этой статье мы исходим из того, что вы уже знакомы с разработкой приложений Android и умеете работать с -менеджером SDK Android и создавать проекты. Тем, кто только начинает работу с -Android, рекомендуется сначала обратиться к разделу Создание -первого приложения.

- -

Загрузка Android Studio 1.3

- -

Для работы Developer Preview лучше всего подходит Android Studio 1.3, которая, кстати, -также является предварительной версией. Для работы с пакетом SDK Preview настоятельно рекомендуется установить предварительную версию -Android Studio 1.3.

- -

Внимание! Предварительная версия Android -Studio 1.3 (сборка Canary) по-прежнему находится на стадии разработки. Если для тестирования - M Developer Preview вы используете свой основной компьютер для разработки, вы можете дополнительно установить -Android Studio для целей тестирования.

- -

Порядок установки предварительной версии Android Studio 1.3

- -
    -
  1. Загрузите и запустите Android -Studio. -
  2. - -
  3. Откройте окно Settings (в ОС Windows для этого выберите -File > Settings). Перейдите к панели -Appearance & Behavior > System -Settings > Updates. - -

    В операционной системе OSX панель Appearance & -Behavior -находится в Android Studio в окне Preferences.

    -
  4. - -
  5. На панели Updates выберите -Automatically check updates for: Canary Channel. -
  6. - -
  7. На панели Updates выберите Check Now, -чтобы проверить наличие актуальных обновлений для сборки Canary. При появлении соответствующего запроса загрузите -и установите сборку. -
  8. -
- -

Загрузка пакета SDK Preview

- -

Порядок добавления компонентов SDK Preview в среду разработки

- -
    -
  1. Запустите предварительную версию Android Studio 1.3. -
  2. - -
  3. Откройте окно Settings (в ОС Windows для этого выберите -File > Settings). Перейдите к панели -Appearance & Behavior > System -Settings > Updates. - -

    В операционной системе OSX панель Appearance & -Behavior -находится в Android Studio в окне Preferences.

    -
  4. - -
  5. На панели Updates выберите параметры -Automatically check updates for: Canary Channel и -Automatically check updates for Android SDK: Preview Channel. -
  6. - -
  7. Запустите менеджер SDK Android. (Менеджер SDK входит в состав Android Studio 1.3; -теперь это не отдельное -приложение.) -
  8. - -
  9. В разделе Platforms выберите Android MNC -Preview. -
  10. - -
  11. В разделе Tools выберите последние инструменты -SDK Tools, Platform-tools и -Build-tools для Android. -
  12. - -
  13. Нажмите на кнопку Install packages и примите условия лицензионного соглашения -для всех пакетов. -
  14. - -
  15. Убедитесь, что платформа M Developer Preview установлена. Для этого откройте окно -Settings и перейдите к панелиAppearance & Behavior -> System Settings > Android SDK.
  16. - -
  17. На панели Android SDK выберите вкладку -SDK Platforms. Для элемента Android MNC -Preview должно быть указано Installed. Кроме того, перейдите на вкладку -SDK Tools и убедитесь в том, что установлены актуальные -версии инструментов. -
  18. -
-

После выполнения этих действий компоненты предварительной версии -доступны для использования в вашей среде разработки.

- - -

Создание или обновление проекта

- -

- Если вы хотите воспользоваться API-интерфейсами предварительной версии, создайте или обновите проект разработки так, чтобы в нем использовались -компоненты предварительной версии. -

- - -

Создание нового проекта

- -

- Для создания проекта в предварительной версии рекомендуется использовать Android Studio. Выполните действия, -приведенные в разделе Создание проекта, -пока в мастере проекта не появится экран Form Factors. Затем выполните -действия по созданию проекта, настроенного для предварительной версии. -

- -
    -
  • Установите флажок Phone and Tablet.
  • -
  • Выберите MNC: Android M (Preview) в разделе Minimum -SDK.
  • -
- - -

Обновление существующего проекта

- -

- В существующем проекте следует изменить его конфигурацию и настроить его на использование API-интерфейсов предварительной версии. В -среде разработки откройте файл build.gradle для вашего модуля и задайте -значения следующим образом: -

- -
    -
  • для параметра compileSdkVersion задайте значение 'android-MNC';
  • -
  • для параметра minSdkVersion задайте значение 'MNC';
  • -
  • для параметра targetSdkVersion задайте значение 'MNC'.
  • -
- - -

Настройка для тестирования

- -

- Для тестирования приложения в предварительной версии необходимо физическое или виртуальное устройство, настроенное с помощью -предварительной версии платформы. Если имеется совместимое устройство, можно установить предварительную версию -платформы для тестирования. Для тестирования также можно настроить виртуальное устройство. -

- -

Настройка физического устройства

- -

- Если у вас имеется устройство Nexus 5, Nexus 6, Nexus 9 или Android TV, для тестирования приложений вы можете установить на эти устройства -системный образ предварительной версии. -Также в Android Studio можно настроить виртуальное устройство с предварительной версией платформы, -используя для этого диспетчер виртуальных устройств Android. -

- -

- Внимание! Установка на устройство образа с предварительной версией платформы приведет к удалению всех данных с -устройства, поэтому перед установкой обязательно создайте резервные копии необходимых данных. -

- -

Настройка виртуального устройства

- -

- В Android Studio можно настроить виртуальное устройство с предварительной версией платформы, -используя для этого диспетчер виртуальных устройств Android. -

- -

Порядок настройки AVD в диспетчере AVD

- -
    -
  1. Установите SDK Preview в свою среду разработки, как описано в разделе -Настройка пакета -SDK Preview.
  2. -
  3. Выполните действия, указанные в статье -Управление виртуальными устройствами с помощью диспетчера -AVD. Используйте следующие настройки: -
      -
    • Device: Nexus 5, Nexus 6, Nexus 9 или Android TV
    • -
    • Target: - Android M (Preview) – API Level M
    • -
    • ABI: x86
    • -
    -
  4. -
- -

- Дополнительные сведения о создании виртуальных устройств для тестирования представлены в статье Управление виртуальными устройствами. -

diff --git a/docs/html-intl/intl/ru/preview/testing/guide.jd b/docs/html-intl/intl/ru/preview/testing/guide.jd deleted file mode 100644 index 8beb0ed0ce67a80b3de3fe17139809c7ea496216..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/testing/guide.jd +++ /dev/null @@ -1,187 +0,0 @@ -page.title=Руководство по тестированию -page.image=images/cards/card-build_16x9_2x.png -page.keywords=ресурсы для предварительной версии,androidm,тестирование,разрешения - -@jd:body - - - -

- Android M Developer Preview предлагает вам проверить, как будут работать ваши приложения в следующей версии -платформы. В ней реализован ряд изменений в API-интерфейсах и поведении системы, -которые могут повлиять на работу вашего приложения, как описано в статьях Обзор -API-интерфейсов и Изменения в работе. Если вы хотите обеспечить удобство работы пользователей, при тестировании -вашего приложения в предварительной версии платформы - обратите особое внимание на изменения в системе. -

- -

- В настоящем руководстве приводится порядок тестирования функций предварительной версии с помощью вашего приложения. Советуем при этом обратить особое внимание -на работу следующих функций новой версии платформы, поскольку они в значительной мере повлияют на поведение -вашего приложения: -

- - - -

- Дополнительные сведения о том, как настроить физические или виртуальные устройства с помощью системного образа предварительной версии платформы -к тестированию, представлены в статье Настройка SDK Preview. -

- - -

Тестирование разрешений

- -

- В новой модели разрешений -изменился подход к тому, каким образом пользователи смогут предоставлять разрешения для вашего приложения. Если раньше пользователь мог предоставить сразу все -разрешения в процессе установки, теперь ваше приложение должно запрашивать у него отдельные разрешения -во время работы. Это позволяет пользователю контролировать каждую операцию приложения, а также -лучше понимать, для чего приложение запрашивает то или иное разрешение. Пользователи могут в любой момент -предоставить разрешения для отдельного приложения или отозвать их. Эта новая -возможность в предварительной версии платформы, вероятнее всего, повлияет на поведение вашего приложения. Некоторые из его функций могут -пострадать или перестать работать. -

- -

- Это касается работы всех приложений на новой платформе, даже тех, которые были разработаны для более ранних ее версий. - Платформа обеспечивает ограниченную поддержку устаревших приложений, однако вам следует приступить к -планированию перехода на новую модель разрешений уже сегодня, чтобы -опубликовать обновленную версию вашего приложения к моменту официального выпуска новой платформы. -

- - -

Советы по тестированию

- -

- Ниже представлены советы по тестированию, которые призваны помочь вам спланировать и провести тестирование вашего приложения с использованием новой -модели разрешений. -

- -
    -
  • Определите текущие разрешения приложения и связанные с этим фрагменты кода.
  • -
  • Протестируйте различные варианты работы пользователя со службами и данными, защищенными с помощью разрешений.
  • -
  • Протестируйте различные сочетания предоставленных и отозванных разрешений.
  • -
  • Воспользуйтесь инструментом {@code adb} для управления разрешениями из командной строки: -
      -
    • Получите список разрешений и их состояний по группам: -
      adb shell pm list permissions -d -g
      -
    • -
    • Предоставьте или отзовите одно или несколько разрешений с помощью следующего синтаксиса:
      -
      adb shell pm [grant|revoke] <permission.name> ...
      -
    • -
    -
  • -
  • Проанализируйте работу вашего приложения со службами, использующими разрешения.
  • -
- -

Стратегия тестирования

- -

- Новая модель разрешений влияет на структуру и дизайн вашего приложения, а также на -работу пользователей с приложением и предлагаемые им варианты взаимодействия с вашим продуктом. Проанализируйте, как ваше приложение использует разрешения в настоящее время, -и продумайте новые варианты взаимодействия, которые хотите реализовать. В официальном выпуске -платформы будет представлена поддержка совместимости, однако мы рекомендует запланировать обновление приложения, не полагаясь -на эту поддержку. -

- -

- Определите круг разрешений, которые действительно необходимы для работы вашего приложения, и найдите фрагменты -кода, где используются службы, защищенные разрешениями. Для этого протестируйте работу приложения на новой -платформе и проанализируйте код. При тестировании следует сосредоточиться на использовании -разрешений на выполнение путем изменения параметра {@code targetSdkVersion} приложения для использования предварительной версии. Дополнительные -сведения представлены в статье Настройка SDK Preview. -

- -

- Протестируйте работу приложения при различных сочетаниях предоставленных и отозванных разрешений, чтобы определить варианты работы пользователя, -которые зависят от разрешений. В случаях, когда зависимость не явная или не логичная, подумайте, можно ли -перестроить или структурировать такой вариант работы, чтобы устранить зависимость или четко обозначить, -для чего требуется каждое разрешение. -

- -

- Дополнительные сведения о поведении разрешений на выполнение и тестировании, а также советы и рекомендации по данному вопросу представлены на странице -Разрешения. - -

- - -

Тестирование режима «Doze» и ждущего режима для приложений

- -

- Режим «Doze» и ждущий режим приложений, предназначенные для экономии энергии, ограничивают объем данных, обрабатываемых вашим приложением в фоновом режиме, -когда устройство не используется или приложение неактивно. Ограничения, -которые система может налагать на приложения, включают ограничение или отключение доступа к сети, -приостановку выполнения фоновых задач, приостановку отправки уведомлений, игнорирование запросов на пробуждение и будильников. Чтобы гарантировать -правильную работу приложения в условиях оптимизации энергопотребления, вам следует протестировать ваше приложение, -смоделировав эти режимы. -

- -

Тестирование работы приложения в режиме «Doze»

- -

Ниже представлен порядок тестирования режима «Doze».

- -
    -
  1. Установите на физическое или виртуальное устройство системный образ M Preview.
  2. -
  3. Подключите устройство к компьютеру для разработки и установите ваше приложение.
  4. -
  5. Запустите ваше приложение и не закрывайте его.
  6. -
  7. Смоделируйте переход устройства в режим «Doze», выполнив следующие команды: - -
    -$ adb shell dumpsys battery unplug
    -$ adb shell dumpsys deviceidle step
    -$ adb shell dumpsys deviceidle -h
    -
    - -
  8. -
  9. Понаблюдайте за поведением приложения после возвращения устройства в обычный режим. Убедитесь, что при выходе устройства из режима «Doze» приложение -должным образом возобновляет свою работу .
  10. -
- - -

Тестирование работы приложения в ждущем режиме

- -

Ниже представлен порядок тестирования ждущего режима для приложений.

- -
    -
  1. Установите на физическое или виртуальное устройство системный образ M Preview.
  2. -
  3. Подключите устройство к компьютеру для разработки и установите ваше приложение.
  4. -
  5. Запустите ваше приложение и не закрывайте его.
  6. -
  7. Смоделируйте переход приложения в ждущий режим, выполнив следующие команды: - -
    -$ adb shell am broadcast -a android.os.action.DISCHARGING
    -$ adb shell am set-idle <packageName> true
    -
    - -
  8. -
  9. Смоделируйте выход приложения из ждущего режима, выполнив следующую команду: -
    $ adb shell am set-idle <packageName> false
    -
  10. -
  11. Понаблюдайте за поведением приложения, когда оно возобновляет свою работу. Убедитесь, что оно -выходит из ждущего режима без сбоев. Обратите особое внимание на работу уведомлений и выполнение фоновых задач приложения -.
  12. -
- -

Автоматическое резервное копирование для приложений и идентификаторы устройств

- -

Если во внутреннем хранилище вашего приложения имеются какие-либо идентификаторы устройства, такие как идентификатор Google -Cloud Messaging, -обязательно воспользуйтесь советами и рекомендациями, приведенными в статье -Автоматическое резервное копирование для приложений,чтобы исключить расположение -хранилища из списка объектов для автоматического резервного копирования.

diff --git a/docs/html-intl/intl/ru/preview/testing/performance.jd b/docs/html-intl/intl/ru/preview/testing/performance.jd deleted file mode 100644 index fc88cbd7cc328bc840a667207e82ed9e165a7280..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/ru/preview/testing/performance.jd +++ /dev/null @@ -1,656 +0,0 @@ -page.title=Тестирование скорости отображения -page.image=images/cards/card-test-performance_2x.png -page.keywords=скорость отображения, кадр/с, инструменты - -@jd:body - - - - - -

- Тестирование производительности интерфейса позволяет убедиться в том, что ваше приложение не только соответствует функциональным требованиям, -но и гарантирует пользователю безупречное взаимодействие с интерфейсом с постоянной частотой отображения -60 кадров в секунду (почему именно -60 кадров/с?), без каких-либо задержек и пропущенных кадров или, как их называют, глюков. В этой -статье рассматриваются инструменты для измерения производительности интерфейса, а также предлагается подход к -интеграции этих процедур измерения производительности в ваши методы тестирования приложений. -

- - -

Измерение производительности интерфейса

- -

- Чтобы улучшить производительность, прежде всего у вас должна иметься возможность измерить производительность -вашей системы. Когда это сделано, необходимо диагностировать и определить проблемы, которые могут возникнуть на разных этапах -процесса разработки. -

- -

- dumpsys -– это инструмент Android, который запускается на устройстве и создает дамп интересной информации о состоянии системных -служб. После передачи в dumpsys команды gfxinfo в журнал устройства (logcat) -записываются сведения о производительности, касающиеся скорости анимации на этапе -записи. -

- -
-> adb shell dumpsys gfxinfo <PACKAGE_NAME>
-
- -

- Эта команда может создавать множество различных вариантов данных кадровой синхронизации. -

- -

Агрегированные статические данные о кадрах

- -

- В M Preview эта команда фиксирует в logcat агрегированный анализ данных о кадрах, полученных -за весь жизненный цикл процесса. Например: -

- -
-Stats since: 752958278148ns
-Total frames rendered: 82189
-Janky frames: 35335 (42.99%)
-90th percentile: 34ms
-95th percentile: 42ms
-99th percentile: 69ms
-Number Missed Vsync: 4706
-Number High input latency: 142
-Number Slow UI thread: 17270
-Number Slow bitmap uploads: 1542
-Number Slow draw: 23342
-
- -

- Приведенные выше статистические данные высокого уровня демонстрируют производительность отрисовки, которой обладает приложение, -а также ее стабильность при обработке множества кадров. -

- - -

Точная информация о кадровой синхронизации

- -

- В M Preview представлена новая команда для gfxinfo – framestats. Она позволяет получить -чрезвычайно точную информацию о кадровой синхронизации из последних кадров, что поможет вам отслеживать проблемы -и устранять их. -

- -
->adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
-
- -

- Данная команда предоставляет информацию о кадровой синхронизации буквально по наносекундам на основании последних 120 -кадров, созданных приложением. Ниже приводится пример необработанного результата из журнала adb после выполнения команды dumpsys gfxinfo -<PACKAGE_NAME> framestats: -

- -
-0,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954,
-0,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120,
-0,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527,
-0,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683,
-
- -

- Каждая строка здесь представляет собой кадр, созданный приложением. В каждой строке имеется фиксированное количество -столбцов, где указано время, затраченное на каждом этапе процесса создания кадра. Более подробно этот формат рассматривается в следующем разделе, -включая сведения о том, что означает каждый столбец. -

- - -

Формат данных, возвращаемых командой framestats

- -

- Поскольку блок данных выводится в формате CSV, вы можете с легкостью вставить его в любую программу -для работы с электронными таблицами, а также собрать и обработать данные с помощью сценария. Ниже рассматривается формат -столбцов выходных данных. Все временные метки указаны в наносекундах. -

- -
    -
  • FLAGS -
      -
    • В строках, в которых в столбце FLAGS указано «0», общее время смены кадра вычислялось путем -вычитания значения в столбце INTENDED_VSYNC из значения в столбце FRAME_COMPLETED. -
    • - -
    • Если это значение не равно нулю, то строку следует пропустить, поскольку кадр был определен как отличающийся от -обычной производительности, при которой ожидается, что размещение и прорисовка кадра займут -больше 16 мс. Причина этого может заключаться в следующем: -
        -
      • макет окна был изменен (например, первый кадр приложения или после -вращения); -
      • - -
      • также, возможно, кадр был пропущен, в результате чего временные метки некоторых значений -содержат бессмысленную информацию; кадр можно пропустить, если, например, скорость отображения превышает 60 кадров/с или если -на экране от этого не появяются шумы; это не обязательно указывает на проблему -в приложении. -
      • -
      -
    • -
    -
  • - -
  • INTENDED_VSYNC -
      -
    • Предполагаемая начальная точка кадра. Если данное значение отличается от значения в столбце VSYNC, это указывает на то, -что поток был занят и не смог своевременно среагировать на сигнал -vsync. -
    • -
    -
  • - -
  • VSYNC -
      -
    • Значение времени, которое использовалось во всех приемниках vsync, а также которое было затрачено на прорисовку кадра -(обратные вызовы кадров Choreographer и анимации, View.getDrawingTime() и т. д.) -
    • - -
    • Чтобы узнать подробнее о VSYNC и о том, какое значение это имеет для вашего приложения, просмотрите -видеоролик, -посвященный общим сведениям о VSYNC. -
    • -
    -
  • - -
  • OLDEST_INPUT_EVENT -
      -
    • Временная метка для самого старого события ввода в очереди ввода или Long.MAX_VALUE, если -для кадра отсутствуют события ввода. -
    • - -
    • Это значение в первую очередь предназначено для платформы и имеет ограниченную практическую ценность для разработчиков -приложений. -
    • -
    -
  • - -
  • NEWEST_INPUT_EVENT -
      -
    • Временная метка для самого нового события ввода в очереди ввода или 0, если -для кадра отсутствуют события ввода. -
    • - -
    • Это значение в первую очередь предназначено для платформы и имеет ограниченную практическую ценность для разработчиков -приложений. -
    • - -
    • Однако оно позволяет получить приблизительное представление о том, какая задержка возникает у приложения, -обратившись к значению разницы (FRAME_COMPLETED - NEWEST_INPUT_EVENT). -
    • -
    -
  • - -
  • HANDLE_INPUT_START -
      -
    • Временная метка отправки событий ввода в приложение. -
    • - -
    • Разность между этим значением и значением ANIMATION_START позволяет определить, -сколько времени было затрачено приложением на обработку событий ввода. -
    • - -
    • Если разность достаточно велика (>2 мс), это означает, что приложение затрачивает очень -много времени на обработку событий ввода, таких как View.onTouchEvent(). В свою очередь, это может указывать на -необходимость оптимизации этого процесса обработки или переноса этой нагрузки в другой поток. Следует помнить, что существуют -сценарии, когда такое -поведение ожидается и является приемлемым. Имеются в виду нажатия или аналогичные сценарии, в которых запускаются новые операции. -
    • -
    -
  • - -
  • ANIMATION_START -
      -
    • Временная метка запуска анимаций, зарегистрированных с помощью Choreographer. -
    • - -
    • Разность между этим значением и PERFORM_TRANVERSALS_START позволяет -определить, сколько времени потребовалось на оценку всех запущенных аниматоров (ObjectAnimator, -ViewPropertyAnimator и общих переходов). -
    • - -
    • Если разность достаточно велика (>2 мс), проверьте наличие в приложении настраиваемых -аниматоров, а также то, какие поля анимируют классы ObjectAnimator. Убедитесь, что -эти поля подходят для анимации. -
    • - -
    • Чтобы узнать подробнее о Choreographer, просмотрите видео For Butter or Worse. - -
    • -
    -
  • - -
  • PERFORM_TRAVERSALS_START -
      -
    • Если вычесть значение DRAW_START из этого значения, вы можете узнать, сколько времени было затрачено на разметку и -измерение. (Обратите внимание, что во время прокрутки или анимации это значение должно быть близко -к нулю.) -
    • - -
    • Чтобы узнать подробнее об этапах разметки и измерения во время рендеринга интерфейса приложения, просмотрите -видео, посвященное -аннулированию, макетам и производительности. -
    • -
    -
  • - -
  • DRAW_START -
      -
    • Время начала этапа отрисовки с помощью метода performTraversals. Это точка начала -записи списков отображения любых представлений, которые были аннулированы. -
    • - -
    • Разность между этим значением и значением SYNC_START означает, сколько времени потребовалось на вызов метода View.draw() для всех -аннулированных представлений в дереве. -
    • - -
    • Дополнительные сведения о модели отрисовки представлены в видео, посвященном аппаратному ускорению -или -аннулированию, макетам и производительности. -
    • -
    -
  • - -
  • SYNC_START -
      -
    • Время начала этапа синхронизации отрисовки. -
    • - -
    • Если разность между этим значением и значением ISSUE_DRAW_COMMANDS_START достаточно велика (>0,4 мс или около этого), -обычно это указывает на наличие большого количество новых растровых изображений, которые следует передать -ЦП. -
    • - -
    • Чтобы узнать подробнее об этапе синхронизации, просмотрите видео, посвященное -рендерингу профиля с помощью ЦП. -
    • -
    -
  • - -
  • ISSUE_DRAW_COMMANDS_START -
      -
    • Время начала отправки аппаратным обработчиком команд на отрисовку для ЦП. -
    • - -
    • Разность между этим значением и значением FRAME_COMPLETED позволяет получить приблизительное представление об объеме ресурсов ЦП, используемых -приложением. Здесь отображаются такие проблемы, как превышение или недостаток эффектов рендеринга. - -
    • -
    -
  • - -
  • SWAP_BUFFERS -
      -
    • Время вызова eglSwapBuffers; -относится к работе платформы и имеет ограниченную практическую ценность для разработчиков приложений. -
    • -
    -
  • - -
  • FRAME_COMPLETED -
      -
    • Все готово! Общее время, затраченное на обработку этого кадра, вычисленное по следующей формуле: -FRAME_COMPLETED - INTENDED_VSYNC. -
    • -
    -
  • - -
- -

- Эти данные можно использовать несколькими способами. Простым, но полезным примером может служить -график, иллюстрирующий распределение времени кадров (FRAME_COMPLETED - INTENDED_VSYNC) в -различных диапазонах задержки (см. рисунок ниже). На графике ясно видно, что с большинством кадров -никаких проблем не возникло (ниже отметки в 16 мс; обозначено красным), однако для некоторых кадров -это значение значительно превышено. Можно обратиться к этому графику и пронаблюдать изменения по времени, -чтобы увидеть общую картину смещения или новые отличающиеся значения. Также можно создать график задержи ввода, -времени, затраченного на разметку или других интересующих вас показателей, руководствуясь множеством временных меток, -имеющихся в данных. -

- - - - -

Дамп простой кадровой синхронизации

- -

- Если в разделе настроек для разработчиков для параметра Profile GPU rendering задано значение In adb shell dumpsys gfxinfo, -команда adb shell dumpsys gfxinfo выдает сведения о синхронизации -по времени для последних 120 кадров. При этом вы увидите несколько категорий значений, -разделенные знаком табуляции. Эти данные могут оказаться полезными для определения частей конвейера отрисовки, которые могут работать с задержкой -при высоком уровне обработки. -

- -

- Как и в случае с командой -framestats, описанной выше, вы можете с легкостью вставить полученные данные в любую программу -для работы с электронными таблицами, а также собрать и обработать их с помощью сценария. На графике ниже иллюстрируется разбивка со сведениями о количестве созданных кадров и времени, которое приложение затратило на -это. -

- - - -

- Результаты выполнения команды gfxinfo, копирования полученного результата, передачи его в приложение для работы -с электронными таблицами и построения графика представлены в виде столбцов. -

- -

- Каждый столбец представляет собой один кадр анимации; его высота обозначает количество миллисекунд, -затраченных на вычисление этого кадра. Каждый цветной сегмент столбца -обозначает различный этап процесса рендеринга, что позволяет определить проблемные компоненты -приложения в плане производительности. Дополнительные сведения о -процессе рендеринга, а также о том, как оптимизировать его, представлены в видео, посвященном -аннулированию, макетам и производительности. -

- - -

Управление промежутком времени для сбора статистических данных

- -

- При выполнении команды framestats, равно как и при вычислении простой кадровой синхронизации, данные собираются за достаточно короткий промежуток времени – порядка -двух секунд рендеринга. Чтобы точно указать этот промежуток времени (например, -чтобы ограничить данные определенной анимацией), можно сбросить все счетчики -и объединить собранные статистические данные. -

- -
->adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
-
- -

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

- - -

Диагностика снижения производительности

- -

- Определение снижений производительности послужит отличным началом для отслеживания проблем, а также позволит всегда поддерживать -хорошую работоспособность приложения. Однако инструмент dumpsys позволяет лишь определить наличие проблем и приблизительный уровень их -серьезности. Вам же требуется диагностировать каждую конкретную проблему с -производительностью и найти подходящие способы ее устранения. Для этих целей прекрасно -подходит инструмент systrace. -

- - -

Дополнительные ресурсы

- -

- Дополнительные сведения о принципе работы конвейера рендеринга платформы Android, информация об общих проблемах, с которыми можно столкнуться при его использовании, -а также способы их устранения представлены на указанных ниже -полезных ресурсах. -

- -
    -
  • Производительность визуализации 101 -
  • -
  • Частота 60 кадров в секунду -
  • -
  • Пользовательский интерфейс Android и графический процессор -
  • -
  • Аннулирование макетов и производительность -
  • -
  • Анализ производительности интерфейса с помощью systrace -
  • -
- - -

Автоматизация тестирования производительности интерфейса

- -

- Один из подходов к тестированию производительности интерфейса заключается в простом привлечении тестировщиков, чтобы они выполнили ряд -операций с целевым приложением, а также либо просто визуально убедились в отсутствии глюков, либо уделили достаточно много времени -тестированию приложения с помощью соответствующего инструмента. Однако такой подход имеет один важный недостаток -– человек физически не способен обрабатывать информацию при очень быстрой смене кадров. -Кроме того, такая работа занимает много времени, утомляет и не гарантирует полное отсутствие ошибок. -

- -

- Гораздо эффективнее записать ключевые показатели производительности, полученные в ходе автоматического -тестирования интерфейса, и проанализировать их. В состав Android M Developer Preview входят новые функции ведения журналов, которые позволяют с легкостью -определить объем и серьезность глюков анимации в приложении. -Их также можно использовать для того, чтобы создать строгий процесс определения текущей производительности и отслеживать соответствие требованиям будущих задач -производительности. -

- -

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

- -

- Этот подход большей частью сводится к двум ключевым шагам. Шаг первый: определите, что вы -тестируете и как вы это делаете. Шаг второй: произведите настройку -среды автоматизированного тестирования и ее обслуживание. -

- - -

Настройка тестов интерфейса

- -

- Прежде чем приступить к автоматизированному тестированию, необходимо принять ряд важных решений, -чтобы правильно определить область тестирования и ваши требования. -

- -

- Определение ключевых анимаций или потоков для тестирования -

- -

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

- -
    -
  • Прокрутка основного представления ListView или RecyclerView -
  • - -
  • Анимации во время циклов асинхронного ожидания -
  • - -
  • Любые анимации, включающие загрузку растровых изображений или манипуляции с ними -
  • - -
  • Анимации, включающие смешивание альфа-канала -
  • - -
  • Отрисовка настраиваемого представления на экране -
  • -
- -

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

- -

- Определите свои будущие задачи и отслеживайте их выполнение -

- -

- С профессиональной точки зрения, может оказаться крайне важным решить, каких именно показателей производительности вы хотите добиться, и -сосредоточить усилия на создании необходимых тестов и сборе соответствующих данных. Например: -

- -
    -
  • Вы только хотите начать отслеживание производительности интерфейса, чтобы получить дополнительные сведения? -
  • - -
  • Вы хотите предотвратить возможно снижение производительности в будущем? -
  • - -
  • На сегодняшний день 90% кадров анимируются плавно. Вы хотите увеличить этот показатель до 98% в этом квартале? -
  • - -
  • На сегодняшний день 98% кадров анимируются плавно. Вы не хотите, чтобы производительность снизилась? -
  • - -
  • Вы хотите улучшить производительность на бюджетных устройствах? -
  • -
- -

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

- -

- Определение устройств для тестирования -

- -

- Производительность приложения может меняться в зависимости от того, на каком устройстве оно установлено. Некоторые устройства могут обладать недостаточным -объемом памяти, менее производительными графическими процессорами или слабыми ЦП. Это означает, что анимации, -которые прекрасно работают на устройстве с одним оборудованием, могут вообще не работать на устройстве с другим оборудованием, и, что хуже всего, препятствовать -нормальной работе другой части конвейера. Поэтому, учитывая такую разницу -в отображении интерфейса для пользователей, выберите диапазон устройств для выполнения тестов, включив в него -как современные высокотехнологичные, так и бюджетные устройства, планшеты и т. д. При выборе диапазона устройств учитывайте разницу в производительности ЦП -, ОЗУ, разрешение экрана, его размер и т. д. Тесты, которые успешно прошли на устройстве верхнего ценового сегмента, -могут завершиться сбоем на бюджетных моделях. -

- -

- Базовые средства для тестирования пользовательского интерфейса -

- -

- Такие наборы инструментов, как UI Automator и -Espresso, -призваны помочь автоматизировать действия пользователей при взаимодействии с вашим приложением. Это простые инструменты, -имитирующие работу пользователя на устройстве. Для использования этих инструментов -вам необходимо создать уникальные сценарии, которые будут включать выполнение ряда действий, и воспроизвести -их на устройстве. -

- -

- Используя эти автоматизированные тесты в сочетании с командой dumpsys gfxinfo, вы можете быстро создать -воспроизводимую систему, позволяющую выполнять тестирование и анализировать -полученную информацию о производительности для каждого определенного условия. -

- - -

Настройка автоматизированного тестирования интерфейса

- -

- Получив возможность протестировать интерфейс, а также настроив конвейер на сбор результатов -отдельного теста, важно выбрать инструмент, который позволит многократно выполнить -тест на нескольких устройствах, а также объединить данные о результатах -тестирования производительности для их дальнейшего анализа вашими специалистами по разработке. -

- -

- Инструмент для автоматизации тестирования -

- -

- Следует отметить, что инструменты для тестирования интерфейса (такие как UI Automator) -можно запускать прямо на целевом устройстве или в эмуляторе. Тогда как сбор командой -dumpsys gfxinfo информации о производительности выполняется на хост-компьютере, который отправляет команды из ADB. Чтобы объединить -усилия этих двух инструментов, был разработан MonkeyRunner. -Эта система написания сценариев, работающая на хост-компьютере, отправляет команды на -подключенные устройства, а также получает данные от них. -

- -

- Набор сценариев для надлежащей автоматизации тестирования производительности интерфейса должен включать использование -системы MonkeyRunner хотя бы для выполнения следующих задач: -

- -
    -
  • загрузка и запуск на целевые устройства или в эмулятор необходимого пакета APK; -
  • - -
  • запуск теста UI Automator и его выполнение; -
  • - -
  • сбор информации о производительности с помощью dumpsys gfxinfo; -
  • - -
  • объединение полученной информации и ее отображение для разработчика в удобочитаемой форме. -
  • -
- - -

Определение и устранение обнаруженных проблем

- -

- После обнаружения проблемных мест или причин снижения производительности необходимо понять, что должно быть исправлено -и внести соответствующие изменения. Если используемый инструмент для автоматизированного тестирования обеспечивает соблюдение точной разбивки -кадровой синхронизации по времени, то с его помощью можно тщательно изучить недавние подозрительные изменения в коде или макете (в случае -со снижением производительности), а также ограничить область системы, которая подлежит анализу, когда вы переключаетесь на изучение проблемы -вручную. В последнем случае прекрасно подойдет systrace. С помощью этого инструмента вы можете получить подробнейшую информацию -о синхронизации относительно каждого этапа конвейера рендеринга, относительно каждого потока и каждого ядра -системы, а также относительно любых настраиваемых маркеров событий, которые вы задаете. -

- -

- Надлежащее профилирование синхронизации по времени -

- -

- Важно отметить трудности, с которыми можно столкнуться при получении и анализе данных синхронизации, связанных -с производительностью визуализации. Результаты по своей природе недетерминированные и зачастую -изменяются в зависимости от состояния системы, объема доступной памяти, температурного -дросселирования и времени суток. Смысл в том, что -вы можете выполнить один и тот же тест дважды и получить результаты, -которые будут похожи, но не совпадут в точности. -

- -

- Правильный сбор и профилирование данных означает выполнение одного и того же теста -несколько раз и накопление результатов для вычисления среднего значения (для простоты назовем это -пакетом результатов). Это позволяет получить приблизительное представление о производительности -теста, когда нет необходимости в точных данных. -

- -

- Пакеты можно использовать при изучении изменений кода, чтобы увидеть их относительное влияние на -производительность. Если средняя частота кадров для пакета результатов, полученных до изменения, больше значения для пакета, полученного -после изменения, обычно вы получаете общее повышение производительности в результате этого конкретного -изменения. -

- -

- При выполнении любого автоматизированного тестирования интерфейса следует учитывать этот момент, -равно как и любые аномалии, которые могут возникнуть во время теста. Например, -если производительность вашего приложения неожиданно резко падает из-за проблем с оборудованием устройства (которые -не вызваны вашим приложением), возможно, вы захотите выполнить пакет повторно, чтобы получить -менее беспорядочные данные о синхронизации. -

- -

- Итак, сколько раз следует выполнять тест, прежде чем его результаты станут значимыми? Не менее 10! - Чем больше раз вы выполняете тест (например, можно сделать 50–100 прогонов), тем выше точность результатов -(хотя, конечно, ради точности приходится поступаться временем). -

diff --git a/docs/html-intl/intl/ru/sdk/index.jd b/docs/html-intl/intl/ru/sdk/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..765c89c5749d08ba2ac8f41a74154c18b0ccde75 --- /dev/null +++ b/docs/html-intl/intl/ru/sdk/index.jd @@ -0,0 +1,432 @@ +page.title=Загрузка Android Studio и инструментов SDK +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=Загрузите официальные средства разработки Android для создания приложений для смартфонов, планшетов, носимых устройств, телевизоров и многих других устройств под управлением ОС Android. + +@jd:body + + + + + + + +
+ + + + + + + + + +
+ +
 
+ + + +
+ +

Android Studio

+ +

Официальная среда разработки Android

+ +
    +
  • Среда разработки Android Studio
  • +
  • Инструменты Android SDK
  • +
  • Платформа Android 6.0 (Marshmallow)
  • +
  • Системный образ Android 6.0 с API Google для эмулятора
  • +
+ +Download Android Studio
+ + +

+Чтобы загрузить Android Studio или отдельные инструменты SDK, посетите веб-сайт developer.android.com/sdk/ +

+
+ + + +
+ + + + +

Интеллектуальный редактор программного кода

+ +
+ +
+ +
+

В основе Android Studio лежит интеллектуальный редактор исходного кода, предлагающий такие возможности, как расширенное + автозавершение кода, его реструктуризация и анализ.

+

Этот редактор поможет вам повысить эффективность разработки приложений для Android.

+
+ + + + + +

Шаблоны программного кода и интеграция с GitHub

+ +
+ +
+ +
+

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

+ +

Создавая новый проект, используйте шаблоны программного кода для таких общих фрагментов, как элементы для навигации по приложению и представления, +а также импортируйте примеры кода Google прямо из GitHub.

+
+ + + + +

Разработка приложений с поддержкой экранов разного размера

+ +
+ +
+ +
+

Создавайте приложения для смартфонов и планшетов под управлением Android, для Android Wear +, Android TV, Android Auto и даже Google Glass.

+

Благодаря новому представлению Android Project и поддержке модулей в Android Studio вы можете с легкостью +управлять своими проектами и ресурсами. +

+ + + + +

Виртуальные устройства на любой вкус и цвет

+ +
+ +
+ +
+

В состав Android Studio входит оптимизированный эмулятор.

+

Обновленный и оптимизированный диспетчер виртуальных устройств порадует вас широким набором +предварительно настроенных профилей устройств Android.

+
+ + + + +

+Эволюция сборок Android благодаря Gradle

+ +
+ +
+ +
+

Создавайте несколько APK для ваших приложений с различными функциональными возможностями — в рамках одного и того же проекта.

+

Управляйте зависимостями между приложениями с помощью Maven.

+

Создавайте APK в Android Studio или прямо в командной строке.

+
+ + + + +

Подробнее об Android Studio

+
+ +Download + +
    +
  • Создано на основе IntelliJ IDEA Community Edition, популярной среды разработки Java от JetBrains.
  • +
  • Гибкая система сборки на основе Gradle.
  • +
  • Различные варианты сборки и методы создания файлов APK.
  • +
  • Расширенная поддержка шаблонов для служб Google и устройств различных типов.
  • +
  • Функциональный редактор макетов с поддержкой редактирования тем оформления.
  • +
  • Инструментарий lint для решения проблем с производительностью, удобством использования, совместимостью версий и т. д.
  • +
  • ProGuard и возможности подписи приложений.
  • +
  • Встроенная поддержка Google Cloud Platform, обеспечивающая удобство интеграции со службами Google Cloud + Messaging и App Engine.
  • +
+ +

+Подробные сведения о возможностях Android Studio +изложены в руководстве Android Studio Basics.

+
+ + +

Если вы используете Eclipse с ADT, вам следует знать, что Android Studio теперь является официальной средой разработки для +Android, поэтому переходите на Android Studio, чтобы всегда иметь под рукой новейшие +инструменты разработки приложений. Сведения о том, как перенести свои проекты, +приведены в документе Переход на Android +Studio.

+ + + + + + + +

Требования к системе

+ +

Windows

+ +
    +
  • Microsoft® Windows® 8/7/Vista/2003 (32- или 64-разрядная версия)
  • +
  • ОЗУ не менее 2 ГБ (рекомендуется 4 ГБ)
  • +
  • 400 МБ свободного места на жестком диске
  • +
  • Не менее 1 ГБ для Android SDK, системных образов эмулятора и кэшированных файлов
  • +
  • Экран с разрешением не менее 1280 x 800 точек
  • +
  • Java Development Kit (JDK) 7
  • +
  • Дополнительно для эмулятора с ускорителем: Процессор Intel® с поддержкой Intel® VT-x, Intel® EM64T +(Intel® 64) и функцией XD-Bit
  • +
+ + +

Mac OS X

+ +
    +
  • Mac® OS X® 10.8.5 или более поздней версии вплоть до 10.9 (Mavericks)
  • +
  • ОЗУ не менее 2 ГБ (рекомендуется 4 ГБ)
  • +
  • 400 МБ свободного места на жестком диске
  • +
  • Не менее 1 ГБ для Android SDK, системных образов эмулятора и кэшированных файлов
  • +
  • Экран с разрешением не менее 1280 x 800 точек
  • +
  • Java Runtime Environment (JRE) 6
  • +
  • Java Development Kit (JDK) 7
  • +
  • Дополнительно для эмулятора с ускорителем: Процессор Intel® с поддержкой Intel® VT-x, Intel® EM64T +(Intel® 64) и функцией XD-Bit
  • +
+ +

В Mac OS для оптимизации отображения шрифтов Android Studio рекомендуется запускать с +Java Runtime Environment (JRE) 6. Впоследствии проекты можно настроить на использование Java Development Kit (JDK) 6 или JDK 7.

+ + + +

Linux

+ +
    +
  • Рабочий стол GNOME или KDE
  • +
  • Библиотека GNU C (glibc) 2.15 или более поздней версии
  • +
  • ОЗУ не менее 2 ГБ (рекомендуется 4 ГБ)
  • +
  • 400 МБ свободного места на жестком диске
  • +
  • Не менее 1 ГБ для Android SDK, системных образов эмулятора и кэшированных файлов
  • +
  • Экран с разрешением не менее 1280 x 800 точек
  • +
  • Oracle® Java Development Kit (JDK) 7
  • +
+

Работа Android Studio протестирована в ОС Ubuntu® 14.04, Trusty Tahr (64-разрядная версия с поддержкой запуска +32-разрядных приложений).

+ + + + +

Другие варианты загрузки

+ + diff --git a/docs/html-intl/intl/ru/sdk/installing/adding-packages.jd b/docs/html-intl/intl/ru/sdk/installing/adding-packages.jd new file mode 100644 index 0000000000000000000000000000000000000000..19d1edd2f1528b9216af877112ce68460437d0b0 --- /dev/null +++ b/docs/html-intl/intl/ru/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=Добавление пакетов SDK + +page.tags=менеджер sdk + +@jd:body + + + + +

+По умолчанию SDK Android включает в себя не все инструменты, которые необходимы для того, чтобы приступить к разработке приложений. +Инструменты, платформы и другие компоненты представлены в Android SDK в виде отдельных пакетов, которые при +необходимости можно загрузить с помощью +менеджера SDK Android. +Поэтому, прежде чем приступить к работе, в пакет SDK Android необходимо добавить некоторые дополнительные пакеты.

+ +

Для добавления пакетов необходимо запустить менеджер SDK. Существует несколько способов запуска менеджера.

+
    +
  • В Android Studio щелкните элемент SDK Manager + на панели инструментов.
  • +
  • Для тех, кто не пользуется Android Studio: +
      +
    • Windows: Дважды щелкните файл SDK Manager.exe, который находится в корневом каталоге пакета Android +SDK.
    • +
    • Mac/Linux: Откройте окно терминала и перейдите в каталог tools/ пакета Android +SDK, после чего выполните команду android sdk.
    • +
    +
  • +
+ +

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

+ + +
    +
  1. +

    Загрузите актуальные инструменты SDK

    + + + +

    Во время установки SDK Android +необходимо как минимум загрузить актуальные инструменты и платформу Android.

    +
      +
    1. Откройте каталог Tools и выберите следующее: +
        +
      • Инструменты Android SDK;
      • +
      • Инструменты платформы Android SDK;
      • +
      • Инструменты сборки Android SDK (последнюю версию).
      • +
      +
    2. +
    3. Откройте первую папку Android X.X (последней версии) и выберите следующее: +
        +
      • Платформа SDK
      • +
      • системный образ для эмулятора, например
        + ARM EABI v7a System Image.
      • +
      +
    4. +
    +
  2. + +
  3. +

    Загрузите вспомогательную библиотеку для дополнительных API-интерфейсов

    + + + +

    Во вспомогательной библиотеке Android +представлен широкий набор API-интерфейсов, которые совместимы с большинством версий ОС Android.

    + +

    Откройте каталог Extras (Дополнения) и выберите следующее:

    +
      +
    • Android Support Repository (Репозиторий вспомогательных библиотек Android);
    • +
    • Вспомогательная библиотека Android
    • +
    + +

     

    +

     

    + +
  4. + + +
  5. +

    Загрузите службы Google Play, чтобы получить доступ к еще большему количеству API-интерфейсов

    + + + +

    Для разработки приложений с помощью API-интерфейсов Google вам потребуется пакет служб Google Play.

    +

    Откройте каталог Extras (Дополнения) и выберите следующее:

    +
      +
    • Google Repository (Репозиторий Google);
    • +
    • Google Play services (службы Google Play).
    • +
    + +

    Примечание. API-интерфейсы служб Google Play доступны не на всех устройствах +Android, однако предлагаются на всех устройствах с доступом к магазину Google Play. Для использования +этих API-интерфейсов в эмуляторе Android необходимо также установить системный образ API-интерфейсов Google, +который находится в менеджере SDK в папке актуальной версии Android X.X.

    +
  6. + + +
  7. +

    Установите пакеты

    +

    После выбора всех необходимых пакетов можно продолжить установку.

    +
      +
    1. Нажмите кнопку Install X packages.
    2. +
    3. В появившемся окне дважды щелкните имя каждого пакета, находящегося в области слева, +чтобы принять условия лицензии для каждого из них.
    4. +
    5. Нажмите кнопку Install.
    6. +
    +

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

    +
  8. + +
  9. +

    Приступайте к созданию приложений

    + +

    После загрузки необходимых пакетов в SDK Android вы можете приступать к созданию приложений для +Android. По мере выхода новых инструментов и других API-интерфейсов просто запустите менеджер SDK +и загрузите новые пакеты.

    + +

    Вот некоторые варианты того, как можно приступить к работе:

    + +
    +
    +

    Для новичков

    +

    Если вы делаете только первые шаги в разработке приложений Android, рекомендуем ознакомиться с основами приложений Android и обратиться к +руководству по созданию своего первого приложения.

    + +
    +
    +

    Создание приложений для носимых устройств

    +

    Если вы готовы приступить к созданию приложений для носимых устройств Android, ознакомьтесь с руководством по +созданию приложений для ОС Android Wear.

    + +
    +
    +

    Использование API-интерфейсов Google

    +

    Чтобы начать работу с API-интерфейсами Google, такими как Карты или +службы Google Play, рекомендуем обратиться к руководству по +настройке служб Google +Play.

    + +
    +
    + + +
  10. + +
+ + diff --git a/docs/html-intl/intl/ru/training/material/animations.jd b/docs/html-intl/intl/ru/training/material/animations.jd new file mode 100644 index 0000000000000000000000000000000000000000..9808a9fc7af1ca2f15ba4b571fe3d0f6e39fc21c --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=Определение настраиваемой анимации + +@jd:body + + + + +

Благодаря анимациям в Material Design пользователи получают отклик на выполняемые +действия. Кроме того, анимации обеспечивают зрительную связь при взаимодействии с приложением. Тема Material Design содержит ряд анимаций +по умолчанию для кнопок и переходов, а в Android 5.0 (уровень API 21) и более поздних версиях можно настраивать эти анимации +и создавать новые:

+ +
    +
  • реакция на касание;
  • +
  • круговое появление;
  • +
  • переходы;
  • +
  • перемещение по кривой;
  • +
  • изменение состояний представления.
  • +
+ + +

Настройка реакции на касание

+ +

Реакция на касание в Material Design обеспечивает моментальное визуальное подтверждение взаимодействия пользователя с элементами интерфейса в точке касания. + В стандартной анимации +для реакции на нажатие кнопок используется новый класс {@link android.graphics.drawable.RippleDrawable}, обеспечивающий переход между разными состояниями с созданием эффекта ряби. +

+ +

В большинстве случаев эту возможность следует применять в XML-файле представления, указав фон представления следующим образом: +

+ +
    +
  • ?android:attr/selectableItemBackground для ограниченной области ряби;
  • +
  • ?android:attr/selectableItemBackgroundBorderless для ряби, распространяемой за границы представления. + При отрисовке она будет ограничиваться ближайшим родительским элементом представления со значением фона, отличным от null. +
  • +
+ +

Примечание. selectableItemBackgroundBorderless — это новый атрибут, представленный в уровне API 21. +

+ + +

Также можно определить {@link android.graphics.drawable.RippleDrawable} + в качестве XML-ресурса с помощью элемента ripple.

+ +

Можно назначить цвет для объектов {@link android.graphics.drawable.RippleDrawable}. Чтобы +изменить стандартный цвет отклика на касание, воспользуйтесь атрибутом темы android:colorControlHighlight +.

+ +

Дополнительные сведения представлены в справке по API для класса {@link +android.graphics.drawable.RippleDrawable}.

+ + +

Применение эффекта появления

+ +

Анимация эффекта появления обеспечивает зрительную связь с действиями пользователя, когда отображается или скрывается группа элементов интерфейса. + С помощью метода {@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()} можно анимировать ограничивающий круг, чтобы отобразить или скрыть с экрана представление. +

+ +

Как отобразить ранее скрытое представление с помощью этого эффекта:

+ +
+// previously invisible view
+View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the final radius for the clipping circle
+int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
+
+// create the animator for this view (the start radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
+
+// make the view visible and start the animation
+myView.setVisibility(View.VISIBLE);
+anim.start();
+
+ +

Как скрыть ранее отображавшееся представление с помощью этого эффекта:

+ +
+// previously visible view
+final View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the initial radius for the clipping circle
+int initialRadius = myView.getWidth();
+
+// create the animation (the final radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
+
+// make the view invisible when the animation is done
+anim.addListener(new AnimatorListenerAdapter() {
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        super.onAnimationEnd(animation);
+        myView.setVisibility(View.INVISIBLE);
+    }
+});
+
+// start the animation
+anim.start();
+
+ + +

Настройка переходов

+ + +
+
+ +
+
+

Рисунок 1. Переход с общими элементами. +

+ Для воспроизведения фильма нажмите экран устройства +
+
+ +

Переходы в приложениях Material Design обеспечивают зрительные связи между различными состояниями путем движения элементов и преобразований между общими элементами. + Можно выбрать настраиваемые анимации для начальных и конечных переходов, а также для переходов общих элементов между операциями. +

+ +
    +
  • Начальный переход определяет порядок появления на экране представлений в операции. + Например, в начальном переходе explode представления появляются на экране извне и перемещаются к центру экрана. +
  • + +
  • Конечный переход определяет порядок исчезновения с экрана представлений в операции. Например, в конечном переходе explode представления исчезают +с экрана в направлении из центра к краям. +
  • + +
  • Переход общих элементов определяет порядок перехода между операциями представлений, используемых в обеих операциях. + Например, если в двух +операциях используется одно и то же изображение, но в разных позициях и с разными размерами, в случае применения перехода общего элемента changeImageTransform выполняется плавное перемещение и масштабирование изображения между этими операциями. +
  • +
+ +

В Android 5.0 (уровень API 21) поддерживаются следующие начальные и конечные переходы:

+ +
    +
  • explode — перемещение представлений в центр экрана или из центра;
  • +
  • slide — перемещение представлений к одному из краев экрана или от него;
  • +
  • fade — отображение или скрытие представления на экране путем изменения его прозрачности.
  • +
+ +

Любой переход, являющийся наследованием класса {@link android.transition.Visibility}, поддерживается как начальный или конечный переход. + Дополнительные сведения представлены в справке по API для класса +{@link android.transition.Transition}.

+ +

В Android 5.0 (уровень API 21) также поддерживаются следующие переходы общих элементов:

+ +
    +
  • changeBounds — анимация изменений границ макетов целевых представлений;
  • +
  • changeClipBounds — анимация изменений границ обрезки целевых представлений;
  • +
  • changeTransform — анимация изменений параметров масштабирования и поворота целевых представлений;
  • +
  • changeImageTransform — анимация изменений размеров и параметров масштабирования целевых изображений.
  • +
+ +

Если активировать переходы в приложении, то между начальной и конечной операциями активируется стандартный переход методом плавной замены. +

+ + +

Рисунок 2. Переход с одним общим элементом. +

+ +

Определение настраиваемых переходов

+ +

Сначала необходимо активировать переходы содержимого окна с помощью атрибута android:windowContentTransitions +при определении стиля, наследуемого из темы Material Design. В определении стиля можно указать начальный и конечный переходы, а также переходы общих элементов: +

+ +
+<style name="BaseAppTheme" parent="android:Theme.Material">
+  <!-- enable window content transitions -->
+  <item name="android:windowContentTransitions">true</item>
+
+  <!-- specify enter and exit transitions -->
+  <item name="android:windowEnterTransition">@transition/explode</item>
+  <item name="android:windowExitTransition">@transition/explode</item>
+
+  <!-- specify shared element transitions -->
+  <item name="android:windowSharedElementEnterTransition">
+    @transition/change_image_transform</item>
+  <item name="android:windowSharedElementExitTransition">
+    @transition/change_image_transform</item>
+</style>
+
+ +

Переход change_image_transform в этом примере задается следующим образом:

+ +
+<!-- res/transition/change_image_transform.xml -->
+<!-- (see also Shared Transitions below) -->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+  <changeImageTransform/>
+</transitionSet>
+
+ +

Элемент changeImageTransform соответствует классу +{@link android.transition.ChangeImageTransform}. Дополнительные сведения представлены в справке по API для {@link android.transition.Transition}. +

+ +

Чтобы активировать в своем коде переходы содержимого окна, вызовите метод +{@link android.view.Window#requestFeature Window.requestFeature()}:

+ +
+// inside your activity (if you did not enable transitions in your theme)
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+
+ +

Чтобы задать переходы в своем коде, вызовите следующие методы с использованием объекта {@link +android.transition.Transition}:

+ +
    +
  • {@link android.view.Window#setEnterTransition Window.setEnterTransition()};
  • +
  • {@link android.view.Window#setExitTransition Window.setExitTransition()};
  • +
  • {@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()};
  • +
  • {@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}.
  • +
+ +

Методы {@link android.view.Window#setExitTransition setExitTransition()} и {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} задают конечный переход для вызывающей операции. + Методы {@link android.view.Window#setEnterTransition +setEnterTransition()} и {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} задают начальный переход для вызываемой операции.

+ +

Чтобы в полной мере реализовать возможности перехода, необходимо активировать переходы содержимого окна как в вызывающей, так и в вызываемой операции. + В противном случае вызывающая операция запустит конечный переход, однако будет выполнен переход окна (например, масштабирование или затемнение). +

+ +

Чтобы запустить начальный переход как можно раньше, используйте в вызываемой операции метод +{@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} +. Это позволит сделать начальные переходы более эффектными.

+ +

Запуск операции с помощью переходов

+ +

Если в приложении разрешены переходы и для операции задан конечный переход, переход активируется при запуске другой операции следующим образом: +

+ +
+startActivity(intent,
+              ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
+
+ +

Если для второй операции задан начальный переход, он также активируется при запуске операции. + Чтобы отключить переходы при запуске другой операции, укажите +значение null для набора параметров.

+ +

Запуск операции с помощью общего элемента

+ +

Порядок анимации перехода на экране между двумя операциями с общим элементом

+ +
    +
  1. Активируйте в своей теме переходы содержимого окна.
  2. +
  3. В определении стиля укажите переходы общих элементов.
  4. +
  5. Определите свой переход как XML-ресурс.
  6. +
  7. Присвойте одинаковое имя общим элементам в обоих макетах, используя для этого атрибут +android:transitionName.
  8. +
  9. Воспользуйтесь методом {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()}.
  10. +
+ +
+// get the element that receives the click event
+final View imgContainerView = findViewById(R.id.img_container);
+
+// get the common element for the transition in this activity
+final View androidRobotView = findViewById(R.id.image_small);
+
+// define a click listener
+imgContainerView.setOnClickListener(new View.OnClickListener() {
+    @Override
+    public void onClick(View view) {
+        Intent intent = new Intent(this, Activity2.class);
+        // create the transition animation - the images in the layouts
+        // of both activities are defined with android:transitionName="robot"
+        ActivityOptions options = ActivityOptions
+            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
+        // start the new activity
+        startActivity(intent, options.toBundle());
+    }
+});
+
+ +

Для общих динамических представлений, создаваемых в коде, используйте метод +{@link android.view.View#setTransitionName View.setTransitionName()} для определения одинакового имени элемента в обеих операциях. +

+ +

Чтобы выполнить анимацию обратного перехода по завершении второй операции, вызовите метод +{@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} + вместо{@link android.app.Activity#finish Activity.finish()}.

+ +

Запуск операции с несколькими общими элементами

+ +

Чтобы создать анимацию перехода на экране между двумя операциями с несколькими общими +элементами, определите общие элементы в обоих макетах с помощью атрибута android:transitionName + (или воспользуйтесь методом {@link android.view.View#setTransitionName View.setTransitionName()} в обеих +операциях), а затем создайте объект {@link android.app.ActivityOptions}, как указано ниже.

+ +
+ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
+        Pair.create(view1, "agreedName1"),
+        Pair.create(view2, "agreedName2"));
+
+ + +

Использование перемещения по кривой

+ +

При анимации в Material Design используются кривые для интерполяции по времени и создания схем перемещения в пространстве. + В Android 5.0 (уровень API 21) и более поздних версиях имеется возможность определить для анимаций настраиваемые кривые синхронизации и схемы перемещения по кривой. +

+ +

Класс {@link android.view.animation.PathInterpolator} — это новый интерполятор на +основе кривой Безье или объекта {@link android.graphics.Path}. Данный интерполятор определяет перемещение по кривой в квадрате 1 x 1 с привязкой в точках (0,0) и (1,1), а также с контрольными точками, задаваемыми с помощью аргументов конструктора. + + Также можно определить интерполятор траектории в качестве XML-ресурса:

+ +
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1"/>
+
+ +

В системе имеются XML-ресурсы для трех основных кривых в спецификации Material Design: +

+ +
    +
  • @interpolator/fast_out_linear_in.xml;
  • +
  • @interpolator/fast_out_slow_in.xml;
  • +
  • @interpolator/linear_out_slow_in.xml.
  • +
+ +

Можно передать объект {@link android.view.animation.PathInterpolator} в метод {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()}.

+ +

Класс {@link android.animation.ObjectAnimator} имеет новые конструкторы, с помощью которых можно анимировать координаты вдоль траектории перемещения, используя для этого не менее двух свойств. + Например, следующий аниматор +использует объект {@link android.graphics.Path} для анимации свойств представления по осям X и Y:

+ +
+ObjectAnimator mAnimator;
+mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
+...
+mAnimator.start();
+
+ + +

Анимация изменений состояния представления

+ +

С помощью класса {@link android.animation.StateListAnimator} можно определить аниматоры, которые запускаются при изменении состояния представления. + В следующем примере показан порядок определения {@link +android.animation.StateListAnimator} в качестве XML-ресурса:

+ +
+<!-- animate the translationZ property of a view when pressed -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_pressed="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="@android:integer/config_shortAnimTime"
+        android:valueTo="2dp"
+        android:valueType="floatType"/>
+        <!-- you could have other objectAnimator elements
+             here for "x" and "y", or other properties -->
+    </set>
+  </item>
+  <item android:state_enabled="true"
+    android:state_pressed="false"
+    android:state_focused="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="100"
+        android:valueTo="0"
+        android:valueType="floatType"/>
+    </set>
+  </item>
+</selector>
+
+ +

Чтобы присоединить к представлению настраиваемые анимации состояния представления, определите аниматор, используя элемент +selector в файле XML-ресурса (как в этом примере), а затем назначьте его своему представлению +с помощью атрибута android:stateListAnimator. Чтобы в своем коде назначить представлению аниматор + списка состояний, используйте метод {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()}, а затем назначьте аниматор своему представлению с помощью метода +{@link android.view.View#setStateListAnimator View.setStateListAnimator()}.

+ +

Если ваша тема является расширением темы Material Design, по умолчанию у кнопок имеется возможность анимации по оси Z. Чтобы отключить +такое поведение кнопок, задайте для атрибута android:stateListAnimator значение +@null.

+ +

С помощью класса {@link android.graphics.drawable.AnimatedStateListDrawable} можно создавать элементы, которые служат для отображения анимации между изменениями состояния связанного представления. + В Android 5.0 в некоторых системных виджетах такая анимация используется по умолчанию. + В следующем примере показан порядок +определения {@link android.graphics.drawable.AnimatedStateListDrawable} в качестве XML-ресурса:

+ +
+<!-- res/drawable/myanimstatedrawable.xml -->
+<animated-selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- provide a different drawable for each state-->
+    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
+        android:state_pressed="true"/>
+    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
+        android:state_focused="true"/>
+    <item android:id="@id/default"
+        android:drawable="@drawable/drawableD"/>
+
+    <!-- specify a transition -->
+    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
+        <animation-list>
+            <item android:duration="15" android:drawable="@drawable/dt1"/>
+            <item android:duration="15" android:drawable="@drawable/dt2"/>
+            ...
+        </animation-list>
+    </transition>
+    ...
+</animated-selector>
+
+ + +

Анимация векторных элементов

+ +

Векторные элементы можно масштабировать без ущерба четкости. + Класс {@link android.graphics.drawable.AnimatedVectorDrawable} +позволяет анимировать свойства векторного элемента.

+ +

Анимированные векторные элементы обычно определяются в трех XML-файлах:

+ +
    +
  • векторный элемент с элементом <vector> в +res/drawable/;
  • +
  • анимированный векторный элемент с элементом <animated-vector> в +res/drawable/;
  • +
  • один или несколько аниматоров для объектов с элементом <objectAnimator> в +res/anim/.
  • +
+ +

С помощью анимированных векторных элементов можно анимировать атрибуты элементов <group> и +<path>. Элемент<group> определяет набор траекторий +или подгрупп, а элемент <path> — траектории для прорисовки.

+ +

При определении векторного элемента, который требуется анимировать, используйте атрибут android:name +для назначения уникальных имен группам или траекториям, чтобы на них можно было сослаться в определениях аниматора. + Например:

+ +
+<!-- res/drawable/vectordrawable.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:width="64dp"
+    android:viewportHeight="600"
+    android:viewportWidth="600">
+    <group
+        android:name="rotationGroup"
+        android:pivotX="300.0"
+        android:pivotY="300.0"
+        android:rotation="45.0" >
+        <path
+            android:name="v"
+            android:fillColor="#000000"
+            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+    </group>
+</vector>
+
+ +

Определение анимированного векторного элемента ссылается на группы и траектории в векторном элементе, используя их имена: +

+ +
+<!-- res/drawable/animvectordrawable.xml -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:drawable="@drawable/vectordrawable" >
+    <target
+        android:name="rotationGroup"
+        android:animation="@anim/rotation" />
+    <target
+        android:name="v"
+        android:animation="@anim/path_morph" />
+</animated-vector>
+
+ +

Определения анимации представляются объектами {@link android.animation.ObjectAnimator} или {@link +android.animation.AnimatorSet}. Первый аниматор в этом примере поворачивает целевую группу на 360 градусов: +

+ +
+<!-- res/anim/rotation.xml -->
+<objectAnimator
+    android:duration="6000"
+    android:propertyName="rotation"
+    android:valueFrom="0"
+    android:valueTo="360" />
+
+ +

Второй аниматор в этом примере преобразует траекторию векторного элемента из одной формы в другую. + Для преобразования обе траектории должны быть совместимы: они должны содержать одинаковое количество команд, а также одинаковое количество параметров для каждой команды. +

+ +
+<!-- res/anim/path_morph.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:duration="3000"
+        android:propertyName="pathData"
+        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
+        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
+        android:valueType="pathType" />
+</set>
+
+ +

Дополнительные сведения представлены в справке по API для {@link +android.graphics.drawable.AnimatedVectorDrawable}.

diff --git a/docs/html-intl/intl/ru/training/material/compatibility.jd b/docs/html-intl/intl/ru/training/material/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..b7ca338045fa9427ea7ecec79d2ec7b2940acdc7 --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=Обеспечение совместимости + +@jd:body + + + + +

Некоторые возможности Material Design, такие как "материальные" темы и настраиваемые переходы между операциями, + доступны только в Android 5.0 (уровень API 21) и более поздних версиях. Однако приложения можно разработать таким образом, +чтобы эти функции были доступны при запуске на устройствах, поддерживающих Material Design, сохранив при этом +совместимость приложений с устройствами под управлением более ранних выпусков Android.

+ + +

Определение альтернативных стилей

+ +

Приложение можно настроить так, чтобы тема Material Design использовалась при работе на поддерживающих эту технологию устройствах, +а при запуске приложения на устройствах с более ранними версиями Android происходило переключение на старые варианты тем. Ниже описан порядок такой настройки.

+ +
    +
  1. В файле +res/values/styles.xml определите тему, наследующую более старой теме (например, Holo).
  2. +
  3. В файле +res/values-v21/styles.xml определите тему с таким же именем, наследующую теме Material Design.
  4. +
  5. В файле манифеста укажите эту тему как тему приложения.
  6. +
+ +

Примечание. +Если в приложении используется тема Material Design, но отсутствует определенная описанным способом альтернативная тема, +это приложение не будет запускаться на устройствах под управлением версий Android, предшествующих 5.0. +

+ + +

Предоставление альтернативных макетов

+ +

Если в создаваемых согласно рекомендациям Material Design макетах отсутствуют новые +атрибуты XML, представленные в Android 5.0 (уровень API 21), +такие макеты поддерживаются в предыдущих версиях ОС Android. В противном случае необходимо предоставить альтернативные макеты. Можно также предоставить +альтернативные макеты для настройки внешнего вида приложения в более ранних версиях Android.

+ +

Создайте файлы макета для Android 5.0 (уровень API 21) в res/layout-v21/, +а также файлы альтернативного макета для более ранних версий Android (в res/layout/). +Например, res/layout/my_activity.xml — это альтернативный макет для +res/layout-v21/my_activity.xml.

+ +

Чтобы сделать код более компактным, определите стили в res/values/, измените + стили в res/values-v21/ для новых API, а также используйте наследование стилей, + определив основные стили в res/values/ и задав наследование из них в res/values-v21/.

+ + +

Использование вспомогательной библиотеки

+ +

Вспомогательные библиотеки v7 +r21 и более поздних версий включают следующие функции Material Design:

+ + + +

Системные виджеты

+ +

В темах Theme.AppCompat имеются стили Material Design для следующих виджетов:

+ +
    +
  • {@link android.widget.EditText};
  • +
  • {@link android.widget.Spinner};
  • +
  • {@link android.widget.CheckBox};
  • +
  • {@link android.widget.RadioButton};
  • +
  • {@link android.support.v7.widget.SwitchCompat};
  • +
  • {@link android.widget.CheckedTextView}.
  • +
+ +

Цветовая палитра

+ +

Чтобы получить стили Material Design и настроить цветовую палитру с помощью вспомогательной библиотеки +Android v7, примените одну из следующих тем Theme.AppCompat:

+ +
+<!-- extend one of the Theme.AppCompat themes -->
+<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
+    <!-- customize the color palette -->
+    <item name="colorPrimary">@color/material_blue_500</item>
+    <item name="colorPrimaryDark">@color/material_blue_700</item>
+    <item name="colorAccent">@color/material_green_A200</item>
+</style>
+
+ +

Списки и карточки

+ +

Виджеты {@link android.support.v7.widget.RecyclerView} и {@link +android.support.v7.widget.CardView} имеются в более ранних версиях Android. Они находятся во +вспомогательной библиотеке Android v7, и для них существуют следующие ограничения.

+
    +
  • В виджете {@link android.support.v7.widget.CardView} применяется программная реализация тени +с помощью дополнительного отступа.
  • +
  • Виджет {@link android.support.v7.widget.CardView} не выполняет обрезку своих дочерних представлений, +пересекающихся со скругленными углами.
  • +
+ + +

Зависимости

+ +

Чтобы воспользоваться этими возможностями в версиях Android, предшествующих 5.0 (уровень API 21), включите в свой проект вспомогательную библиотеку +Android v7 как зависимость Gradle:

+ +
+dependencies {
+    compile 'com.android.support:appcompat-v7:21.0.+'
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
+ + +

Проверка версии системы

+ +

Следующие функции доступны только в Android 5.0 (уровень API 21) и более поздних версиях:

+ +
    +
  • переходы;
  • +
  • реакция на касание;
  • +
  • анимации появления;
  • +
  • анимации по траектории;
  • +
  • векторные элементы;
  • +
  • тонирование элементов дизайна.
  • +
+ +

Чтобы обеспечить совместимость приложения с более ранними версиями Android, проверьте системный параметр {@link +android.os.Build.VERSION#SDK_INT version} во время запуска, прежде чем вызвать API любой из этих +функций:

+ +
+// Check if we're running on Android 5.0 or higher
+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+    // Call some material design APIs here
+} else {
+    // Implement this feature without material design
+}
+
+ +

Примечание. Чтобы указать версии Android, поддерживаемые вашим приложением, +установите атрибуты android:minSdkVersion и android:targetSdkVersion +в файле манифеста. Чтобы использовать функции Material Design, которые имеются в Android 5.0, задайте +для атрибута android:targetSdkVersion значение 21. Дополнительные сведения см. + в руководстве по API +<uses-sdk>.

diff --git a/docs/html-intl/intl/ru/training/material/drawables.jd b/docs/html-intl/intl/ru/training/material/drawables.jd new file mode 100644 index 0000000000000000000000000000000000000000..2554f071729093237d4e5c90db42148172fca61c --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=Работа с элементами дизайна + +@jd:body + + + +

Указанные ниже возможности по работе с элементами дизайна позволяют реализовать в приложении элементы Material Design:

+ +
    +
  • тонирование элементов дизайна;
  • +
  • извлечение главного цвета;
  • +
  • создание векторных элементов.
  • +
+ +

В этом уроке рассматривается порядок использования этих функций в приложении.

+ + +

Тонирование элементов дизайна

+ +

В Android 5.0 (уровень API 21) и более поздних версий можно тонировать растровые изображения и изображения в формате NinePatch, определенные как +альфа-маски. Для тонирования можно применять как цветовые ресурсы, так и атрибуты темы, которые +разрешаются в цветовые ресурсы (например, ?android:attr/colorPrimary). Обычно такие ресурсы создаются +только один раз, после чего они автоматически окрашиваются для соответствия цветам темы.

+ +

Тонирование можно применить к объектам {@link android.graphics.drawable.BitmapDrawable} и {@link +android.graphics.drawable.NinePatchDrawable} с помощью метода {@code setTint()}. Также можно +задать цвет и способ тонирования в макетах, используя для этого атрибуты android:tint и +android:tintMode.

+ + +

Извлечение главных цветов на изображении

+ +

Во вспомогательной библиотеке Android r21 и более поздних версий содержится класс {@link +android.support.v7.graphics.Palette}, +с помощью которого можно извлекать следующие главные цвета на изображении:

+ +
    +
  • насыщенные цвета;
  • +
  • насыщенные темные цвета;
  • +
  • насыщенные светлые цвета;
  • +
  • приглушенные цвета;
  • +
  • приглушенные темные цвета;
  • +
  • приглушенные светлые цвета.
  • +
+ +

Для извлечения этих цветов передайте объект {@link android.graphics.Bitmap} в статический метод +{@link android.support.v7.graphics.Palette#generate Palette.generate()} в фоновом +потоке, где загружаются изображения. Если невозможно использовать этот поток, вместо этого вызовите метод +{@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} и укажите +модуль прослушивания.

+ +

Извлечь главные цвета на изображении можно также с помощью методов getter из класса +Palette, таких как Palette.getVibrantColor.

+ +

Чтобы использовать класс {@link android.support.v7.graphics.Palette} в своем проекте, добавьте в модуль своего приложения следующую +зависимость Gradle: +

+ +
+dependencies {
+    ...
+    compile 'com.android.support:palette-v7:21.0.0'
+}
+
+ +

Дополнительные сведения представлены в справке по API для класса {@link android.support.v7.graphics.Palette} +.

+ + +

Создание векторных элементов

+ + + +
+

Видео

+

Векторная графика в Android

+
+
+ +

В Android 5.0 (уровень API 21) и более поздних версий имеется возможность определить векторные элементы, которые можно масштабировать без ущерба для четкости. + Для векторного изображения требуется лишь один файл ресурсов, тогда как для растровых изображений необходим отдельный файл ресурсов для каждой плотности экрана. + Чтобы создать векторное изображение, укажите детали формы в XML-элементе <vector>. +

+ +

В следующем примере создается векторное изображение в форме сердца.

+ +
+<!-- res/drawable/heart.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    <!-- intrinsic size of the drawable -->
+    android:height="256dp"
+    android:width="256dp"
+    <!-- size of the virtual canvas -->
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+  <!-- draw a path -->
+  <path android:fillColor="#8fff"
+      android:pathData="M20.5,9.5
+                        c-1.955,0,-3.83,1.268,-4.5,3
+                        c-0.67,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
+
+ +

Векторные изображения представлены в Android как объекты {@link android.graphics.drawable.VectorDrawable}. + Дополнительные сведения о синтаксисе pathData см. в справке по траекториям SVG. Дополнительные сведения +об анимации свойств векторных элементов см. в разделе +Анимация векторных элементов.

diff --git a/docs/html-intl/intl/ru/training/material/get-started.jd b/docs/html-intl/intl/ru/training/material/get-started.jd new file mode 100644 index 0000000000000000000000000000000000000000..476de7f1bcf5c3ca43f419670954b5ce15632b38 --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=Начало работы + +@jd:body + + + + +

Порядок создания приложения с элементами Material Design

+ +
    +
  1. + Ознакомьтесь со спецификацией Material Design.
  2. +
  3. + Примените тему Material Design к своему приложению.
  4. +
  5. + Создайте макеты в соответствии с рекомендациями Material Design.
  6. +
  7. + Установите высоту своих представлений для отбрасывания теней.
  8. +
  9. + Используйте системные виджеты для списков и карточек.
  10. +
  11. + Настройте анимацию в своем приложении.
  12. +
+ +

Обеспечение обратной совместимости

+ +

В приложении можно реализовать множество функций Material Design и одновременно сохранить его совместимость с версиями Android, предшествующими версии 5.0. + Дополнительные сведения представлены на странице +Обеспечение совместимости.

+ +

Обновление приложения с добавлением элементов Material Design

+ +

Чтобы дополнить существующее приложение функциями и элементами Material Design, обновите макеты в соответствии с рекомендациями Material Design. + Также не забудьте добавить в приложение функции глубины, реакции на касание и анимации. +

+ +

Создание новых приложений с элементами Material Design

+ +

При создании приложений с "материальными" функциями следуйте рекомендациям Material Design, которые позволят вам получить целостное представление о новых принципах дизайна. + При проектировании и разработке своего приложения следуйте инструкциям и используйте новые функциональные возможности платформы Android. +

+ + +

Применение темы Material Design

+ +

Чтобы применить тему Material Design в своем приложении, укажите стиль, который наследует от +android:Theme.Material:

+ +
+<!-- res/values/styles.xml -->
+<resources>
+  <!-- your theme inherits from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- theme customizations -->
+  </style>
+</resources>
+
+ +

В теме Material Design содержатся обновленные системные виджеты, для которых можно настраивать цветовую палитру, а также стандартные анимации для реакции на касания и переходы. + Дополнительные сведения представлены в разделе +Использование темы Material Design.

+ + +

Разработка макетов

+ +

Помимо применения и настройки темы Material Design, необходимо соответствие +рекомендациям Material Design используемых макетов. При разработке макетов необходимо контролировать следующие элементы: +

+ +
    +
  • сетки базовых линий;
  • +
  • контуры;
  • +
  • интервалы;
  • +
  • размер целевой области касания;
  • +
  • структура макета.
  • +
+ + +

Определение высоты представлений

+ +

Представления могут отбрасывать тени, и значение высоты определяет размер тени и порядок ее прорисовки. + Чтобы установить высоту представления, используйте в макетах атрибут +android:elevation:

+ +
+<TextView
+    android:id="@+id/my_textview"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:text="@string/next"
+    android:background="@color/white"
+    android:elevation="5dp" />
+
+ +

С помощью нового свойства translationZ можно создавать анимации, которые отражают временные изменения в высоте представления. + Изменения высоты могут быть полезны при +реагировании на сенсорные +жесты.

+ +

Дополнительные сведения представлены в разделе Определение теней и обрезка представлений. +

+ + +

Создание списков и карточек

+ +

{@link android.support.v7.widget.RecyclerView} представляет собой более гибкую версию {@link +android.widget.ListView}. Она поддерживает различные типы макетов и способствует повышению производительности. +{@link android.support.v7.widget.CardView} обеспечивает единообразное отображение фрагментов информации внутри карточек в разных приложениях. + В следующем примере показано, как включить +{@link android.support.v7.widget.CardView} в макет:

+ +
+<android.support.v7.widget.CardView
+    android:id="@+id/card_view"
+    android:layout_width="200dp"
+    android:layout_height="200dp"
+    card_view:cardCornerRadius="3dp">
+    ...
+</android.support.v7.widget.CardView>
+
+ +

Дополнительные сведения представлены в разделе Создание списков и карточек. +

+ + +

Настройка анимации

+ +

В состав Android 5.0 (уровень API 21) входят новые API для создания в приложении настраиваемой анимации. +Например, можно разрешать переходы между операциями и задавать для операций конечный переход: +

+ +
+public class MyActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // enable transitions
+        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+        setContentView(R.layout.activity_my);
+    }
+
+    public void onSomeButtonClicked(View view) {
+        getWindow().setExitTransition(new Explode());
+        Intent intent = new Intent(this, MyOtherActivity.class);
+        startActivity(intent,
+                      ActivityOptions
+                          .makeSceneTransitionAnimation(this).toBundle());
+    }
+}
+
+ +

При запуске одной операции из другой активируется конечный переход.

+ +

Подробные сведения о новых API для анимации см. в разделе Определение настраиваемой анимации.

diff --git a/docs/html-intl/intl/ru/training/material/index.jd b/docs/html-intl/intl/ru/training/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..0b3f1c4fc0ba81ebd142b98f54b4cfeff6dba9c8 --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=Создание приложений с помощью Material Design +page.type=проектирование +page.image=images/cards/material_2x.png +page.metaDescription=Научитесь применять Material Design к своим приложениям. + + +@jd:body + +
+
+

Необходимые знания и компоненты

+
    +
  • Android 5.0 (уровень API 21)
  • +
+
+
+ +

Material Design представляет собой комплексную концепцию создания визуальных, движущихся и интерактивных элементов для различных платформ и устройств. + Чтобы применить элементы Material Design в своих приложениях Android, +руководствуйтесь инструкциями в +спецификации Material +Design, а также воспользуйтесь новыми компонентами и функциями, которые доступны в Android 5.0 (уровень API 21). +

+ +

Данный курс обучения посвящен созданию приложений Material Design, в ходе которого используются следующие элементы:

+ +
    +
  • тема Material Design;
  • +
  • виджеты для карточек и списков;
  • +
  • настраиваемые тени и обрезка представлений;
  • +
  • векторные элементы;
  • +
  • настраиваемая анимация.
  • +
+ +

В этом курсе также рассматривается обеспечение обратной совместимости нового приложения, в котором используются функции Material Design, с версиями Android, предшествующими версии 5.0 (уровень API 21). +

+ +

Уроки

+ +
+
Начало работы
+
Узнайте, как обновлять приложения с помощью элементов Material Design.
+ +
Использование темы Material Design
+
Узнайте, как применять стили Material Design в приложениях.
+ +
Создание списков и карточек
+
Узнайте, как с помощью системных виджетов создавать единообразные списки и карточки.
+ +
Определение теней и обрезка представлений
+
Узнайте, как устанавливать высоту представлений для создания настраиваемых теней, а также обрезать представления.
+ +
Работа с элементами дизайна
+
Узнайте, как создавать векторные элементы и тонировать элементы дизайна.
+ +
Определение настраиваемой анимации
+
Узнайте, как создавать настраиваемую анимацию для представлений и переходов между операциями, в которых используются общие элементы.
+ +
Обеспечение совместимости
+
Узнайте, как обеспечить совместимость приложений с версиями платформы, предшествующими Android 5.0.
+
diff --git a/docs/html-intl/intl/ru/training/material/lists-cards.jd b/docs/html-intl/intl/ru/training/material/lists-cards.jd new file mode 100644 index 0000000000000000000000000000000000000000..44ff1601734f87a3d6917bf16b78b90ca268d234 --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=Создание списков и карточек + +@jd:body + + + + +

Для создания в приложениях составных списков и карточек с помощью стилей Material Design можно использовать виджеты +{@link android.support.v7.widget.RecyclerView} и {@link android.support.v7.widget.CardView}. +

+ + +

Создание списков

+ +

Виджет {@link android.support.v7.widget.RecyclerView} представляет собой расширенную и более гибкую версию {@link android.widget.ListView}. + Он является контейнером для отображения больших наборов данных, которые можно эффективно прокручивать, сохраняя при этом ограниченное количество представлений. + Виджет +{@link android.support.v7.widget.RecyclerView} рекомендуется использовать в случаях, когда имеются коллекции данных, элементы которых изменяются во время выполнения в зависимости от действий пользователя или сетевых событий. +

+ +

Класс {@link android.support.v7.widget.RecyclerView} упрощает отображение и обработку больших наборов данных с помощью следующих возможностей: +

+ +
    +
  • менеджеров макетов для размещения элементов;
  • +
  • стандартных анимаций для операций с общими элементами, таких как удаление или добавление элементов.
  • +
+ +

Также предоставляются гибкие возможности определения настраиваемых менеджеров макетов и анимации для виджетов {@link +android.support.v7.widget.RecyclerView}.

+ + +

+Рисунок 1. Виджет RecyclerView. +

+ +

Чтобы воспользоваться виджетом {@link android.support.v7.widget.RecyclerView}, необходимо определить адаптер и менеджер макетов. + Для создания адаптера необходимо наследовать класс {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}. Характеристики реализации зависят от особенностей используемого набора данных и типа представлений. + Дополнительные сведения представлены в примерах ниже. +

+ +
+ +

+Рисунок 2. Список с использованием RecyclerView. +

+
+ +

Менеджер макетов размещает представления элемента внутри {@link +android.support.v7.widget.RecyclerView} и определяет порядок повторного использования представлений элемента, которые перестают быть видимыми пользователю. + Чтобы повторно использовать (или перезапустить) представление, менеджер макетов может запросить адаптер заменить содержимое представления другим элементом из набора данных. + Такой перезапуск представлений позволяет повысить производительность за счет того, что исключается создание ненужных представлений и выполнение ресурсоемких операций поиска {@link android.app.Activity#findViewById findViewById()}. + +

+ +

Виджет {@link android.support.v7.widget.RecyclerView} включает следующие встроенные менеджеры макетов:

+ +
    +
  • {@link android.support.v7.widget.LinearLayoutManager} для отображения элементов в виде списка с вертикальной или горизонтальной прокруткой; +
  • +
  • {@link android.support.v7.widget.GridLayoutManager} для отображения элементов в виде сетки;
  • +
  • {@link android.support.v7.widget.StaggeredGridLayoutManager} для отображения элементов в виде шахматной сетки.
  • +
+ +

Для создания менеджера макетов необходимо наследовать класс {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager}.

+ +

Анимация

+ +

По умолчанию анимация для добавления и удаления элементов включается в виджете {@link +android.support.v7.widget.RecyclerView}. Для настройки такой анимации следует наследовать класс +{@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} и воспользоваться методом + {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} +.

+ +

Примеры

+ +

В следующем примере демонстрируется, как включить в макет виджет +{@link android.support.v7.widget.RecyclerView}:

+ +
+<!-- A RecyclerView with some commonly used attributes -->
+<android.support.v7.widget.RecyclerView
+    android:id="@+id/my_recycler_view"
+    android:scrollbars="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
+
+ +

После добавления виджета {@link android.support.v7.widget.RecyclerView} подключите дескриптор объекта, подключите его к менеджеру макетов, а затем подключите адаптер к данным, которые будут отображаться: + +

+ +
+public class MyActivity extends Activity {
+    private RecyclerView mRecyclerView;
+    private RecyclerView.Adapter mAdapter;
+    private RecyclerView.LayoutManager mLayoutManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.my_activity);
+        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
+
+        // use this setting to improve performance if you know that changes
+        // in content do not change the layout size of the RecyclerView
+        mRecyclerView.setHasFixedSize(true);
+
+        // use a linear layout manager
+        mLayoutManager = new LinearLayoutManager(this);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+
+        // specify an adapter (see also next example)
+        mAdapter = new MyAdapter(myDataset);
+        mRecyclerView.setAdapter(mAdapter);
+    }
+    ...
+}
+
+ +

Адаптер обеспечивает доступ к элементам в наборе данных, создает представления для элементов и заменяет содержимое некоторых представлений новыми элементами данных, когда исходный элемент уже не отображается. + + Пример кода ниже является простой реализацией набора данных, который состоит из массива строк, выведенного на экран с помощью виджетов {@link android.widget.TextView}: +

+ +
+public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
+    private String[] mDataset;
+
+    // Provide a reference to the views for each data item
+    // Complex data items may need more than one view per item, and
+    // you provide access to all the views for a data item in a view holder
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        // each data item is just a string in this case
+        public TextView mTextView;
+        public ViewHolder(TextView v) {
+            super(v);
+            mTextView = v;
+        }
+    }
+
+    // Provide a suitable constructor (depends on the kind of dataset)
+    public MyAdapter(String[] myDataset) {
+        mDataset = myDataset;
+    }
+
+    // Create new views (invoked by the layout manager)
+    @Override
+    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+                                                   int viewType) {
+        // create a new view
+        View v = LayoutInflater.from(parent.getContext())
+                               .inflate(R.layout.my_text_view, parent, false);
+        // set the view's size, margins, paddings and layout parameters
+        ...
+        ViewHolder vh = new ViewHolder(v);
+        return vh;
+    }
+
+    // Replace the contents of a view (invoked by the layout manager)
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        // - get element from your dataset at this position
+        // - replace the contents of the view with that element
+        holder.mTextView.setText(mDataset[position]);
+
+    }
+
+    // Return the size of your dataset (invoked by the layout manager)
+    @Override
+    public int getItemCount() {
+        return mDataset.length;
+    }
+}
+
+ + +
+ +

+Рисунок 3. Примеры карточек. +

+
+ +

Создание карточек

+ +

Виджет {@link android.support.v7.widget.CardView} является расширением класса {@link android.widget.FrameLayout} +и позволяет отображать информацию внутри карточек, которые будут выглядеть одинаково в рамках платформы. Виджеты {@link +android.support.v7.widget.CardView} могут отбрасывать тени и иметь закругленные углы.

+ +

Чтобы создать карточку, отбрасывающую тень, воспользуйтесь атрибутом card_view:cardElevation. В Android 5.0 (уровень API 21) и более поздних версий виджет +{@link android.support.v7.widget.CardView} использует реальную +высоту и динамические тени, а в более ранних версиях в виджете реализованы программные тени. +Дополнительные сведения представлены на странице Обеспечение +совместимости.

+ +

Для настройки внешнего вида виджета +{@link android.support.v7.widget.CardView} используйте следующие свойства:

+ +
    +
  • чтобы задать радиус скругления углов в макете, используйте атрибут card_view:cardCornerRadius; +
  • +
  • чтобы задать радиус скругления углов в программном коде, используйте метод CardView.setRadius;
  • +
  • чтобы задать цвет фона карточки, используйте атрибут card_view:cardBackgroundColor. +
  • +
+ +

В следующем примере показано, как включить виджет {@link android.support.v7.widget.CardView} + в макет:

+ +
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    ... >
+    <!-- A CardView that contains a TextView -->
+    <android.support.v7.widget.CardView
+        xmlns:card_view="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/card_view"
+        android:layout_gravity="center"
+        android:layout_width="200dp"
+        android:layout_height="200dp"
+        card_view:cardCornerRadius="4dp">
+
+        <TextView
+            android:id="@+id/info_text"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </android.support.v7.widget.CardView>
+</LinearLayout>
+
+ +

Дополнительные сведения представлены в справке по API для {@link android.support.v7.widget.CardView}.

+ + +

Добавление зависимостей

+ +

Виджеты {@link android.support.v7.widget.RecyclerView} и {@link android.support.v7.widget.CardView} +входят во вспомогательные +библиотеки v7. Чтобы использовать эти виджеты в своем проекте, добавьте в модуль приложения следующие +зависимости Gradle: +

+ +
+dependencies {
+    ...
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
diff --git a/docs/html-intl/intl/ru/training/material/shadows-clipping.jd b/docs/html-intl/intl/ru/training/material/shadows-clipping.jd new file mode 100644 index 0000000000000000000000000000000000000000..a1c41fcd93d85aeb370b535a31c180f72155ba62 --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=Определение теней и обрезка представлений + +@jd:body + + + +

В Material Design появилась возможность настройки высоты элементов интерфейса. Высота позволяет пользователю понять относительную важность каждого элемента и сосредоточиться на выполнении поставленной задачи. +

+ +

Высота представления, выраженная с помощью свойства Z, определяет внешний вид его тени: представления с более высоким значением Z отбрасывают более мягкие тени большего размера. + Представления с более высокими значениями Z перекрывают представления с более низкими значениями Z, однако значение Z не влияет на размер самого представления. +

+ +

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

+ +

Установка высоты также полезна для создания анимации, когда виджеты временно поднимаются выше плоскости представления при выполнении какого-либо действия. +

+ +

Дополнительные сведения об установке высоты в Material Design представлены на странице +Объекты в трехмерном пространстве. +

+ + +

Установка высоты представления

+ +

Значение Z для представления включает два компонента: + +

    +
  • высота: статический компонент;
  • +
  • смещение: динамический компонент, используемый для анимации.
  • +
+ +

Z = elevation + translationZ

+ + +

Рисунок 1. Тени для различной высоты представления.

+ +

Чтобы установить высоту представления в определении макета, используйте атрибут android:elevation +. Чтобы установить высоту представления в программном коде операции, используйте метод +{@link android.view.View#setElevation View.setElevation()}.

+ +

Чтобы установить смещение представления, используйте метод {@link android.view.View#setTranslationZ +View.setTranslationZ()}.

+ +

Новые методы {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} и {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} позволяют с легкостью анимировать изменение высоты представлений. + Дополнительные сведения см. в справке по API для +{@link android.view.ViewPropertyAnimator}, а также в руководстве по анимации свойств для разработчиков. +

+ +

Также можно использовать класс {@link android.animation.StateListAnimator} для декларирования этих анимаций. + Это особенно полезно в тех случаях, когда анимация запускается при изменении состояния, например, когда пользователь нажимает на кнопку. + Дополнительные сведения см. в разделе +Анимация изменений состояния представления.

+ +

Значения Z измеряются в dp (пиксели, не зависящие от плотности).

+ + +

Настройка теней и контуров представления

+ +

Границы фонового элемента представления определяют форму его тени по умолчанию. +Контуры — это внешние границы графического объекта, они также определяют область ряби для отклика на касание. +

+ +

В примере ниже рассматривается это представление, определенное с помощью фонового элемента дизайна:

+ +
+<TextView
+    android:id="@+id/myview"
+    ...
+    android:elevation="2dp"
+    android:background="@drawable/myrect" />
+
+ +

Фоновый элемент дизайна определен как прямоугольник со скругленными углами:

+ +
+<!-- res/drawable/myrect.xml -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#42000000" />
+    <corners android:radius="5dp" />
+</shape>
+
+ +

Представление отбрасывает тень с закругленными углами, поскольку контур представления определяется фоновым элементом дизайна. + Если указать настраиваемый контур, он будет переопределять стандартную форму тени для представления.

+ +

Порядок определения настраиваемого контура представления в программном коде

+ +

    +
  1. Сначала наследуйте класс {@link android.view.ViewOutlineProvider}.
  2. +
  3. Затем переопределите метод {@link android.view.ViewOutlineProvider#getOutline getOutline()}.
  4. +
  5. Наконец, назначьте источник контуров для своего представления с помощью метода {@link +android.view.View#setOutlineProvider View.setOutlineProvider()}.
  6. +
+ +

Можно создавать овальные и прямоугольные контуры со скругленными углами, используя для этого методы класса +{@link android.graphics.Outline}. Стандартный источник контуров получает контуры из фона представления. + Чтобы представление не отбрасывало тень, задайте для источника контуров значение null. +

+ + +

Обрезка представления

+ +

С помощью обрезки представлений можно с легкостью изменять их форму. Представление можно обрезать для обеспечения единообразия элементов дизайна. +Также можно изменить форму представления в ответ на действия пользователя. +С помощью метода {@link android.view.View#setClipToOutline +View.setClipToOutline()} или атрибута android:clipToOutline представление можно обрезать по его контуру. Обрезать +можно только прямоугольники, круги и скругленные прямоугольники, как определено в методе +{@link android.graphics.Outline#canClip Outline.canClip()}.

+ +

Чтобы обрезать представление по форме элемента дизайна, установите этот элемент в качестве фона представления +(как показано выше) и вызовите метод {@link android.view.View#setClipToOutline View.setClipToOutline()} +.

+ +

Обрезка представления — довольно ресурсоемкая операция, поэтому не анимируйте форму, которую вы используете для обрезки представления. + Для достижения требуемого эффекта используйте анимацию эффекта появления.

diff --git a/docs/html-intl/intl/ru/training/material/theme.jd b/docs/html-intl/intl/ru/training/material/theme.jd new file mode 100644 index 0000000000000000000000000000000000000000..320f308312bfe4afe164c6010f894f04d710110e --- /dev/null +++ b/docs/html-intl/intl/ru/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=Использование темы Material Design + +@jd:body + + + + +

Новая тема Material Design предоставляет следующие возможности:

+ +
    +
  • системные виджеты, для которых можно настраивать цветовую палитру;
  • +
  • анимация для реакции на касание для системных виджетов;
  • +
  • анимация переходов между операциями.
  • +
+ +

Внешний вид темы Material Design можно настроить с учетом фирменного стиля, воспользовавшись для этого цветовой палитрой. + Можно изменить оттенок строки действий и строки состояния, используя атрибуты темы, как показано на рисунке 3. +

+ +

Системные виджеты отличаются новым дизайном и анимацией отклика на касание. В своем приложении вы можете настроить цветовую палитру, анимацию отклика на касание и переходы между операциями. +

+ +

Тема Material Design задается следующими параметрами:

+ +
    +
  • @android:style/Theme.Material (темные цвета);
  • +
  • @android:style/Theme.Material.Light (светлые цвета);
  • +
  • @android:style/Theme.Material.Light.DarkActionBar.
  • +
+ +

Список доступных стилей Material Design см. в справке по API для +{@link android.R.style R.style}.

+ + +
+
+ +
+

Рисунок 1. Тема Material Design в темных тонах.

+
+
+
+ +
+

Рисунок 2. Тема Material Design в светлых тонах.

+
+
+
+
+ +

+Примечание. Темы Material Design доступны только в ОС Android 5.0 (уровень API 21) и более поздних версий. + Во вспомогательных библиотеках v7 + представлены темы со стилями Material Design для некоторых виджетов. Эти библиотеки также обеспечивают поддержку настройки цветовой палитры. + Дополнительные сведения см. на странице +Обеспечение совместимости. +

+ + +

Настройка цветовой палитры

+ +

Для настройки основных цветов в соответствии с фирменным стилем определите настраиваемые цвета с помощью атрибутов темы при наследовании от темы Material Design: +

+ +
+<resources>
+  <!-- inherit from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- Main theme colors -->
+    <!--   your app branding color for the app bar -->
+    <item name="android:colorPrimary">@color/primary</item>
+    <!--   darker variant for the status bar and contextual app bars -->
+    <item name="android:colorPrimaryDark">@color/primary_dark</item>
+    <!--   theme UI controls like checkboxes and text fields -->
+    <item name="android:colorAccent">@color/accent</item>
+  </style>
+</resources>
+
+ +
+ +

+Рисунок 3. Настройка темы Material Design.

+
+ + +

Настройка строки состояния

+ +

В теме Material Design можно с легкостью настроить строку состояния, указав нужный цвет в соответствии с фирменным стилем и задав достаточную контрастность для отображения белых значков состояния. + Чтобы установить настраиваемый цвет для строки состояния, воспользуйтесь атрибутом android:statusBarColor при наследовании темы Material Design. + + По умолчанию параметр android:statusBarColor наследует значение android:colorPrimaryDark. +

+ +

Кроме того, можно самостоятельно разместить элемент за строкой состояния. Например, если требуется наложить прозрачную строку состояния поверх фотографии, применив еле уловимый темный градиент, чтобы были видны белые значки состояния. + + Для этого задайте для атрибута android:statusBarColor значение +@android:color/transparent и настройте флаги окна требуемым образом. Также можно воспользоваться +методом {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} для применения анимации или эффекта постепенного исчезания. +

+ +

+Примечание. Строка состояния почти всегда должна иметь четкую границу, отделяющую ее от основной панели инструментов, за исключением случаев, когда за этими панелями от края и до края экрана отображается большое количество изображений или мультимедийный контент, а также в случае, когда вы используете градиент, чтобы обеспечить видимость значков. + + +

+ +

При настройке панели навигации и строки состояния сделайте их прозрачными либо измените только строку состояния. + Во всех остальных случаях панель навигации должна оставаться черной.

+ + +

Отдельные представления темы

+ +

Элементы в определениях макета XML могут задавать атрибут android:theme, который ссылается на ресурс темы. + Этот атрибут изменяет тему для элемента и любых дочерних элементов, +что можно использовать для изменения цветовых палитр темы в определенной области интерфейса. +

diff --git a/docs/html-intl/intl/vi/design/get-started/principles.jd b/docs/html-intl/intl/vi/design/get-started/principles.jd new file mode 100644 index 0000000000000000000000000000000000000000..61b2c77c973664e907004291875c0b6cc70161ae --- /dev/null +++ b/docs/html-intl/intl/vi/design/get-started/principles.jd @@ -0,0 +1,307 @@ +page.title=Nguyên tắc Thiết kế Android +@jd:body + +

Những nguyên tắc thiết kế này được xây dựng bởi và dành cho +Nhóm Trải nghiệm Người dùng Android để đảm bảo lợi ích tốt nhất cho người dùng. +Đối với nhà phát triển và nhà thiết kế Android, chúng tiếp tục +nhấn mạnh những nguyên tắc chỉ đạo thiết kế chi tiết hơn dành cho các loại +thiết bị khác nhau.

+ +

+Hãy cân nhắc những nguyên tắc này khi áp dụng sự sáng tạo +và tư duy thiết kế của mình. Thay đổi có mục đích. +

+ +

Thu hút tôi

+ +
+
+ +

Khiến tôi xao xuyến vì ngạc nhiên

+

Cảnh vật đẹp, hoạt hình được bố trí cẩn thận hay hiệu ứng âm thanh đúng lúc khiến +trải nghiệm càng thêm phần thích thú. Hiệu ứng tinh tế góp phần đem lại cảm giác nhẹ nhàng nhưng +mạnh mẽ trong tầm tay.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Các đối tượng đời thật trông thú vị hơn các nút bấm và menu

+

Để người dùng trực tiếp chạm và thao tác các đối tượng trong ứng dụng của bạn. Làm như vậy vừa giảm nỗ lực nhận thức +cần thiết để thực hiện một tác vụ, vừa đem lại sự thỏa mãn về cảm xúc.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Hãy để tôi biến nó thành của mình

+

Người dùng thích cảm giác tiếp xúc cá nhân bởi nó giúp họ cảm thấy rất tự nhiên và nắm quyền kiểm soát. Cung cấp +thiết lập mặc định tuyệt đẹp, dễ cảm nhận, nhưng cũng phải xét đến những tùy chỉnh thú vị, có thể lựa chọn để không cản trở +nhiệm vụ chính yếu.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Hiểu rõ về tôi

+

Tìm hiểu sở thích của người dùng qua thời gian. Thay vì bảo họ chọn +đi chọn lại cùng một thứ, hãy bố trí để những lựa chọn trước đó dễ dàng trong tầm với.

+ +
+
+ + + +
+
+ +

Đơn giản hóa cuộc sống của tôi

+ +
+
+ +

Hãy ngắn gọn

+

Sử dụng những cụm từ ngắn với từ ngữ đơn giản. Người dùng thường hay bỏ qua những câu dài dòng.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Hình ảnh đến nhanh hơn câu chữ

+

Hãy nghĩ đến việc dùng hình ảnh để giải thích ý tưởng. Chúng dễ thu hút sự chú ý của người dùng và có thể hiệu quả hơn nhiều +so với từ ngữ.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Quyết định hộ tôi nhưng phải để tôi “chốt hạ”

+

Hãy đưa ra phán đoán tốt nhất và hành động thay vì hỏi trước. Quá nhiều lựa chọn và quyết định khiến người dùng +không vui. Phòng trường hợp bạn sai, hãy cho 'hoàn tác'.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Chỉ hiện những thứ tôi cần khi tôi cần đến

+

Người dùng sẽ bị quá tải khi họ thấy quá nhiều thứ cùng lúc. Hãy chia các tác vụ và thông tin thành những đoạn nhỏ, +dễ nắm bắt. Ẩn những tùy chọn không thiết yếu vào thời điểm đó và nhắc người dùng khi đi qua chúng.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Tôi cần luôn biết mình đang ở đâu

+

Khiến người khác tự tin rằng họ biết mình đang ở đâu. Khiến những địa điểm trong ứng dụng của bạn trông khác biệt +và sử dụng chuyển tiếp để thể hiện mối quan hệ giữa các màn hình. Cung cấp phản hồi về các tác vụ đang diễn ra.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Không bao giờ đánh mất những thứ của tôi

+

Lưu những thứ người dùng mất thời gian để tạo và cho phép họ truy cập chúng từ mọi nơi. Nhớ các cài đặt, +thao tác chạm cá nhân và nội dung khởi tạo giữa các điện thoại, máy tính bảng và máy tính. Điều đó sẽ khiến việc nâng cấp +trở nên dễ dàng nhất trên đời.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Nếu bề ngoài giống nhau thì hành động cũng phải như nhau

+

Giúp người dùng phân biệt sự khác nhau về chức năng bằng cách khiến bề ngoài trông khác biệt thay vì chỉ khác một chút. +Tránh những chế độ trông giống nhau nhưng lại phản hồi khác nhau với cùng nội dung đầu vào.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Chỉ làm tôi gián đoạn nếu thật sự quan trọng

+

Như một trợ lý cá nhân đúng nghĩa, hãy tránh làm người dùng mất thời gian vào thứ không quan trọng. Người dùng thường muốn +tập trung và trừ trường hợp rất quan trọng và nhạy cảm về thời gian, nếu không việc gián đoạn có thể khiến họ khó chịu.

+ +
+
+ + + +
+
+ +

Làm tôi ấn tượng

+ +
+
+ +

Cho tôi xem những trò có thể áp dụng ở mọi nơi

+

Người dùng thấy thích thú khi họ tự khám phá mọi thứ. Thiết kế ứng dụng của bạn dễ học hỏi hơn nhờ +tận dụng các kiểu mẫu trực quan và trí nhớ có điều kiện từ các ứng dụng Android khác. Ví dụ, cử chỉ trượt nhanh +có thể là một lối tắt điều hướng hay.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Không phải lỗi của tôi

+

Nhẹ nhàng trong cách nhắc người dùng sửa lỗi. Họ luôn muốn cảm thấy thông minh khi dùng ứng dụng +của bạn. Nếu có gì đó không đúng, hãy đưa ra chỉ dẫn khắc phục rõ ràng nhưng bỏ qua những chi tiết kỹ thuật. +Nếu bạn có thể khắc phục mà không làm phiền họ thì càng tuyệt.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Khơi gợi sự khuyến khích

+

Chia các nhiệm vụ phức tạp thành nhiều bước nhỏ hơn và dễ hoàn thành. Phản hồi hành động, +kể cả khi đó chỉ là một vầng sáng nhỏ.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Làm giúp tôi những chuyện nặng nhọc

+

Khiến người mới cảm thấy mình như chuyên gia bằng cách cho phép họ làm những việc mà họ chưa từng nghĩ mình có thể làm được. Ví +dụ, các lối tắt kết hợp nhiều hiệu ứng ảnh chụp có thể khiến một bức ảnh nghiệp dư trở nên đáng kinh ngạc +chỉ sau vài bước.

+ +
+
+ + + +
+
+ +
 
+ +
+
+ +

Nhanh chóng đến phần quan trọng

+

Không phải hành động nào cũng như nhau. Quyết định xem điều gì là quan trọng nhất trong ứng dụng của bạn và khiến nó dễ tìm thấy và +nhanh chóng được sử dụng, ví dụ như nút chụp trong camera hoặc nút tạm dừng trong một trình phát nhạc.

+ +
+
+ + + +
+
diff --git a/docs/html-intl/intl/vi/design/material/index.jd b/docs/html-intl/intl/vi/design/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..d72636f0c79b0f6ed45a01b6034405f21871cd3e --- /dev/null +++ b/docs/html-intl/intl/vi/design/material/index.jd @@ -0,0 +1,186 @@ +page.title=Material Design cho Android +page.tags=Material,design +page.type=thiết kế +page.image=images/cards/design-material-for-android_2x.jpg + +@jd:body + + + +
+

Tài liệu cho Nhà phát triển

+

Tạo ứng dụng với Material Design

+
+
+ + + +
+

Video

+

Giới thiệu Material Design

+
+
+ + + +
+

Video

+

Giấy và Mực: Những vật liệu quan trọng

+
+
+ + + +
+

Video

+

Material Design trong Google I/O App

+
+
+ + + +

Material design là một hướng dẫn toàn diện về thiết kế +trực quan, chuyển động và tương tác giữa nhiều nền tảng và thiết bị. Hiện nay Android có hỗ trợ +những ứng dụng theo phong cách material design. Để sử dụng material design trong ứng dụng Androi của mình, hãy làm theo hướng dẫn nêu +trong đặc tả material design và sử dụng +những thành phần và tính năng mới sẵn có trong Android 5.0 (API mức 21) trở lên.

+ +

Android cung cấp những phần tử sau để bạn dựng ứng dụng theo phong cách material design:

+ +
    +
  • Một giao diện mới
  • +
  • Widget mới cho các dạng xem phức tạp
  • +
  • API mới cho đổ bóng và hoạt hình tùy chỉnh
  • +
+ +

Để biết thêm thông tin về triển khai material design trên Android, hãy xem +Tạo ứng dụng với Material Design.

+ + +

Giao diện Material

+ +

Giao diện material mang đến một phong cách mới cho ứng dụng của bạn và các widget hệ thống, cho phép bạn đặt +bảng màu của chúng và hoạt hình mặc định cho phản hồi chạm và chuyển tiếp hoạt động.

+ + +
+
+ +
+

Giao diện material tối

+
+
+
+ +
+

Giao diện material sáng

+
+
+
+
+ +

Để biết thêm thông tin, hãy xem phần Sử dụng Giao diện Material +.

+ + +

Danh sách và Thẻ

+ +

Android cung cấp hai loại widget mới để hiển thị thẻ và danh sách bằng phong cách +và hoạt hình material design:

+ + +
+
+ +

Widget RecyclerView mới là một phiên bản có thể ghép nối hơn của ListView + có hỗ trợ nhiều kiểu bố trí khác nhau và cải thiện hiệu năng.

+
+
+ +

Widget CardView mới cho phép bạn hiển thị những mẩu thông tin quan trọng bên trong + thẻ với diện mạo và cảm nhận nhất quán.

+
+
+
+ +

Để biết thêm thông tin, hãy xem phần Tạo Danh sách +và Thẻ.

+ + +

Đổ bóng Dạng xem

+ +

Bên cạnh các thuộc tính X và Y, dạng xem trong Android nay có +thêm thuộc tính Z. Thuộc tính mới này biểu diễn độ cao của một dạng xem và sẽ quyết định:

+ +
    +
  • Kích cỡ của đổ bóng: dạng xem có giá trị Z cao hơn sẽ đặt đổ bóng lớn hơn.
  • +
  • Thứ tự vẽ: dạng xem có giá trị Z cao hơn sẽ xuất hiện phía trên những dạng xem khác.
  • +
+ +
+
+ +
+
+ Để phát lại phim, nhấp vào màn hình thiết bị +
+
+ +

Để biết thêm thông tin, hãy xem phần Định nghĩa +Đổ bóng và Dạng xem Cắt hình.

+ + +

Hoạt hình

+ +

API hoạt hình mới cho phép bạn tạo hoạt hình tùy chỉnh cho phản hồi chạm trong điều khiển UI, +thay đổi trạng thái xem và chuyển tiếp hoạt động.

+ +

Những API này cho phép bạn:

+ +
    +
  • +Phản hồi lại sự kiện chạm trong dạng xem của mình bằng hoạt hình phản hồi chạm. +
  • +
  • +Ẩn và hiện dạng xem bằng hoạt hình hiện hình tròn. +
  • +
  • +Chuyển giữa các hoạt động bằng hoạt hình chuyển tiếp hoạt động tùy chỉnh. +
  • +
  • +Tạo hoạt hình tự nhiên hơn bằng chuyển động cong. +
  • +
  • +Tạo hoạt hình cho các thay đổi trong một hoặc nhiều thuộc tính dạng xem bằng hoạt hình thay đổi trạng thái dạng xem. +
  • +
  • +Thể hiện hoạt hình trong các nội dung vẽ được theo danh sách trạng thái giữa những thay đổi về trạng thái dạng xem. +
  • +
+ +

Hoạt hình phản hồi chạm được tích hợp vào một vài dạng xem tiêu chuẩn, ví dụ như các nút. Các API mới này +cho phép bạn tùy biến những hoạt hình này và thêm chúng vào dạng xem tùy chỉnh của mình.

+ +

Để biết thêm thông tin, hãy xem phần Định nghĩa Hoạt hình +Tùy chỉnh.

+ + +

Nội dung vẽ được

+ +

Những khả năng mới này của nội dung vẽ được sẽ giúp bạn triển khai các ứng dụng theo phong cách material design:

+ +
    +
  • Nội dung vẽ được véc-tơ có thể thay đổi kích cỡ mà không làm mất độ sắc nét và hoàn hảo +cho các biểu tượng trong ứng dụng đơn sắc.
  • +
  • Nhuộm nội dung vẽ được cho phép bạn định nghĩa ảnh bitmap thành mặt nạ alpha và nhuộm +màu cho chúng theo thời gian chạy.
  • +
  • Trích xuất màu cho phép bạn tự động trích xuất màu nổi bật từ +hình ảnh bitmap.
  • +
+ +

Để biết thêm thông tin, hãy xem phần Làm việc với +Nội dung Vẽ được.

diff --git a/docs/html-intl/intl/vi/design/patterns/compatibility.jd b/docs/html-intl/intl/vi/design/patterns/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..f1a610b06d34201ce440edf9bc37184158ecd301 --- /dev/null +++ b/docs/html-intl/intl/vi/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=Tương thích Ngược +page.tags="support" +page.metaDescription=Lưu ý về cách Android 4.x thích ứng với UI được thiết kế cho các phần cứng và phiên bản HĐH cũ hơn. +@jd:body + + +
+

Tài liệu cho Nhà phát triển

+

Hỗ trợ nhiều Thiết bị khác nhau

+
+
+ +

Những thay đổi đáng kể trong Android 3.0 gồm có:

+
    +
  • Bỏ phím cứng điều hướng (Quay lại, Menu, Tìm kiếm, Trang chủ̉), đổi thành xử lý điều hướng + thông qua các nút điều khiển ảo (Quay lại, Trang chủ, Gần đây).
  • +
  • Mẫu hình thiết thực để sử dụng menu trong thanh hành động.
  • +
+

Android 4.0 mang những thay đổi cho máy tính bảng sang nền tảng điện thoại.

+ +

Thích nghi Android 4.0 với Phần cứng và Ứng dụng cũ hơn

+ +
+
+ +

Điện thoại có các điều khiển điều hướng ảo

+

Ứng dụng Android được viết cho Android 3.0 trở lên sẽ hiển thị các hành động trong thanh hành động. Những hành động không vừa +với thanh hành động hoặc không đủ quan trọng để được hiển thị ở mức trên cùng sẽ xuất hiện trong +vùng tràn hành động.

+

Người dùng truy cập vùng tràn hành động bằng cách chạm vào nó trong thanh hành động.

+ +
+
+ + + +
+
+ +
+
+ +

Điện thoại có phím điều hướng vật lý

+

Điện thoại Android có phím cứng điều hướng truyền thống không hiển thị thanh điều hướng ảo ở +phía dưới màn hình. Thay vào đó, có thể sử dụng vùng tràn hành động từ phím cứng menu. Kết quả là +cửa sổ hành động bật lên có cùng kiểu như trong ví dụ trước, nhưng được hiển thị ở phía dưới màn hình.

+ +
+
+ + + +
+
+ +
+
+ +

Ứng dụng kế thừa trên điện thoại có điều khiển điều hướng ảo

+

Khi bạn chạy một ứng dụng được dựng cho Android 2.3 hoặc cũ hơn trên một điện thoại có điều khiển điều hướng +ảo, điều khiển vùng tràn hành động sẽ xuất hiện phía bên phải của thanh điều hướng ảo. Bạn có thể +chạm vào điều khiển này để hiển thị hành động của ứng dụng theo kiểu dáng menu Android truyền thống.

+ +
+
+ + + +
+
diff --git a/docs/html-intl/intl/vi/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/vi/design/patterns/confirming-acknowledging.jd new file mode 100644 index 0000000000000000000000000000000000000000..e0253c3d3f04eddd26b44d39dfd3253cd7462e86 --- /dev/null +++ b/docs/html-intl/intl/vi/design/patterns/confirming-acknowledging.jd @@ -0,0 +1,70 @@ +page.title=Xác nhận & Báo nhận +page.tags=dialog,toast,notification +@jd:body + +

Trong một số tình huống, khi người dùng gọi ra một hành động trong ứng dụng của bạn, bạn nên xác nhận hoặc báo nhận hành động đó thông qua văn bản.

+ +
+
+ +

Xác nhận là việc yêu cầu người dùng xác minh rằng họ thực sự muốn tiếp tục hành động mà họ vừa gọi ra. Trong một số trường hợp, xác nhận được đưa ra cùng với một cảnh báo hoặc thông tin trọng yếu liên quan tới hành động mà họ cần cân nhắc.

+
+
+ +

Báo nhận là việc hiển thị văn bản để người dùng biết rằng hành động họ vừa gọi ra đã được hoàn tất. Điều này loại bỏ sự không chắc chắn về các thao tác không biểu thị mà hệ thống đang thực hiện. Trong một số trường hợp, báo nhận được đưa ra cùng với một tùy chọn hoàn tác hành động đó.

+
+
+ +

Giao tiếp với người dùng theo những cách này có thể giúp gỡ bỏ sự không chắc chắn về những thứ đã hoặc sẽ xảy ra. Xác nhận hoặc báo nhận cũng có thể ngăn người dùng mắc sai lầm mà họ có thể hối tiếc.

+ +

Khi nào thì xác nhận hoặc báo nhận hành động của người dùng

+

Không phải hành động nào cũng chắc chắn có xác nhận hay báo nhận. Hãy sử dụng lưu đồ sau làm hướng dẫn đưa ra quyết định thiết kế của bạn.

+ + +

Xác nhận

+
+
+

Ví dụ: Google Play Books

+ +

Trong ví dụ này, người dùng đã yêu cầu xóa một cuốn sách khỏi thư viện Google Play của mình. Một cảnh báo hiện ra để xác nhận hành động này bởi quan trọng là phải hiểu rằng cuốn sách sẽ không còn khả dụng trên bất cứ thiết bị nào nữa.

+

Khi tạo một hộp thoại xác nhận, hãy đặt tiêu đề có ý nghĩa bằng cách thể hiện hành động được yêu cầu đó.

+
+
+

Ví dụ: Android Beam

+ +

Không nhất thiết phải đưa ra xác nhận trong cảnh báo có hai nút. Sau khi khởi chạy Android Beam, người dùng được nhắc chạm vào nội dung muốn chia sẻ (trong ví dụ này là một ảnh chụp). Nếu quyết định không tiếp tục, họ chỉ cần đưa điện thoại của mình ra xa.

+
+
+ +

Báo nhận

+
+
+

Ví dụ: Bỏ đi bản thảo Gmail đã lưu

+ +

Trong ví dụ này, nếu người dùng điều hướng quay lại hoặc lên trên từ màn hình soạn thảo Gmail thì có thể sẽ xảy ra việc ngoài ý muốn: bản thảo hiện tại được tự động lưu. Báo nhận dưới hình thức cửa sổ sẽ khiến bản thảo đó biểu hiện ra ngoài. Cửa sổ này sẽ mờ dần sau vài giây.

+

Ở đây việc hoàn tác sẽ không thích hợp bởi việc lưu là do ứng dụng khởi chạy chứ không phải người dùng. Sẽ dễ dàng và nhanh chóng tiếp tục soạn thư bằng cách điều hướng tới danh sách bản thảo.

+ +
+
+

Ví dụ: Đã xóa hội thoại Gmail

+ +

Sau khi người dùng xóa một hội thoại khỏi danh sách trong Gmail, báo nhận sẽ xuất hiện cùng tùy chọn hoàn tác. Báo nhận vẫn còn đó tới khi người dùng thực hiện một hành động không liên quan, ví dụ như cuộn danh sách.

+
+
+ +

Không xác nhận hoặc báo nhận

+
+
+

Ví dụ: hành động +1

+ +

Xác nhận là không cần thiết. Nếu người dùng vô tình +1 thì cũng không sao. Họ chỉ cần chạm lại vào nút này để hoàn tác hành động đó.

+

Báo nhận là không cần thiết. Người dùng sẽ thấy nút +1 bật lại và chuyển màu đỏ. Đó là một dấu hiệu rất rõ ràng.

+
+
+

Ví dụ: Xóa một ứng dụng khỏi Màn hình Trang chủ

+ +

Xác nhận là không cần thiết. Đây là một hành động có chủ ý: người dùng phải kéo và thả một mục lên một mụ̣c tiêu tương đối lớn và tách biệt. Vì thế, rất khó có khả năng xảy ra trường hợp vô ý. Nhưng nếu người dùng hối hận về quyết định, họ chỉ mất vài giây để đưa trở lại như cũ.

+

Báo nhận là không cần thiết. Người dùng sẽ biết ứng dụng đã biến mất khỏi Màn hình Trang chủ bởi họ khiến nó biến mất bằng cách kéo nó đi.

+ +
+
diff --git a/docs/html-intl/intl/vi/design/patterns/navigation.jd b/docs/html-intl/intl/vi/design/patterns/navigation.jd new file mode 100644 index 0000000000000000000000000000000000000000..98490db0e5b635d4344795a487e3545c41814df4 --- /dev/null +++ b/docs/html-intl/intl/vi/design/patterns/navigation.jd @@ -0,0 +1,213 @@ +page.title=Điều hướng bằng Quay lại và Lên trên +page.tags="navigation","activity","task","up navigation","back navigation" +page.image=/design/media/navigation_between_siblings_gmail.png +@jd:body + + +
+

Tài liệu cho Nhà phát triển

+

Triển khai Điều hướng Hiệu quả

+
+
+ +

Điều hướng nhất quán là một thành phần thiết yếu trong trải nghiệm người dùng nói chung. Một vài điều nhỏ bé lại khiến +người dùng khó chịu hơn so với điều hướng cơ bản chính là hành xử không nhất quán và không như kỳ vọng. Android 3.0 +đã giới thiệu những thay đổi đáng kể về hành vi điều hướng toàn cục. Tuân thủ kỹ lưỡng các hướng dẫn +về Quay lại và Lên trên sẽ giúp việc điều hướng trên ứng dụng của bạn trở nên dễ đoán và đáng tin cậy cho người dùng của bạn.

+

Phiên bản Android 2.3 và mới hơn dựa vào nút Quay lại của hệ thống để hỗ trợ điều hướng trong một +ứng dụng. Với việc giới thiệu thanh hành động trong Android 3.0, một cơ chế điều hướng thứ hai đã xuất hiện: +nút Lên trên, bao gồm biểu tượng ứng dụng và dấu nháy lùi trái.

+ + + +

Lên trên so với Quay lại

+ +

Nút Lên trên được sử dụng để điều hướng bên trong một ứng dụng dựa trên mối quan hệ thứ bậc +giữa các màn hình. Ví dụ, nếu màn hình A hiển thị một danh sách các mục và việc chọn một mục sẽ dẫn đến +màn hình B (trình bày chi tiết hơn về mục đó), khi đó B sẽ đưa ra nút Lên trên để +quay lại màn hình A.

+

Nếu một màn hình đang ở trên cùng trong một ứng dụng (cụ thể là trang chủ của ứng dụng), thì nó sẽ xuất hiện nút +Lên trên.

+ +

Nút Quay lại của hệ thống được sử dụng để điều hướng ngược theo thứ tự thời gian, qua lịch sử các màn hình +mà người dùng mới thao tác qua. Nó thường được dựa trên mối quan hệ thời gian +giữa các màn hình thay vì thứ bậc của ứng dụng.

+ +

Khi màn hình xem trước đó cũng đồng thời là màn hình mẹ theo thứ bậc của màn hình hiện tại, nhấn nút +Quay lại sẽ có cùng kết quả như khi nhấn nút Lên trên—điều này thường hay +xảy ra. Tuy nhiên, không như nút Lên trên đảm bảo người dùng vẫn còn trong ứng dụng của bạn, +nút Quay lại có thể trả người dùng về màn hình Trang chủ hoặc thậm chí sang một ứng dụng khác.

+ + + +

Nút Quay lại cũng hỗ trợ một vài hành vi không trực tiếp gắn với điều hướng giữa các màn hình: +

+
    +
  • Bỏ qua cửa sổ nổi (hộp thoại, cửa sổ bật lên)
  • +
  • Bỏ qua thanh hành động ngữ cảnh và bỏ tô sáng khỏi mục đã chọn
  • +
  • Ẩn bàn phím trên màn hình (IME)
  • +
+

Điều hướng trong Ứng dụng của Bạn

+ +

Điều hướng tới màn hình với nhiều điểm bắt đầu

+

Đôi khi một màn hình không chỉ có một vị trí cố định trong thứ bậc của ứng dụng và bạn có thể truy cập nó +từ nhiều điểm bắt đầu—ví dụ như màn hình cài đặt có thể được truy cập từ bất cứ màn hình +nào khác trong ứng dụng của bạn. Trong trường hợp này, nút Lên trên sẽ chọn quay lại màn hình đã chuyển đến, có tác dụng +giống như Quay lại.

+

Thay đổi dạng xem bên trong một màn hình

+

Thay đổi tùy chọn dạng xem cho một màn hình không làm thay đổi hành vi Lên trên hay Quay lại: màn hình vẫn +ở cùng một chỗ trong thứ bậc của ứng dụng và không tạo thêm lịch sử điều hướng mới nào.

+

Ví dụ về thay đổi dạng xem gồm:

+
    +
  • Chuyển dạng xem bằng tab và/hoặc trượt nhanh trái phải
  • +
  • Chuyển dạng xem bằng danh sách thả xuống (còn gọi là tab thu gọn)
  • +
  • Lọc một danh sách
  • +
  • Sắp xếp một danh sách
  • +
  • Thay đổi đặc tính hiển thị (như thu phóng)
  • +
+

Điều hướng giữa các màn hình anh em

+

Khi ứng dụng của bạn hỗ trợ điều hướng từ một danh mục tới một dạng xem chi tiết của một trong những mục đó, thường +nó ưu tiên hỗ trợ điều hướng theo hướng từ mục đó tới một mục khác ở phía trước hoặc +phía sau nó trong danh sách. Ví dụ, trong Gmail, bạn có thể dễ dàng trượt nhanh sang trái hoặc phải từ một hội thoại +để xem hội thoại mới hơn hoặc cũ hơn trong cùng Hộp thư đến. Như khi thay đổi dạng xem trong một màn hình, +điều hướng đó không làm thay đổi hành vi Lên trên hoặc Quay lại.

+ + + +

Tuy nhiên, có một trường hợp ngoại lệ đáng chú ý đó là khi duyệt giữa các dạng xem chi tiết có liên quan, không +ràng buộc với nhau bởi danh sách chuyển đến—ví dụ như khi duyệt trong Play Store giữa các ứng dụng từ +cùng một nhà phát triển hoặc giữa các album bởi cùng một nghệ sĩ. Trong các trường hợp này, truy cập vào từng liên kết không tạo ra +lịch sử, khiến nút Quay lại đi qua từng màn hình được xem trước đó. Thao tác Lên trên sẽ tiếp tục +bỏ qua những màn hình liên quan này và điều hướng tới màn hình bộ chứa được xem gần đây nhất.

+ + + +

Bạn có khả năng thiết kế để hành vi Lên trên thông minh hơn dựa trên hiểu biết của mình về dạng xem +chi tiết. Suy rộng từ ví dụ về Play Store bên trên, tưởng tượng người dùng đã điều hướng từ +Cuốn sách xem gần nhất tới chi tiết chuyển thể Phim. Trong trường hợp đó, thao tác Lên trên có thể trả về một bộ chứa +(Phim) mà trước đó người dùng chưa điều hướng qua.

+ + + +

Điều hướng tới Ứng dụng của Bạn thông qua Widget và Thông báo trên Màn hình Trang chủ

+ +

Bạn có thể sử dụng widget hoặc thông báo trên màn hình Trang chủ để giúp người dùng của mình điều hướng trực tiếp tới màn hình +nằm sâu trong thứ bậc ứng dụng của bạn. Ví dụ, widget Hộp thư đến của Gmail và thông báo thư mới có thể +vừa bỏ qua màn hình Hộp thư đến, vừa đưa người dùng trực tiếp tới dạng xem hội thoại.

+ +

Trong cả hai trường hợp này, hãy điều khiển nút Lên trên như sau:

+ +
    +
  • Nếu màn hình đích thường được truy cập từ một màn hình cụ thể trong ứng dụng +của bạn, thao tác Lên trên sẽ điều hướng tới màn hình đó.
  • +
  • Nếu không, thao tác Lên trên sẽ điều hướng tới màn hình trên cùng ("Trang chủ") của ứng dụng của bạn.
  • +
+ +

Trong trường hợp nút Quay lại, bạn nên thiết kế để việc điều hướng dễ đoán hơn bằng cách chèn vào ngăn xếp +của tác vụ toàn bộ đường dẫn điều hướng lên trên tới màn hình trên cùng của ứng dụng. Làm vậy sẽ cho phép những người dùng nào +quên cách họ vào ứng dụng của bạn sẽ điều hướng tới màn hình trên cùng của ứng dụng trước khi +thoát.

+ +

Ví dụ, widget màn hình Trang chủ của Gmail có một nút để đi trực tiếp vào màn hình +soạn email của nó. Lên hoặc Quay lại từ màn hình soạn thư sẽ đưa người dùng tới Hộp thư đến, và từ đó +nút Quay lại tiếp tục đưa về Trang chủ.

+ + + +

Thông báo gián tiếp

+ +

Khi ứng dụng của bạn cần trình bày thông tin về nhiều sự kiện đồng thời, nó có thể sử dụng +thông báo duy nhất để chuyển hướng người dùng tới một màn hình xen kẽ. Màn hình này tổng hợp những sự kiện +này và cung cấp đường dẫn cho người dùng đi sâu vào ứng dụng. Thông báo kiểu này được gọi +là thông báo gián tiếp.

+ +

Không như thông báo tiêu chuẩn (trực tiếp), nhấn Quay lại từ màn hình xen kẽ của một thông báo gián tiếp +sẽ trả người dùng về điểm mà từ đó thông báo đó được kích hoạt—không có +màn hình bổ sung nào được chèn vào ngăn xếp. Sau khi người dùng tiến vào ứng dụng từ màn hình xen kẽ +, thao tác Lên trên và Quay lại có tác dụng như với thông báo tiêu chuẩn, như được mô tả ở trên: +điều hướng bên trong ứng dụng thay vì quay lại màn hình xen kẽ.

+ +

Ví dụ, giả sử một người dùng trong Gmail nhận được một thông báo gián tiếp từ Lịch. Chạm +vào thông báo này để mở ra màn hình xen kẽ, trong đó hiển thị nhắc nhở cho một vài sự kiện +khác nhau. Chạm vào Quay lại từ màn hình xen kẽ sẽ trả người dùng về Gmail. Chạm vào một sự kiện +cụ thể sẽ đưa người dùng ra khỏi màn hình xen kẽ và vào ứng dụng Lịch để hiển thị chi tiết về +sự kiện. Từ chi tiết sự kiện, Lên trên và Quay lại sẽ điều hướng tới dạng xem trên cùng của Lịch.

+ + + +

Thông báo bật lên

+ +

Thông báo bật lên sẽ bỏ qua ngăn thông báo thay vì xuất hiện trực tiếp phía +trước người dùng. Chúng hiếm khi được sử dụng và nên được để dành cho những dịp khi cần +phản ứng kịp thời và việc gián đoạn ngữ cảnh của người dùng là cần thiết. Ví dụ, +Talk sử dụng kiểu này để cảnh báo người dùng về lời mời từ một người bạn để tham gia trò chuyện video, bởi +lời mời này sẽ tự động hết hạn sau một vài giây.

+ +

Xét về hành vi điều hướng, thông báo bật lên bám sát chặt chẽ hành vi của màn hình xen kẽ +trong thông báo gián tiếp. Quay lại sẽ bỏ qua thông báo bật lên. Nếu người dùng điều hướng +từ màn hình bật lên vào ứng dụng thông báo, hành vi Lên trên và Quay lại sẽ bám sát quy tắc áp dụng với thông báo tiêu chuẩn, +điều hướng bên trong ứng dụng.

+ + + +

Điều hướng giữa các Ứng dụng

+ +

Một trong những thế mạnh cơ bản của hệ thống Android đó là khả năng các ứng dụng kích hoạt +lẫn nhau, giúp người dùng có thể điều hướng trực tiếp từ ứng dụng này sang ứng dụng khác. Ví dụ, một ứng dụng +cần chụp ảnh có thể kích hoạt ứng dụng Camera, ứng dụng này sẽ trả +ảnh về ứng dụng chuyển đến. Điều này đặc biệt có ích đối với cả nhà phát triển, những người có thể dễ dàng tận dụng +đoạn mã từ các ứng dụng khác, lẫn người dùng, những người thích có một trải nghiệm nhất quán đối với những hành động +được thực hiện thường xuyên.

+ +

Để hiểu rõ điều hướng giữa các ứng dụng, quan trọng là phải hiểu được hành vi bộ khung Android +được đề cập dưới đây.

+ +

Hoạt động, tác vụ và ý định

+ +

Trong Android, hoạt động là một thành phần ứng dụng định nghĩa một màn hình +thông tin và tất cả hành động liên kết mà người dùng có thể thực hiện. Ứng dụng của bạn là tập hợp +của nhiều hoạt động, bao gồm cả hoạt động do bạn tạo và hoạt động mà bạn sử dụng lại từ các ứng dụng khác.

+ +

Tác vụ là trình tự các hoạt động mà một người dùng tuân theo để hoàn thành một mục tiêu. +Tác vụ đơn có thể sử dụng các hoạt động từ chỉ một ứng dụng, hoặc có thể dựa trên hoạt động từ nhiều +ứng dụng khác nhau.

+ +

Ý định là một cơ chế để một ứng dụng báo hiệu rằng nó muốn sự trợ giúp +của một ứng dụng khác khi thực hiện một hành động. Các hoạt động của một ứng dụng có thể biểu thị những ý định +mà chúng có thể phản hồi. Đối với những ý định thường gặp như "Chia sẻ", người dùng có thể cài đặt nhiều ứng dụng +có khả năng thực hiện yêu cầu đó.

+ +

Ví dụ: điều hướng giữa các ứng dụng để hỗ trợ chia sẻ

+ +

Để hiểu những hoạt động, tác vụ và ý định này vận hành với nhau như thế nào, hãy xem cách một ứng dụng cho phép người dùng +chia sẻ nội dung bằng cách dùng một ứng dụng khác. Ví dụ, khởi chạy ứng dụng Play Store từ Trang chủ sẽ bắt đầu +Tác vụ A mới (xem hình bên dưới). Sau khi điều hướng qua Play Store và chạm vào một cuốn sách được quảng cáo +để xem chi tiết của sách, người dùng vẫn ở trong cùng một tác vụ, mở rộng nó bằng cách thêm vào các hoạt động. Kích hoạt +hành động Chia sẻ sẽ nhắc người dùng bằng một hộp thoại liệt kê từng hoạt động một (từ các ứng dụng khác nhau) +mà đã đăng ký xử lý ý định Chia sẻ đó.

+ + + +

Khi người dùng chọn chia sẻ qua Gmail, hoạt động soạn email của Gmail sẽ được thêm làm hoạt động tiếp tục +Tác vụ A—không có tác vụ mới nào được tạo thêm. Nếu Gmail có tác vụ riêng đang chạy nền, nó sẽ +không bị ảnh hưởng.

+ +

Từ hoạt động soạn email, gửi thư hoặc chạm vào nút Quay lại sẽ trả người dùng về +hoạ̣t động chi tiết về cuốn sách. Những lần chạm sau đó vào Quay lại sẽ tiếp tục điều hướng quay lại qua +Store và cuối cùng về lại Trang chủ.

+ + + +

Tuy nhiên, nếu chạm vào Lên trên từ hoạt động soạn email, người dùng biểu thị mong muốn vẫn ở nguyên trong +Gmail. Hoạt động danh sách hội thoại của Gmail xuất hiện và Tác vụ B mới sẽ được tạo cho nó. Các tác vụ mới +sẽ luôn được liên kết gốc với Trang chủ, vì thế chạm vào Quay lại từ danh sách hội thoại sẽ quay lại đó.

+ + + +

Tác vụ A duy trì chạy nền và người dùng có thể quay lại nó sau (ví dụ, thông qua +màn hình Gần đây). Nếu Gmail đã có tác vụ riêng đang chạy nền, nó sẽ được thay thế +bằng Tác vụ B—ngữ cảnh trước đó sẽ bị bỏ đi phục vụ cho mục đích mới của người dùng.

+ +

Khi ứng dụng của bạn đăng ký điều khiển ý định bằng một hoạt động nằm sâu trong thứ bậc của ứng dụng, +hãy tham khảo phần Điều hướng tới Ứng dụng của Bạn thông qua Widget và Thông báo trên +Màn hình Trang chủ để xem hướng dẫn về cách quy định điều hướng Lên trên.

diff --git a/docs/html-intl/intl/vi/guide/components/activities.jd b/docs/html-intl/intl/vi/guide/components/activities.jd new file mode 100644 index 0000000000000000000000000000000000000000..83e7669a7f7676028c73e69744a67ca905a7e419 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Các hoạt động +page.tags=hoạt động,ý định +@jd:body + + + + + +

{@link android.app.Activity} là một thành phần ứng dụng cung cấp một màn hình mà với nó +người dùng có thể tương tác để thực hiện một điều gì đó, chẳng hạn như quay số điện thoại, chụp ảnh, gửi e-mail hoặc +xem bản đồ. Mỗi hoạt động được cho trong một cửa sổ là nơi để vẽ giao diện người dùng của nó. Cửa sổ này +thường lấp đầy màn hình, nhưng có thể nhỏ hơn màn hình và nổi bên trên các cửa sổ +khác.

+ +

Ứng dụng thường bao gồm nhiều hoạt động được liên kết lỏng lẻo +với nhau. Thường thì một hoạt động trong một ứng dụng sẽ được quy định là hoạt động "chính", nó được +trình bày trước người dùng khi khởi chạy ứng dụng lần đầu. Sau đó, mỗi +hoạt động có thể bắt đầu một hoạt động khác để thực hiện các hành động khác nhau. Mỗi khi một hoạt động +mới bắt đầu, hoạt động trước đó sẽ bị dừng lại, nhưng hệ thống vẫn giữ nguyên hoạt động +trong một ngăn xếp ("back stack"). Khi một hoạt động mới bắt đầu, nó được đẩy lên ngăn xếp và +chiếm lấy tiêu điểm của người dùng. Ngăn xếp sẽ tuân theo cơ chế xếp chồng cơ bản "vào cuối, ra đầu", +vì thế, khi người dùng kết thúc hoạt động hiện tại và nhấn nút Quay lại, nó +sẽ được đẩy ra khỏi ngăn xếp (và bị hủy) và hoạt động trước đó sẽ tiếp tục. (Ngăn xếp được +đề cập kỹ hơn trong tài liệu Tác vụ +và Ngăn Xếp.)

+ +

Khi một hoạt động bị dừng vì một hoạt động mới bắt đầu, nó được thông báo về sự thay đổi trạng thái này +qua các phương pháp gọi lại vòng đời của hoạt động. +Có một vài phương pháp gọi lại vòng đời mà một hoạt động có thể nhận, do một thay đổi về +trạng thái của nó—dù hệ thống đang tạo, dừng hay tiếp tục nó, hay hủy nó—và +mỗi lần gọi lại cho bạn cơ hội thực hiện công việc cụ thể +phù hợp với sự thay đổi trạng thái đó. Ví dụ, khi bị dừng, hoạt động của bạn sẽ giải phóng mọi +đối tượng lớn, chẳng hạn như các kết nối mạng hoặc cơ sở dữ liệu. Khi hoạt động tiếp tục, bạn có thể +thu lại những tài nguyên cần thiết và tiếp tục những hành động bị gián đoạn. Những chuyển tiếp trạng thái này +đều là một phần của vòng đời hoạt động.

+ +

Phần còn lại của tài liệu này bàn đến những nội dung cơ bản về cách xây dựng và sử dụng một hoạt động, +bao gồm một nội dung đề cập đầy đủ về cách vận hành của vòng đời hoạt động, để bạn có thể quản lý tốt +sự chuyển tiếp giữa các trạng thái hoạt động khác nhau.

+ + + +

Tạo một Hoạt động

+ +

Để tạo một hoạt động, bạn phải tạo một lớp con của {@link android.app.Activity} (hoặc +một lớp con hiện tại của nó). Trong lớp con của mình, bạn cần triển khai các phương pháp gọi lại mà hệ thống +gọi khi hoạt động chuyển tiếp giữa các trạng thái khác nhau trong vòng đời, chẳng hạn như khi +hoạt động đang được tạo, dừng, tiếp tục, hoặc hủy. Hai phương pháp gọi lại quan trọng nhất +là:

+ +
+
{@link android.app.Activity#onCreate onCreate()}
+
Bạn phải triển khai phương pháp này. Hệ thống gọi phương pháp này khi tạo hoạt động +của bạn. Trong quá trình thực hiện của mình, bạn nên khởi chạy những thành phần thiết yếu cho hoạt động +của mình. + Quan trọng nhất, đây là lúc bạn phải gọi {@link android.app.Activity#setContentView + setContentView()} để định nghĩa bố trí cho giao diện người dùng của hoạt động.
+
{@link android.app.Activity#onPause onPause()}
+
Hệ thống gọi phương pháp này là dấu hiệu đầu tiên về việc người dùng đang rời khỏi hoạt động +của bạn (mặc dù không phải lúc nào cũng có nghĩa rằng hoạt động đang bị hủy). Trường hợp này thường là khi bạn +định thực hiện bất kỳ thay đổi nào vẫn cần có hiệu lực ngoài phiên của người dùng hiện thời (vì +người dùng có thể không quay lại).
+
+ +

Có một vài phương pháp gọi lại vòng đời khác mà bạn nên sử dụng để đem đến +một trải nghiệm người dùng mượt mà giữa các hoạt động và xử lý những gián đoạn bất ngờ khiến hoạt động của bạn +bị dừng và thậm chí bị hủy. Tất cả phương pháp gọi lại vòng đời được bàn sau trong phần +nói về Quản lý Vòng đời của Hoạt động.

+ + + +

Triển khai một giao diện người dùng

+ +

Giao diện người dùng cho một hoạt động sẽ được cung cấp theo phân cấp dạng xem—đối tượng được suy ra +từ lớp {@link android.view.View}. Mỗi chế độ xem kiểm soát một không gian chữ nhật riêng +trong cửa sổ của hoạt động và có thể phản hồi trước tương tác của người dùng. Ví dụ, chế độ xem có thể là +một nút khởi xướng một hành động khi người dùng chạm vào nó.

+ +

Android cung cấp nhiều chế độ xem sẵn có mà bạn có thể sử dụng để thiết kế và tổ chức cho bố trí +của mình. "Widget" là những chế độ xem cung cấp những phần tử trực quan (và tương tác) cho màn hình, chẳng hạn như +nút, trường văn bản, hộp kiểm, hay chỉ là một hình ảnh. "Bố trí" là những chế độ xem được suy ra từ {@link +android.view.ViewGroup} cung cấp một mô hình bố trí duy nhất cho các chế độ xem con của nó, chẳng hạn như bố trí +tuyến tính, bố trí lưới, hoặc bố trí tương đối. Bạn cũng có thể chia thành lớp con {@link android.view.View} và các lớp +{@link android.view.ViewGroup} (hoặc các lớp con hiện tại) để tạo widget và +bố trí của chính mình và áp dụng chúng vào bố trí hoạt động của bạn.

+ +

Cách phổ biến nhất để định nghĩa một bố trí bằng cách sử dụng các chế độ xem là dùng một tệp bố trí XML được lưu trong tài nguyên ứng dụng +của bạn. Bằng cách này, bạn có thể duy trì thiết kế giao diện người dùng của mình độc lập với +mã nguồn định nghĩa hành vi của hoạt động. Bạn có thể đặt bố trí làm UI cho hoạt động +của mình bằng {@link android.app.Activity#setContentView(int) setContentView()}, chuyển +ID tài nguyên cho bố trí. Tuy nhiên, bạn cũng có thể tạo {@link android.view.View} mới trong mã hoạt động +của mình và xây dựng một cấp bậc chế độ xem bằng cách chèn các {@link +android.view.View} mới vào một {@link android.view.ViewGroup}, sau đó sử dụng bố trí đó bằng cách chuyển root +{@link android.view.ViewGroup} sang {@link android.app.Activity#setContentView(View) +setContentView()}.

+ +

Để biết thông tin về việc tạo một giao diện người dùng, hãy xem tài liệu Giao diện Người dùng.

+ + + +

Khai báo hoạt động trong bản kê khai

+ +

Bạn phải khai báo hoạt động của mình trong tệp bản kê khai để hoạt động +có thể truy cập được vào hệ thống. Để khai báo hoạt động của mình, hãy mở tệp bản kê khai của bạn và thêm một phần tử {@code <activity>} +làm con của phần tử {@code <application>} +. Ví dụ:

+ +
+<manifest ... >
+  <application ... >
+      <activity android:name=".ExampleActivity" />
+      ...
+  </application ... >
+  ...
+</manifest >
+
+ +

Có vài thuộc tính khác mà bạn có thể nêu trong phần tử này, để định nghĩa các thuộc tính +như nhãn cho hoạt động, biểu tượng cho hoạt động, hoặc chủ đề mô tả kiểu UI của +hoạt động. Thuộc tính {@code android:name} +là thuộc tính bắt buộc duy nhất—nó quy định tên lớp của hoạt động. Một khi +bạn phát hành ứng dụng của mình, bạn không nên thay đổi tên này, vì nếu bạn làm vậy, bạn có thể làm hỏng +một số tính năng, chẳng hạn như các lối tắt của ứng dụng (hãy đọc bài đăng trên blog, Những Điều +Không Thay Đổi Được).

+ +

Xem tài liệu tham khảo phần tử {@code <activity>} +để biết thêm thông tin về việc khai báo hoạt động của bạn trong bản kê khai.

+ + +

Sử dụng các bộ lọc ý định

+ +

Một phần tử {@code +<activity>} cũng có thể quy định các bộ lọc ý định khác nhau—bằng cách sử dụng phần tử {@code +<intent-filter>} —để khai báo cách thức mà các thành phần khác của ứng dụng có thể +kích hoạt nó.

+ +

Khi bạn tạo một ứng dụng mới bằng cách sử dụng các công cụ SDK của Android, hoạt động chương trình nhỏ +được tạo cho bạn sẽ tự động bao gồm một bộ lọc ý định khai báo hoạt động +phản hồi lại hành động "chính" và nên được đặt trong thể loại "trình khởi chạy". Bộ lọc ý định +trông như thế này:

+ +
+<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
+    <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+</activity>
+
+ +

Phần tử {@code +<action>} quy định rằng đây là điểm mục nhập "chính" đối với ứng dụng. Phần tử {@code +<category>} quy định rằng hoạt động này nên được liệt kê trong trình khởi chạy ứng dụng của hệ thống +(để cho phép người dùng khởi chạy hoạt động này).

+ +

Nếu bạn có ý định cho ứng dụng của mình được độc lập và không cho phép các ứng dụng khác +kích hoạt các hoạt động của nó, vậy bạn không cần bất kỳ bộ lọc ý định nào khác. Chỉ một hoạt động nên có +hành động "chính" và thể loại "trình khởi chạy" như trong ví dụ trước. Những hoạt động mà +bạn không muốn cung cấp sẵn cho các ứng dụng khác không nên có bộ lọc ý định và bạn có thể +tự mình bắt đầu chúng bằng cách sử dụng các ý định rõ ràng (như được đề cập trong phần sau).

+ +

Tuy nhiên, nếu bạn muốn hoạt động của mình phản hồi lại những ý định ngầm mà được chuyển giao từ +các ứng dụng khác (và chính bạn), thì bạn phải định nghĩa các bộ lọc ý định bổ sung cho hoạt động +của mình. Với mỗi loại ý định mà bạn muốn phản hồi, bạn phải nêu một {@code +<intent-filter>} bao gồm một phần tử +{@code +<action>} và, không bắt buộc, một phần tử {@code +<category>} và/hoặc một phần tử {@code +<data>}. Những phần tử này quy định loại ý định mà hoạt động của bạn có thể +phản hồi.

+ +

Để biết thêm thông tin về cách thức các hoạt động của bạn có thể phản hồi lại ý định, hãy xem tài liệu Ý định và Bộ lọc Ý định +.

+ + + +

Bắt đầu một Hoạt động

+ +

Bạn có thể bắt đầu một hoạt động khác bằng cách gọi {@link android.app.Activity#startActivity + startActivity()}, chuyển cho nó một {@link android.content.Intent} mà mô tả hoạt động bạn +muốn bắt đầu. Ý định này sẽ quy định hoặc hoạt động chính xác mà bạn muốn bắt đầu hoặc mô tả + loại hành động mà bạn muốn thực hiện (và hệ thống lựa chọn hoạt động phù hợp cho bạn, +thậm chí +có thể từ một ứng dụng khác). Một ý định cũng có thể mang theo lượng nhỏ dữ liệu sẽ được + sử dụng bởi hoạt động được bắt đầu.

+ +

Khi đang làm việc trong ứng dụng của chính mình, bạn thường sẽ cần khởi chạy một hoạt động đã biết. + Bạn có thể làm vậy bằng cách tạo một ý định trong đó quy định rõ hoạt động bạn muốn bắt đầu, +sử dụng tên lớp đó. Ví dụ, sau đây là cách một hoạt động bắt đầu một hoạt động khác có tên {@code +SignInActivity}:

+ +
+Intent intent = new Intent(this, SignInActivity.class);
+startActivity(intent);
+
+ +

Tuy nhiên, ứng dụng của bạn cũng có thể muốn thực hiện một số hành động, chẳng hạn như gửi một e-mail, tin nhắn + văn bản, hoặc cập nhật trạng thái, bằng cách sử dụng dữ liệu từ hoạt động của bạn. Trong trường hợp này, ứng dụng của bạn có thể + không có các hoạt động của chính nó để thực hiện những hành động đó, vì vậy, thay vào đó, bạn có thể tận dụng những hoạt động + được cung cấp bởi các ứng dụng khác trên thiết bị mà có thể thực hiện hành động cho bạn. Đây là lúc +ý định thực sự có giá trị—bạn có thể tạo một ý định mô tả một hành động bạn muốn +thực hiện và hệ thống + sẽ khởi chạy hoạt động phù hợp đó từ một ứng dụng khác. Nếu có + nhiều hoạt động mà có thể xử lý ý định, vậy người dùng có thể chọn hoạt động nào sẽ sử dụng. Ví + dụ, nếu bạn muốn cho phép người dùng gửi e-mail, bạn có thể tạo + ý định sau:

+ +
+Intent intent = new Intent(Intent.ACTION_SEND);
+intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
+startActivity(intent);
+
+ +

{@link android.content.Intent#EXTRA_EMAIL} phụ được thêm vào ý định là một mảng xâu của + các địa chỉ e-mail mà e-mail sẽ được gửi tới. Khi một ứng dụng e-mail phản hồi + ý định này, nó đọc mảng xâu được cung cấp trong phần phụ và đặt nó vào trường "đến" của mẫu soạn thảo + e-mail. Trong trường hợp này, hoạt động của ứng dụng e-mail bắt đầu và khi người dùng + làm xong, hoạt động của bạn sẽ tiếp tục.

+ + + + +

Bắt đầu một hoạt động cho một kết quả

+ +

Đôi khi bạn có thể muốn nhận được một kết quả từ hoạt động mà bạn bắt đầu. Trong trường hợp đó, +hãy bắt đầu hoạt động bằng cách gọi {@link android.app.Activity#startActivityForResult + startActivityForResult()} (thay vì {@link android.app.Activity#startActivity + startActivity()}). Rồi để nhận được kết quả từ hoạt động +sau đó, hãy triển khai phương pháp gọi lại {@link android.app.Activity#onActivityResult onActivityResult()} +. Khi hoạt động sau đó diễn ra xong, nó trả về một kết quả trong một {@link +android.content.Intent} cho phương pháp {@link android.app.Activity#onActivityResult onActivityResult()} +của bạn.

+ +

Ví dụ, bạn có thể muốn người dùng chọn một trong các liên lạc của họ, vì vậy hoạt động của bạn có thể +làm gì đó với thông tin trong liên lạc đó. Đây là cách bạn có thể tạo một ý định như vậy và +xử lý kết quả:

+ +
+private void pickContact() {
+    // Create an intent to "pick" a contact, as defined by the content provider URI
+    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+    startActivityForResult(intent, PICK_CONTACT_REQUEST);
+}
+
+@Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
+    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
+        // Perform a query to the contact's content provider for the contact's name
+        Cursor cursor = getContentResolver().query(data.getData(),
+        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
+        if (cursor.moveToFirst()) { // True if the cursor is not empty
+            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
+            String name = cursor.getString(columnIndex);
+            // Do something with the selected contact's name...
+        }
+    }
+}
+
+ +

Ví dụ này thể hiện lô-gic cơ bản mà bạn sẽ sử dụng trong phương pháp {@link +android.app.Activity#onActivityResult onActivityResult()} của mình để xử lý một +kết quả hoạt động. Điều kiện đầu tiên kiểm tra xem yêu cầu có thành công không—nếu có thì +{@code resultCode} sẽ là {@link android.app.Activity#RESULT_OK}—và liệu yêu cầu +mà kiểm tra này đang phản hồi có được biết hay không—trong trường hợp này, {@code requestCode} phù hợp với +tham số thứ hai được gửi bằng {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Từ đó, mã xử lý kết quả hoạt động bằng cách truy vấn +dữ liệu được trả về trong {@link android.content.Intent} (tham số {@code data}).

+ +

Điều xảy ra đó là, {@link +android.content.ContentResolver} sẽ thực hiện một truy vấn đối với nhà cung cấp nội dung, truy vấn này trả về một +{@link android.database.Cursor} cho phép đọc dữ liệu được truy vấn. Để biết thêm thông tin, hãy xem tài liệu +Trình cung cấp Nội dung.

+ +

Để biết thêm thông tin về việc sử dụng ý định, hãy xem tài liệu Ý định và Bộ lọc +Ý định.

+ + +

Tắt một Hoạt động

+ +

Bạn có thể tắt một hoạt động bằng cách gọi phương pháp {@link android.app.Activity#finish +finish()} của nó. Bạn cũng có thể tắt một hoạt động riêng mà trước đó bạn đã bắt đầu bằng cách gọi +{@link android.app.Activity#finishActivity finishActivity()}.

+ +

Lưu ý: Trong hầu hết trường hợp, bạn không nên kết thúc một hoạt động một cách rõ ràng +bằng cách sử dụng những phương pháp này. Như đề cập trong phần sau về vòng đời của hoạt động, hệ thống +Android quản lý tuổi thọ của một hoạt động cho bạn, vì vậy bạn không cần kết thúc các hoạt động +của chính mình. Việc gọi những phương pháp này có thể ảnh hưởng tiêu cực tới trải nghiệm người dùng +kỳ vọng và chỉ nên được sử dụng khi bạn tuyệt đối không muốn người dùng quay lại thực thể này của +hoạt động.

+ + +

Quản lý Vòng đời của Hoạt động

+ +

Việc quản lý vòng đời các hoạt động của bạn bằng cách triển khai các phương pháp gọi lại +rất quan trọng đối với việc xây dựng một ứng dụng mạnh +và linh hoạt. Vòng đời của một hoạt động trực tiếp bị ảnh hưởng bởi sự liên kết giữa nó với +các hoạt động khác, tác vụ của nó và ngăn xếp (back stack).

+ +

Về cơ bản, một hoạt động có thể tồn tại ở ba trạng thái:

+ +
+
Tiếp tục
+
Hoạt động ở tiền cảnh của màn hình và có tiêu điểm của người dùng. (Trạng thái này +đôi khi cũng được gọi là "đang chạy".)
+ +
Tạm dừng
+
Một hoạt động khác ở tiền cảnh và có tiêu điểm, nhưng hoạt động này vẫn hiển thị. Cụ thể, +một hoạt động khác hiển thị ở trên hoạt động này và hoạt động đó trong suốt một phần hoặc không +che toàn bộ màn hình. Trạng thái tạm dừng hoàn toàn đang hoạt động (đối tượng {@link android.app.Activity} +được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, và vẫn gắn với +trình quản lý cửa sổ), nhưng có thể bị hệ thống tắt bỏ trong trường hợp bộ nhớ cực kỳ thấp.
+ +
Dừng
+
Hoạt động bị che khuất hoàn toàn bởi một hoạt động khác (hoạt động hiện đang +“dưới nền"). Hoạt động dừng cũng vẫn đang hoạt động ({@link android.app.Activity} +đối tượng được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, nhưng không +gắn với trình quản lý cửa sổ). Tuy nhiên, hoạt động không còn hiển thị với người dùng nữa và hệ thống +có thể tắt bỏ hoạt động này khi cần bộ nhớ ở nơi khác.
+
+ +

Nếu một hoạt động bị tạm dừng hoặc dừng, hệ thống có thể bỏ nó khỏi bộ nhớ hoặc bằng cách yêu cầu nó +kết thúc (gọi phương pháp {@link android.app.Activity#finish finish()} của nó), hoặc đơn giản là tắt bỏ tiến trình +của hoạt động. Khi hoạt động được mở lại (sau khi bị kết thúc hoặc tắt bỏ), nó phải được tạo +lại hoàn toàn.

+ + + +

Triển khai gọi lại vòng đời

+ +

Khi một hoạt động chuyển tiếp vào ra các trạng thái khác nhau nêu trên, nó được thông báo +thông qua các phương pháp gọi lại. Tất cả phương pháp gọi lại đều là những móc (hook) mà bạn +có thể khống chế để làm công việc phù hợp khi trạng thái hoạt động của bạn thay đổi. Hoạt động khung sau +bao gồm từng phương pháp trong các phương pháp vòng đời cơ bản:

+ + +
+public class ExampleActivity extends Activity {
+    @Override
+    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // The activity is being created.
+    }
+    @Override
+    protected void {@link android.app.Activity#onStart onStart()} {
+        super.onStart();
+        // The activity is about to become visible.
+    }
+    @Override
+    protected void {@link android.app.Activity#onResume onResume()} {
+        super.onResume();
+        // The activity has become visible (it is now "resumed").
+    }
+    @Override
+    protected void {@link android.app.Activity#onPause onPause()} {
+        super.onPause();
+        // Another activity is taking focus (this activity is about to be "paused").
+    }
+    @Override
+    protected void {@link android.app.Activity#onStop onStop()} {
+        super.onStop();
+        // The activity is no longer visible (it is now "stopped")
+    }
+    @Override
+    protected void {@link android.app.Activity#onDestroy onDestroy()} {
+        super.onDestroy();
+        // The activity is about to be destroyed.
+    }
+}
+
+ +

Lưu ý: Việc bạn triển khai những phương pháp vòng đời này phải luôn +gọi triển khai siêu lớp trước khi làm bất kỳ công việc nào, như minh họa trong các ví dụ bên trên.

+ +

Cùng nhau, những phương pháp này định nghĩa toàn bộ vòng đời của một hoạt động. Bằng việc triển khai những phương pháp +này, bạn có thể theo dõi ba vòng lặp lồng nhau trong vòng đời của hoạt động:

+ +
    +
  • Toàn bộ vòng đời của một hoạt động sẽ xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onCreate onCreate()} cho tới thời điểm lệnh gọi đến {@link +android.app.Activity#onDestroy}. Hoạt động của bạn nên thực hiện thiết lập +trạng thái "chung" (chẳng hạn như định nghĩa bố trí) trong {@link android.app.Activity#onCreate onCreate()}, và +giải phóng tất cả tài nguyên còn lại trong {@link android.app.Activity#onDestroy}. Ví dụ, nếu hoạt động của bạn +có một luồng đang chạy ngầm để tải xuống dữ liệu từ mạng, nó có thể tạo +luồng đó trong {@link android.app.Activity#onCreate onCreate()} rồi dừng luồng trong {@link +android.app.Activity#onDestroy}.
  • + +
  • Vòng đời hiển thị của một hoạt động xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onStart onStart()} cho tới lệnh gọi đến {@link +android.app.Activity#onStop onStop()}. Trong thời gian này, người dùng có thể thấy hoạt động +trên màn hình và tương tác với nó. Ví dụ, {@link android.app.Activity#onStop onStop()} được gọi +khi một hoạt động mới bắt đầu và không còn hiển thị nữa. Giữa hai phương pháp này, bạn có thể +duy trì các tài nguyên cần để cho người dùng thấy hoạt động. Ví dụ, bạn có thể đăng ký một +{@link android.content.BroadcastReceiver} trong {@link +android.app.Activity#onStart onStart()} để theo dõi các thay đổi tác động tới UI của mình, và bỏ đăng ký +nó trong {@link android.app.Activity#onStop onStop()} khi người dùng không còn thấy thứ bạn đang +hiển thị nữa. Hệ thống có thể gọi {@link android.app.Activity#onStart onStart()} và {@link +android.app.Activity#onStop onStop()} nhiều lần trong suốt vòng đời của hoạt động, khi đó +hoạt động luân chuyển giữa trạng thái hiển thị và ẩn với người dùng.

  • + +
  • Vòng đời ở tiền cảnh của một hoạt động xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onResume onResume()} cho tới thời điểm lệnh gọi đến {@link android.app.Activity#onPause +onPause()}. Trong thời gian này, hoạt động sẽ ở phía trước tất cả hoạt động khác trên màn hình và có +tiêu điểm đầu vào của người dùng. Hoạt động có thể thường xuyên chuyển tiếp vào và ra tiền cảnh—ví +dụ, {@link android.app.Activity#onPause onPause()} được gọi khi thiết bị vào trạng thái ngủ hoặc +khi một hộp thoại xuất hiện. Vì trạng thái này có thể chuyển tiếp thường xuyên, mã trong hai phương pháp này nên +tương đối nhẹ để tránh chuyển tiếp chậm khiến người dùng phải đợi.

  • +
+ +

Hình 1 minh họa những vòng lặp này và các đường dẫn mà một hoạt động có thể diễn ra giữa các trạng thái. +Hình chữ nhật đại diện cho các phương pháp gọi lại bạn có thể triển khai để thực hiện thao tác khi +hoạt động chuyển tiếp giữa những trạng thái này.

+ + +

Hình 1. Vòng đời của hoạt động.

+ +

Những phương pháp gọi lại vòng đời này cũng được liệt kê trong bảng 1, trong đó mô tả từng phương pháp +gọi lại một cách chi tiết hơn và xác định từng phương pháp +trong vòng đời tổng thể của hoạt động, bao gồm việc hệ thống có thể tắt bỏ hoạt động hay không sau khi +phương pháp gọi lại hoàn tất.

+ +

Bảng 1. Tóm tắt các phương pháp gọi lại +trong vòng đời của hoạt động.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Phương pháp Mô tả Có thể tắt bỏ sau? Tiếp theo
{@link android.app.Activity#onCreate onCreate()}Được gọi khi hoạt động mới được tạo. + Đây là lúc bạn nên thực hiện tất cả thiết lập cố định thông thường của mình — + tạo chế độ xem, kết ghép dữ liệu với danh sách, v.v. Phương pháp này được chuyển cho + một đối tượng Gói chứa trạng thái trước đây của hoạt động, nếu trạng thái + đó được thu lại (xem phần Lưu Trạng thái Hoạt động, + ở đoạn sau). +

Luôn được theo sau bởi {@code onStart()}.

Không{@code onStart()}
    {@link android.app.Activity#onRestart +onRestart()}Được gọi sau khi hoạt động đã được dừng, ngay trước khi hoạt động được + bắt đầu lại. +

Luôn được theo sau bởi {@code onStart()}

Không{@code onStart()}
{@link android.app.Activity#onStart onStart()}Được gọi ngay trước khi hoạt động hiển thị trước người dùng. +

Được theo sau bởi {@code onResume()} nếu hoạt động vào + tiền cảnh, hoặc {@code onStop()} nếu hoạt động bị ẩn.

Không{@code onResume()}
hoặc
{@code onStop()}
    {@link android.app.Activity#onResume onResume()}Được gọi ngay trước khi hoạt động bắt đầu + tương tác với người dùng. Tại điểm này, hoạt động nằm ở + trên cùng của chồng hoạt động, trong đó mục nhập của người dùng sẽ đến hoạt động này. +

Luôn được theo sau bởi {@code onPause()}.

Không{@code onPause()}
{@link android.app.Activity#onPause onPause()}Được gọi khi hệ thống sắp bắt đầu tiếp tục một hoạt động + khác. Phương pháp này thường được sử dụng để thực hiện các thay đổi chưa lưu cho + dữ liệu liên tục, dừng các hoạt ảnh và những việc khác mà có thể tiêu tốn công suất + CPU, v.v. Nó sẽ thực hiện rất nhanh, vì + hoạt động tiếp theo sẽ không được tiếp tục tới khi nó trở lại. +

Được theo sau hoặc bởi {@code onResume()} nếu hoạt động + trở lại phía trước, hoặc bởi {@code onStop()} nếu nó + không hiển thị với người dùng.

{@code onResume()}
hoặc
{@code onStop()}
{@link android.app.Activity#onStop onStop()}Được gọi khi hoạt động không còn hiển thị với người dùng. Điều này + có thể xảy ra vì nó đang bị hủy, hoặc vì một hoạt động khác + (đang tồn tại hoặc mới) đã được tiếp tục và đang che khuất nó. +

Được theo sau hoặc bởi {@code onRestart()} nếu + hoạt động đang quay lại để tương tác với người dùng, hoặc bởi + {@code onDestroy()} nếu hoạt động này sẽ đi mất.

{@code onRestart()}
hoặc
{@code onDestroy()}
{@link android.app.Activity#onDestroy +onDestroy()}Được gọi trước khi hoạt động bị hủy. Đây là lần gọi cuối cùng + mà hoạt động sẽ nhận được. Nên gọi nó hoặc vì + hoạt động đang kết thúc (ai đó đã gọi {@link android.app.Activity#finish + finish()} trên nó), hoặc vì hệ thống đang tạm thời hủy thực thể này của + hoạt động để tiết kiệm bộ nhớ trống. Bạn có thể phân biệt + những những kịch bản này bằng phương pháp {@link + android.app.Activity#isFinishing isFinishing()}.không có gì
+ +

Cột ghi "Có thể tắt bỏ sau?" cho biết liệu hệ thống có thể +tắt bỏ tiến trình đang lưu trữ hoạt động vào bất cứ lúc nào sau khi phương pháp trả về, mà không +thực hiện một dòng mã khác của hoạt động hay không. Ba phương pháp được ghi là "có": ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()}, và {@link android.app.Activity#onDestroy +onDestroy()}). Vì {@link android.app.Activity#onPause onPause()} là phương pháp đầu tiên +trong ba phương pháp, sau khi hoạt động được tạo, {@link android.app.Activity#onPause onPause()} là +phương pháp cuối cùng được bảo đảm sẽ được gọi trước khi tiến trình có thể bị tắt bỏ—nếu +hệ thống phải khôi phục bộ nhớ trong một tình huống khẩn cấp, khi đó {@link +android.app.Activity#onStop onStop()} và {@link android.app.Activity#onDestroy onDestroy()} có thể +không được gọi. Vì thế, bạn nên sử dụng {@link android.app.Activity#onPause onPause()} để ghi +dữ liệu cố định quan trọng (chẳng hạn như những chỉnh sửa của người dùng) vào thiết bị lưu trữ. Tuy nhiên, bạn nên chọn lọc +thông tin nào phải được giữ lại trong {@link android.app.Activity#onPause onPause()}, vì bất kỳ +thủ tục chặn nào trong phương pháp này cũng chặn chuyển tiếp sang hoạt động kế tiếp và làm chậm trải nghiệm +của người dùng.

+ +

Những phương pháp được ghi "Không" trong cột Có thể tắt bỏ sẽ bảo vệ tiến trình đang lưu trữ +hoạt động khỏi bị tắt bỏ từ thời điểm chúng được gọi. Vì thế, một hoạt động có thể tắt bỏ được +từ thời điểm {@link android.app.Activity#onPause onPause()} trở về tới thời điểm +{@link android.app.Activity#onResume onResume()} sẽ được gọi. Nó sẽ không thể lại tắt bỏ được tới khi +{@link android.app.Activity#onPause onPause()} lại được gọi và trả về.

+ +

Lưu ý: Một hoạt động mà không thể "tắt bỏ được" về mặt kỹ thuật bởi +định nghĩa này trong bảng 1 vẫn có thể bị hệ thống tắt bỏ—nhưng điều đó chỉ xảy ra trong +những hoàn cảnh cực đoan khi không còn giải pháp nào khác. Thời điểm một hoạt động có thể bị tắt bỏ được +đề cập kỹ hơn trong tài liệu Tiến trình và +Luồng.

+ + +

Lưu trạng thái của hoạt động

+ +

Phần giới thiệu về Quản lý Vòng đời của Hoạt động có đề cập sơ qua +rằng +khi một hoạt động bị tạm dừng hoặc dừng, trạng thái của hoạt động đó sẽ được giữ lại. Điều này đúng vì +đối tượng {@link android.app.Activity} vẫn được giữ trong bộ nhớ khi nó bị tạm dừng hoặc +dừng—tất cả thông tin về các thành viên và trạng thái hiện tại của nó vẫn hoạt động. Vì thế, bất kỳ thay đổi nào +mà người dùng đã thực hiện trong hoạt động đều được giữ lại sao cho khi hoạt động trở về +tiền cảnh (khi nó "tiếp tục"), thì những thay đổi này vẫn còn đó.

+ +

Tuy nhiên, khi hệ thống hủy một hoạt động để khôi phục bộ nhớ, đối tượng {@link +android.app.Activity} bị hủy, vì vậy hệ thống không thể đơn thuần tiếp tục hoạt động với trạng thái +không bị ảnh hưởng. Thay vào đó, hệ thống phải tạo lại đối tượng {@link android.app.Activity} nếu người dùng +điều hướng trở lại nó. Tuy vậy, người dùng không biết +rằng hệ thống đã hủy hoạt động và tạo lại nó và, vì thế, có thể +cho rằng hoạt động sẽ vẫn nguyên như cũ. Trong tình huống này, bạn có thể đảm bảo rằng +thông tin quan trọng về trạng thái của hoạt động được giữ nguyên bằng cách triển khai một phương pháp gọi lại +bổ sung cho phép bạn lưu thông tin về trạng thái của hoạt động của mình: {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}.

+ +

Hệ thống gọi {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +trước khi khiến hoạt động dễ bị hủy. Hệ thống chuyển cho phương pháp này +một {@link android.os.Bundle} trong đó bạn có thể lưu +thông tin trạng thái về hoạt động như cặp tên giá trị, bằng cách sử dụng các phương pháp như {@link +android.os.Bundle#putString putString()} và {@link +android.os.Bundle#putInt putInt()}. Sau đó, nếu hệ thống tắt bỏ tiến trình ứng dụng của bạn +và người dùng điều hướng trở lại hoạt động của bạn, hệ thống sẽ tạo lại hoạt động đó và +chuyển {@link android.os.Bundle} cho cả {@link android.app.Activity#onCreate onCreate()} và {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}. Sử dụng một trong +hai phương pháp này, bạn có thể trích xuất trạng thái đã lưu của mình từ {@link android.os.Bundle} và khôi phục +trạng thái của hoạt động. Nếu không có thông tin trạng thái để khôi phục, khi đó {@link +android.os.Bundle} được chuyển cho bạn sẽ rỗng (là trường hợp khi hoạt động được tạo +lần đầu).

+ + +

Hình 2. Hai cách mà theo đó một hoạt động trở về tiêu điểm +của người dùng với trạng thái không thay đổi: hoặc hoạt động bị hủy, rồi tạo lại và hoạt động phải khôi phục +trạng thái đã lưu trước đó, hoặc hoạt động bị dừng, rồi tiếp tục và trạng thái của hoạt động +giữ nguyên không đổi.

+ +

Lưu ý: Không có gì bảo đảm rằng {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} sẽ được gọi trước khi hoạt động +của bạn bị hủy, vì có những trường hợp mà sẽ không cần lưu trạng thái +(chẳng hạn như khi người dùng rời bỏ hoạt động của bạn bằng cách sử dụng nút Quay lại, vì người dùng +rõ ràng +đang đóng hoạt động). Nếu hệ thống gọi {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, nó làm vậy trước {@link +android.app.Activity#onStop onStop()} và có thể trước cả {@link android.app.Activity#onPause +onPause()}.

+ +

Tuy nhiên, ngay cả khi bạn không làm gì và không triển khai {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, một phần trạng thái của hoạt động được khôi phục +bởi việc lớp {@link android.app.Activity} triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Cụ thể, triển khai +mặc định sẽ gọi phương pháp {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} tương ứng cho mọi {@link +android.view.View} trong bố trí, nó cho phép mỗi chế độ xem cung cấp thông tin về chính nó +mà sẽ được lưu. Gần như mọi widget trong khuôn khổ Android đều triển khai phương pháp này nếu +phù hợp, sao cho mọi thay đổi hiển thị đối với UI đều tự động được lưu và khôi phục khi hoạt động +của bạn được tạo lại. Ví dụ, widget {@link android.widget.EditText} lưu mọi văn bản +do người dùng điền vào và widget {@link android.widget.CheckBox} lưu sẽ thông tin cho dù đã được kiểm tra +hay chưa. Việc duy nhất bạn cần làm đó là cung cấp một ID duy nhất (với thuộc tính {@code android:id} +) cho mỗi widget bạn muốn lưu trạng thái của nó. Nếu một widget không có ID thì hệ thống +không thể lưu trạng thái của nó.

+ + + +

Mặc dù việc triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} lưu thông tin hữu ích về +UI hoạt động của bạn, bạn có thể vẫn cần khống chế nó để lưu thêm thông tin. +Ví dụ, bạn có thể cần lưu các giá trị thành viên đã thay đổi trong vòng đời của hoạt động (mà +có thể tương quan với các giá trị được khôi phục trong UI, nhưng các thành viên nắm giữ giá trị UI đó không được +khôi phục theo mặc định).

+ +

Vì việc triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} giúp lưu trạng thái của UI, nếu +bạn khống chế phương pháp để lưu thêm thông tin trạng thái, bạn nên luôn luôn gọi +triển khai siêu lớp của {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +trước khi thực hiện bất kỳ công việc nào. Tương tự, bạn cũng nên gọi triển khai siêu lớp {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} nếu bạn khống chế nó, để +triển khai mặc định có thể khôi phục các trạng thái xem.

+ +

Lưu ý: Vì {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} không đảm bảo +sẽ được gọi, bạn chỉ nên sử dụng nó để ghi trạng thái giao thời của hoạt động (trạng thái của +UI)—bạn không nên sử dụng nó để lưu giữ dữ liệu liên tục. Thay vào đó, bạn nên sử dụng {@link +android.app.Activity#onPause onPause()} để lưu giữ dữ liệu liên tục (chẳng hạn như dữ liệu mà nên được lưu +vào một cơ sở dữ liệu) khi người dùng rời bỏ hoạt động.

+ +

Một cách hay để kiểm tra khả năng khôi phục trạng thái của ứng dụng của bạn đó là chỉ cần xoay +thiết bị sao cho hướng màn hình thay đổi. Khi hướng màn hình thay đổi, hệ thống +hủy và tạo lại hoạt động để áp dụng các tài nguyên thay thế mà có thể có sẵn +cho cấu hình màn hình mới. Chỉ với lý do này mà một điều rất quan trọng đó là hoạt động của bạn +hoàn toàn khôi phục trạng thái của mình khi nó được tạo lại, vì người dùng thường xoay màn hình trong khi +sử dụng ứng dụng.

+ + +

Xử lý thay đổi về cấu hình

+ +

Một số cấu hình thiết bị có thể thay đổi trong thời gian chạy (chẳng hạn như hướng màn hình, sự sẵn có +của bàn phím, và ngôn ngữ). Khi sự thay đổi đó diễn ra, Android tạo lại hoạt động đang chạy +(hệ thống gọi {@link android.app.Activity#onDestroy}, rồi ngay lập tức gọi {@link +android.app.Activity#onCreate onCreate()}). Hành vi này +được thiết kế để giúp ứng dụng của bạn điều chỉnh theo những cấu hình mới bằng cách tự động tải lại ứng dụng +của bạn bằng các tài nguyên thay thế mà bạn đã cung cấp (chẳng hạn như bố trí khác cho +các hướng và kích cỡ màn hình khác).

+ +

Nếu bạn thiết kế hoạt động của mình một cách phù hợp để xử lý khởi động lại do thay đổi hướng màn hình và +khôi phục trạng thái hoạt động như nêu trên, ứng dụng của bạn sẽ linh hoạt hơn trước +những sự kiện bất ngờ khác trong vòng đời của hoạt động.

+ +

Cách tốt nhất để xử lý khởi động lại đó là + lưu và khôi phục trạng thái hoạt động của bạn bằng cách sử dụng {@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()} và {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (hoặc {@link +android.app.Activity#onCreate onCreate()}), như đã đề cập trong phần trước.

+ +

Để biết thêm thông tin về những thay đổi cấu hình xảy ra tại thời điểm chạy và cách bạn có thể xử lý +chúng, hãy đọc hướng dẫn Xử lý +Thay đổi trong Thời gian chạy.

+ + + +

Điều phối hoạt động

+ +

Khi một hoạt động bắt đầu một hoạt động khác, cả hai đều trải qua những chuyển tiếp vòng đời. Hoạt động thứ nhất +tạm dừng và dừng (tuy nhiên, nó sẽ không dừng nếu vẫn hiển thị được dưới nền), trong khi hoạt động kia +được tạo. Trong trường hợp những hoạt động này chia sẻ dữ liệu được lưu vào đĩa hoặc nơi khác, điều quan trọng là +phải hiểu rằng hoạt động thứ nhất không bị dừng hoàn toàn trước khi hoạt động thứ hai được tạo. +Thay vào đó, tiến trình bắt đầu hoạt động thứ hai chồng lấp với tiến trình dừng hoạt động +thứ nhất.

+ +

Thứ tự gọi lại vòng đời được định nghĩa rõ, cụ thể là khi hai hoạt động trong cùng tiến trình +và hoạt động này bắt đầu hoạt động kia. Sau đây là thứ tự thao tác diễn ra khi Hoạt động +A bắt đầu Hoạt động B:

+ +
    +
  1. Phương pháp {@link android.app.Activity#onPause onPause()} của Hoạt động A thực thi.
  2. + +
  3. {@link android.app.Activity#onCreate onCreate()} của Hoạt động B, {@link +android.app.Activity#onStart onStart()}, và các phương pháp {@link android.app.Activity#onResume onResume()} +thực thi theo trình tự. (Hoạt động B lúc này có tiêu điểm của người dùng.)
  4. + +
  5. Sau đó, nếu Hoạt động A không còn hiển thị trên màn hình, phương pháp {@link +android.app.Activity#onStop onStop()} của nó sẽ thực thi.
  6. +
+ +

Trình tự gọi lại vòng đời có thể dự đoán này cho phép bạn quản lý chuyển tiếp +thông tin từ hoạt động này sang hoạt động khác. Ví dụ, nếu bạn phải ghi vào một cơ sở dữ liệu khi +hoạt động thứ nhất dừng sao cho hoạt động theo sau có thể đọc nó, khi đó bạn nên ghi vào +cơ sở dữ liệu trong khi {@link android.app.Activity#onPause onPause()} thay vì trong khi {@link +android.app.Activity#onStop onStop()}.

+ + diff --git a/docs/html-intl/intl/vi/guide/components/bound-services.jd b/docs/html-intl/intl/vi/guide/components/bound-services.jd new file mode 100644 index 0000000000000000000000000000000000000000..7a2ddbaf6321e02d0d9db6d39674685f581f80ac --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=Dịch vụ Gắn kết +parent.title=Dịch vụ +parent.link=services.html +@jd:body + + +
+
    +

    Trong tài liệu này

    +
      +
    1. Nội dung Cơ bản
    2. +
    3. Tạo một Dịch vụ Gắn kết +
        +
      1. Mở rộng lớp Trình gắn kết
      2. +
      3. Sử dụng một Hàm nhắn tin
      4. +
      +
    4. +
    5. Gắn kết với một Dịch vụ
    6. +
    7. Quản lý Vòng đời của một Dịch vụ Gắn kết
    8. +
    + +

    Lớp khóa

    +
      +
    1. {@link android.app.Service}
    2. +
    3. {@link android.content.ServiceConnection}
    4. +
    5. {@link android.os.IBinder}
    6. +
    + +

    Mẫu

    +
      +
    1. {@code + RemoteService}
    2. +
    3. {@code + LocalService}
    4. +
    + +

    Xem thêm

    +
      +
    1. Dịch vụ
    2. +
    +
+ + +

Dịch vụ gắn kết là máy chủ trong một giao diện máy khách-máy chủ. Dịch vụ gắn kết cho phép các thành phần +(chẳng hạn như các hoạt động) gắn kết với dịch vụ, gửi yêu cầu, nhận phản hồi, và thậm chí thực hiện +truyền thông liên tiến trình (IPC). Dịch vụ gắn kết thường chỉ hoạt động khi nó phục vụ một thành phần +ứng dụng khác và không chạy ngầm mãi liên tục.

+ +

Tài liệu này cho bạn biết cách tạo một dịch vụ gắn kết, bao gồm cách gắn kết +với dịch vụ từ các thành phần ứng dụng khác. Tuy nhiên, bạn cũng nên tham khảo tài liệu Dịch vụ để biết thêm thông tin +về các dịch vụ nói chung, chẳng hạn như cách gửi thông báo từ một dịch vụ, đặt +dịch vụ để chạy trong tiền cảnh, và nhiều nội dung khác.

+ + +

Nội dung Cơ bản

+ +

Dịch vụ gắn kết là một sự triển khai lớp {@link android.app.Service} cho phép +các ứng dụng khác gắn kết và tương tác với nó. Để thực hiện gắn kết cho một +dịch vụ, bạn phải triển khai phương pháp gọi lại {@link android.app.Service#onBind onBind()}. Phương pháp này +trả về một đối tượng {@link android.os.IBinder} định nghĩa giao diện lập trình mà +các máy khách có thể sử dụng để tương tác với dịch vụ.

+ + + +

Một máy khách có thể gắn kết với dịch vụ bằng cách gọi {@link android.content.Context#bindService +bindService()}. Khi làm vậy, nó phải cung cấp việc triển khai {@link +android.content.ServiceConnection}, có chức năng theo dõi kết nối với dịch vụ. Phương pháp {@link +android.content.Context#bindService bindService()} trả về ngay lập tức mà không có giá trị, nhưng +khi hệ thống Android tạo kết nối giữa +máy khách và dịch vụ, nó gọi {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} trên {@link +android.content.ServiceConnection}, để giao {@link android.os.IBinder} mà +máy khách có thể sử dụng để giao tiếp với dịch vụ.

+ +

Nhiều máy khách có thể kết nối với dịch vụ đồng thời. Tuy nhiên, hệ thống sẽ gọi phương pháp +{@link android.app.Service#onBind onBind()} của dịch vụ của bạn để truy xuất {@link android.os.IBinder} chỉ +khi máy khách đầu tiên gắn kết. Sau đó, hệ thống sẽ giao cùng một {@link android.os.IBinder} đó cho bất kỳ +máy khách bổ sung nào có gắn kết mà không gọi lại {@link android.app.Service#onBind onBind()}.

+ +

Khi máy khách cuối cùng bỏ gắn kết với dịch vụ, hệ thống sẽ hủy dịch vụ (trừ khi dịch vụ +cũng được bắt đầu bởi {@link android.content.Context#startService startService()}).

+ +

Khi bạn triển khai dịch vụ gắn kết của mình, phần quan trọng nhất là định nghĩa giao diện +mà phương pháp gọi lại {@link android.app.Service#onBind onBind()} của bạn sẽ trả về. Có một vài +cách khác nhau mà bạn có thể định nghĩa giao diện {@link android.os.IBinder} của dịch vụ của mình và phần +sau đây sẽ bàn về từng kỹ thuật.

+ + + +

Tạo một Dịch vụ Gắn kết

+ +

Khi tạo một dịch vụ thực hiện gắn kết, bạn phải nêu một {@link android.os.IBinder} +cung cấp giao diện lập trình mà các máy khách có thể sử dụng để tương tác với dịch vụ. Có +ba cách bạn có thể định nghĩa giao diện:

+ +
+
Mở rộng lớp Trình gắn kết
+
Nếu dịch vụ của bạn chỉ riêng cho ứng dụng của chính bạn và chạy trong cùng tiến trình như máy khách +(điều này thường hay gặp), bạn nên tạo giao diện của mình bằng cách mở rộng lớp {@link android.os.Binder} +và trả về một thực thể của nó từ +{@link android.app.Service#onBind onBind()}. Máy khách nhận được {@link android.os.Binder} và +có thể sử dụng nó để trực tiếp truy cập các phương pháp công khai có sẵn trong triển khai {@link android.os.Binder} +hoặc thậm chí trong {@link android.app.Service}. +

Nên áp dụng kỹ thuật này khi dịch vụ của bạn chỉ là một trình thực hiện chạy ngầm cho ứng dụng +của chính bạn. Lý do duy nhất bạn không nên tạo giao diện của mình bằng cách này đó là +dịch vụ của bạn được sử dụng bởi các ứng dụng khác hoặc giữa những tiến trình khác nhau.

+ +
Sử dụng một Hàm nhắn tin
+
Nếu bạn cần giao diện của mình thực hiện các tiến trình khác nhau, bạn có thể tạo +một giao diện cho dịch vụ bằng {@link android.os.Messenger}. Bằng cách này, dịch vụ +định nghĩa một {@link android.os.Handler} phản hồi các loại đối tượng {@link +android.os.Message} khác nhau. {@link android.os.Handler} +này là cơ sở cho một {@link android.os.Messenger} mà sau đó có thể chia sẻ một {@link android.os.IBinder} +với máy khách, cho phép máy khách gửi lệnh tới dịch vụ bằng cách sử dụng các đối tượng {@link +android.os.Message}. Ngoài ra, máy khách có thể định nghĩa {@link android.os.Messenger} của +chính nó để dịch vụ có thể gửi lại thông báo. +

Đây là cách đơn giản nhất để thực hiện truyền thông liên tiến trình (IPC), vì {@link +android.os.Messenger} xếp hàng tất cả yêu cầu thành một luồng duy nhất sao cho bạn không phải thiết kế +dịch vụ của mình an toàn với luồng.

+
+ +
Sử dụng AIDL
+
AIDL (Ngôn ngữ Định nghĩa Giao diện Android) thực hiện tất cả công việc để phân tách đối tượng thành +các phần tử mà hệ điều hành có thể hiểu được và ghép nối chúng qua các tiến trình để thực hiện +IPC. Bằng cách sử dụng {@link android.os.Messenger}, kỹ thuật trước đó thực tế được dựa trên AIDL như là +cấu trúc cơ bản của nó. Như đã đề cập bên trên, {@link android.os.Messenger} tạo một hàng chờ +gồm tất cả yêu cầu của máy khách trong một luồng duy nhất, vì thế dịch vụ nhận được từng yêu cầu một. Tuy nhiên, nếu +bạn muốn dịch vụ xử lý nhiều yêu cầu đồng thời, bạn có thể sử dụng AIDL +trực tiếp. Trong trường hợp này, dịch vụ của bạn phải có khả năng tạo đa luồng và được xây dựng an toàn với luồng. +

Để sử dụng AIDL trực tiếp, bạn phải +tạo một tệp {@code .aidl} định nghĩa giao diện lập trình. Các công cụ SDK Android sử dụng tệp +này để khởi tạo một lớp tóm tắt (abstract class) nhằm triển khai giao diện và xử lý IPC, mà sau đó +bạn có thể mở rộng trong dịch vụ của mình.

+
+
+ +

Lưu ý: Hầu hết ứng dụng không nên sử dụng AIDL để +tạo một dịch vụ gắn kết, vì nó có thể yêu cầu khả năng tạo đa luồng và +có thể dẫn đến việc triển khai phức tạp hơn. Như vậy, AIDL không phù hợp với hầu hết ứng dụng +và tài liệu này không bàn về cách sử dụng nó cho dịch vụ của bạn. Nếu bạn chắc chắn rằng mình cần +sử dụng AIDL trực tiếp, hãy xem tài liệu AIDL +.

+ + + + +

Mở rộng lớp Trình gắn kết

+ +

Nếu dịch vụ của bạn chỉ được sử dụng bởi ứng dụng cục bộ và không cần làm việc qua nhiều tiến trình, +khi đó bạn có thể triển khai lớp {@link android.os.Binder} của chính mình để cung cấp quyền truy cập +trực tiếp cho máy khách của bạn để truy nhập các phương pháp công khai trong dịch vụ.

+ +

Lưu ý: Cách này chỉ có tác dụng nếu máy khách và dịch vụ nằm trong cùng +ứng dụng và tiến trình, là trường hợp phổ biến nhất. Ví dụ, cách này sẽ hoạt động tốt đối với một ứng dụng +nhạc cần gắn kết một hoạt động với dịch vụ của chính nó đang phát nhạc +chạy ngầm.

+ +

Sau đây là cách thiết lập:

+
    +
  1. Trong dịch vụ của bạn, hãy tạo một thực thể {@link android.os.Binder} mà hoặc: +
      +
    • chứa các phương pháp công khai mà máy khách có thể gọi
    • +
    • trả về thực thể {@link android.app.Service} hiện tại, trong đó có các phương pháp công khai mà +máy khách có thể gọi
    • +
    • hoặc, trả về một thực thể của một lớp khác được lưu trữ bởi dịch vụ bằng các phương pháp công khai mà +máy khách có thể gọi
    • +
    +
  2. Trả về thực thể {@link android.os.Binder} này từ phương pháp gọi lại {@link +android.app.Service#onBind onBind()}.
  3. +
  4. Trong máy khách, nhận {@link android.os.Binder} từ phương pháp gọi lại {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} và +thực hiện gọi tới dịch vụ gắn kết bằng cách sử dụng các phương pháp đã nêu.
  5. +
+ +

Lưu ý: Lý do dịch vụ và máy khách phải ở trong cùng +ứng dụng đó là máy khách có thể đổi kiểu đối tượng được trả về và gọi các API của nó một cách phù hợp. Dịch vụ +và máy khách cũng phải ở trong cùng tiến trình, vì kỹ thuật này không thực hiện bất kỳ thao tác +ghép nối qua các tiến trình nào.

+ +

Ví dụ, sau đây là một dịch vụ cung cấp cho máy khách quyền truy cập các phương pháp trong dịch vụ thông qua +việc triển khai {@link android.os.Binder}:

+ +
+public class LocalService extends Service {
+    // Binder given to clients
+    private final IBinder mBinder = new LocalBinder();
+    // Random number generator
+    private final Random mGenerator = new Random();
+
+    /**
+     * Class used for the client Binder.  Because we know this service always
+     * runs in the same process as its clients, we don't need to deal with IPC.
+     */
+    public class LocalBinder extends Binder {
+        LocalService getService() {
+            // Return this instance of LocalService so clients can call public methods
+            return LocalService.this;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    /** method for clients */
+    public int getRandomNumber() {
+      return mGenerator.nextInt(100);
+    }
+}
+
+ +

{@code LocalBinder} cung cấp phương pháp {@code getService()} cho máy khách để truy xuất +thực thể hiện tại của {@code LocalService}. Điều này cho phép máy khách gọi các phương pháp công khai trong +dịch vụ. Ví dụ, máy khách có thể gọi {@code getRandomNumber()} từ dịch vụ.

+ +

Sau đây là một hoạt động gắn kết với {@code LocalService} và sẽ gọi {@code getRandomNumber()} +khi nhấp vào nút:

+ +
+public class BindingActivity extends Activity {
+    LocalService mService;
+    boolean mBound = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        // Bind to LocalService
+        Intent intent = new Intent(this, LocalService.class);
+        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // Unbind from the service
+        if (mBound) {
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
+
+    /** Called when a button is clicked (the button in the layout file attaches to
+      * this method with the android:onClick attribute) */
+    public void onButtonClick(View v) {
+        if (mBound) {
+            // Call a method from the LocalService.
+            // However, if this call were something that might hang, then this request should
+            // occur in a separate thread to avoid slowing down the activity performance.
+            int num = mService.getRandomNumber();
+            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /** Defines callbacks for service binding, passed to bindService() */
+    private ServiceConnection mConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceConnected(ComponentName className,
+                IBinder service) {
+            // We've bound to LocalService, cast the IBinder and get LocalService instance
+            LocalBinder binder = (LocalBinder) service;
+            mService = binder.getService();
+            mBound = true;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName arg0) {
+            mBound = false;
+        }
+    };
+}
+
+ +

Mẫu trên cho thấy cách mà máy khách gắn kết với dịch vụ bằng cách sử dụng triển khai +{@link android.content.ServiceConnection} và gọi lại {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}. Phần tiếp theo +cung cấp thêm thông tin về tiến trình gắn kết này với dịch vụ.

+ +

Lưu ý: Ví dụ trên không công khai bỏ gắn kết khỏi dịch vụ, +nhưng tất cả máy khách cần bỏ gắn kết tại một thời điểm phù hợp (chẳng hạn như khi hoạt động tạm dừng).

+ +

Để biết thêm mã ví dụ, hãy xem lớp {@code +LocalService.java} và lớp {@code +LocalServiceActivities.java} trong ApiDemos.

+ + + + + +

Sử dụng một Hàm nhắn tin

+ + + +

Nếu bạn cần dịch vụ của mình giao tiếp với các tiến trình từ xa, khi đó bạn có thể sử dụng một +{@link android.os.Messenger} để cung cấp giao diện cho dịch vụ của mình. Kỹ thuật này cho phép +bạn thực hiện truyền thông liên tiến trình (IPC) mà không cần sử dụng AIDL.

+ +

Sau đây là tóm tắt cách sử dụng {@link android.os.Messenger}:

+ +
    +
  • Dịch vụ triển khai {@link android.os.Handler} để nhận lệnh gọi lại cho mỗi +lệnh gọi từ một máy khách.
  • +
  • {@link android.os.Handler} được sử dụng để tạo một đối tượng {@link android.os.Messenger} +(là một tham chiếu tới {@link android.os.Handler}).
  • +
  • {@link android.os.Messenger} tạo một {@link android.os.IBinder} mà dịch vụ +trả về máy khách từ {@link android.app.Service#onBind onBind()}.
  • +
  • Máy khách sử dụng {@link android.os.IBinder} để khởi tạo {@link android.os.Messenger} +(tham chiếu tới {@link android.os.Handler} của dịch vụ), mà máy khách sử dụng để gửi các đối tượng +{@link android.os.Message} tới dịch vụ.
  • +
  • Dịch vụ nhận được từng {@link android.os.Message} trong {@link +android.os.Handler} của mình—cụ thể là theo phương pháp {@link android.os.Handler#handleMessage +handleMessage()}.
  • +
+ + +

Theo cách này, không có "phương pháp" nào để máy khách gọi đối với dịch vụ. Thay vào đó, máy khách +gửi “thông báo” (đối tượng {@link android.os.Message}) mà dịch vụ nhận được trong +{@link android.os.Handler} của mình.

+ +

Sau đây là một dịch vụ ví dụ đơn giản sử dụng một giao diện {@link android.os.Messenger}:

+ +
+public class MessengerService extends Service {
+    /** Command to the service to display a message */
+    static final int MSG_SAY_HELLO = 1;
+
+    /**
+     * Handler of incoming messages from clients.
+     */
+    class IncomingHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SAY_HELLO:
+                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
+                    break;
+                default:
+                    super.handleMessage(msg);
+            }
+        }
+    }
+
+    /**
+     * Target we publish for clients to send messages to IncomingHandler.
+     */
+    final Messenger mMessenger = new Messenger(new IncomingHandler());
+
+    /**
+     * When binding to the service, we return an interface to our messenger
+     * for sending messages to the service.
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
+        return mMessenger.getBinder();
+    }
+}
+
+ +

Để ý rằng phương pháp {@link android.os.Handler#handleMessage handleMessage()} trong +{@link android.os.Handler} là nơi dịch vụ nhận được {@link android.os.Message} +đến và quyết định việc cần làm dựa trên thành viên {@link android.os.Message#what}.

+ +

Tất cả việc mà một máy khách cần làm đó là tạo một {@link android.os.Messenger} dựa trên {@link +android.os.IBinder} được dịch vụ trả về và gửi một thông báo bằng cách sử dụng {@link +android.os.Messenger#send send()}. Ví dụ, sau đây là một hoạt động đơn giản gắn kết với dịch vụ +và gửi tin nhắn {@code MSG_SAY_HELLO} cho dịch vụ:

+ +
+public class ActivityMessenger extends Activity {
+    /** Messenger for communicating with the service. */
+    Messenger mService = null;
+
+    /** Flag indicating whether we have called bind on the service. */
+    boolean mBound;
+
+    /**
+     * Class for interacting with the main interface of the service.
+     */
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            // This is called when the connection with the service has been
+            // established, giving us the object we can use to
+            // interact with the service.  We are communicating with the
+            // service using a Messenger, so here we get a client-side
+            // representation of that from the raw IBinder object.
+            mService = new Messenger(service);
+            mBound = true;
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            // This is called when the connection with the service has been
+            // unexpectedly disconnected -- that is, its process crashed.
+            mService = null;
+            mBound = false;
+        }
+    };
+
+    public void sayHello(View v) {
+        if (!mBound) return;
+        // Create and send a message to the service, using a supported 'what' value
+        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
+        try {
+            mService.send(msg);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        // Bind to the service
+        bindService(new Intent(this, MessengerService.class), mConnection,
+            Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        // Unbind from the service
+        if (mBound) {
+            unbindService(mConnection);
+            mBound = false;
+        }
+    }
+}
+
+ +

Để ý rằng ví dụ này không cho biết cách mà dịch vụ có thể phản hồi máy khách. Nếu bạn muốn dịch vụ +phản hồi, khi đó bạn cũng cần tạo một {@link android.os.Messenger} trong máy khách. Sau đó +khi máy khách nhận được lệnh gọi lại {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, nó sẽ gửi một {@link android.os.Message} tới dịch vụ, trong đó bao gồm +{@link android.os.Messenger} của máy khách trong tham số {@link android.os.Message#replyTo} +của phương pháp {@link android.os.Messenger#send send()}.

+ +

Bạn có thể xem một ví dụ về cách cung cấp tính năng nhắn tin hai chiều trong {@code +MessengerService.java} (dịch vụ) và các mẫu {@code +MessengerServiceActivities.java} (máy khách).

+ + + + + +

Gắn kết với một Dịch vụ

+ +

Các thành phần ứng dụng (máy khách) có thể gắn kết với một dịch vụ bằng cách gọi +{@link android.content.Context#bindService bindService()}. Hệ thống Android +khi đó sẽ gọi phương pháp {@link android.app.Service#onBind +onBind()} của dịch vụ, nó trả về một {@link android.os.IBinder} để tương tác với dịch vụ.

+ +

Việc gắn kết diễn ra không đồng bộ. {@link android.content.Context#bindService +bindService()} trả về ngay lập tức và không trả {@link android.os.IBinder} về +máy khách. Để nhận một {@link android.os.IBinder}, máy khách phải tạo một thực thể của {@link +android.content.ServiceConnection} và chuyển nó cho {@link android.content.Context#bindService +bindService()}. {@link android.content.ServiceConnection} bao gồm một phương pháp gọi lại mà hệ thống +gọi để gửi {@link android.os.IBinder}.

+ +

Lưu ý: Chỉ các hoạt động, dịch vụ, và trình cung cấp nội dung mới có thể gắn kết +với một dịch vụ—bạn không thể gắn kết với một dịch vụ từ một hàm nhận quảng bá (broadcast receiver).

+ +

Vì vậy, để gắn kết với một dịch vụ từ máy khách của mình, bạn phải:

+
    +
  1. Triển khai {@link android.content.ServiceConnection}. +

    Việc triển khai của bạn phải khống chế hai phương pháp gọi lại:

    +
    +
    {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
    +
    Hệ thống gọi phương pháp này để gửi {@link android.os.IBinder} được trả về bởi +phương pháp {@link android.app.Service#onBind onBind()} của dịch vụ.
    +
    {@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}
    +
    Hệ thống Android gọi phương pháp này khi kết nối với dịch vụ bị mất +đột ngột, chẳng hạn như khi dịch vụ bị lỗi hoặc bị tắt bỏ. Phương pháp này không được gọi khi +máy khách bỏ gắn kết.
    +
    +
  2. +
  3. Gọi {@link +android.content.Context#bindService bindService()}, chuyển việc triển khai {@link +android.content.ServiceConnection}.
  4. +
  5. Khi hệ thống gọi phương pháp gọi lại {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} của bạn, bạn có thể bắt đầu thực hiện các lệnh gọi tới dịch vụ bằng các phương pháp +được định nghĩa bởi giao diện.
  6. +
  7. Để ngắt kết nối khỏi dịch vụ, hãy gọi {@link +android.content.Context#unbindService unbindService()}. +

    Khi máy khách của bạn bị hủy, nó sẽ bỏ gắn kết khỏi dịch vụ, nhưng bạn nên luôn bỏ gắn kết +khi bạn đã tương tác xong với dịch vụ hoặc khi hoạt động của bạn tạm dừng sao cho dịch vụ có thể +tắt khi không dùng đến. (Thời điểm phù hợp để gắn kết và bỏ gắn kết được đề cập +kỹ hơn ở bên dưới.)

    +
  8. +
+ +

Ví dụ, đoạn mã HTML sau sẽ kết nối máy khách với dịch vụ được tạo bên trên bằng cách +mở rộng lớp Trình gắn kết, vì vậy tất cả những việc mà nó phải làm là đổi kiểu +{@link android.os.IBinder} được trả về thành lớp {@code LocalService} và yêu cầu thực thể {@code +LocalService}:

+ +
+LocalService mService;
+private ServiceConnection mConnection = new ServiceConnection() {
+    // Called when the connection with the service is established
+    public void onServiceConnected(ComponentName className, IBinder service) {
+        // Because we have bound to an explicit
+        // service that is running in our own process, we can
+        // cast its IBinder to a concrete class and directly access it.
+        LocalBinder binder = (LocalBinder) service;
+        mService = binder.getService();
+        mBound = true;
+    }
+
+    // Called when the connection with the service disconnects unexpectedly
+    public void onServiceDisconnected(ComponentName className) {
+        Log.e(TAG, "onServiceDisconnected");
+        mBound = false;
+    }
+};
+
+ +

Với {@link android.content.ServiceConnection} này, máy khách có thể gắn kết với một dịch vụ bằng cách chuyển +nó cho {@link android.content.Context#bindService bindService()}. Ví dụ:

+ +
+Intent intent = new Intent(this, LocalService.class);
+bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+ +
    +
  • Tham số đầu tiên của {@link android.content.Context#bindService bindService()} là một +{@link android.content.Intent} trong đó nêu rõ tên của các dịch vụ sẽ gắn kết (mặc dù ý định +có thể ngầm hiểu).
  • +
  • Tham số thứ hai là đối tượng {@link android.content.ServiceConnection}.
  • +
  • Tham số thứ ba là một cờ cho biết các tùy chọn cho gắn kết. Nên luôn luôn là {@link +android.content.Context#BIND_AUTO_CREATE} để tạo dịch vụ nếu nó chưa hoạt động. +Các giá trị có thể khác là {@link android.content.Context#BIND_DEBUG_UNBIND} +và {@link android.content.Context#BIND_NOT_FOREGROUND}, hoặc {@code 0} trong trường hợp không có.
  • +
+ + +

Lưu ý bổ sung

+ +

Sau đây là một số lưu ý quan trọng về việc gắn kết với một dịch vụ:

+
    +
  • Bạn nên luôn bẫy các lỗi ngoại lệ {@link android.os.DeadObjectException} phát sinh khi +kết nối bị đứt. Đây là lỗi ngoại lệ duy nhất phát sinh bởi các phương pháp từ xa.
  • +
  • Các đối tượng được xem là tham chiếu khắp các tiến trình.
  • +
  • Bạn nên luôn ghép đôi gắn kết và bỏ gắn kết trong khi +khớp những khoảnh khắc kết nối và đứt kết nối trong vòng đời của máy khách. Ví dụ: +
      +
    • Nếu bạn chỉ cần tương tác với dịch vụ trong khi hoạt động của bạn hiển thị, bạn +nên gắn kết trong khi {@link android.app.Activity#onStart onStart()} và bỏ gắn kết trong khi {@link +android.app.Activity#onStop onStop()}.
    • +
    • Nếu bạn muốn hoạt động của mình nhận được phản hồi ngay cả trong khi bị dừng khi đang +dưới nền, khi đó bạn có thể gắn kết trong khi {@link android.app.Activity#onCreate onCreate()} và bỏ gắn kết +trong khi {@link android.app.Activity#onDestroy onDestroy()}. Chú ý rằng điều này hàm ý rằng hoạt động +của bạn cần sử dụng dịch vụ trong toàn bộ thời gian khi nó đang chạy (ngay cả khi chạy ngầm), do đó nếu +dịch vụ ở trong một tiến trình khác thì bạn hãy tăng trọng số của tiến trình và khả năng hệ thống +tắt bỏ tiến trình đó sẽ cao hơn.
    • +
    +

    Lưu ý: Thông thường bạn không nên gắn kết và bỏ gắn kết +trong khi {@link android.app.Activity#onResume onResume()} và {@link +android.app.Activity#onPause onPause()} cho hoạt động của mình, vì những lệnh gọi lại này diễn ra tại mọi thời điểm chuyển tiếp vòng đời +và bạn nên duy trì xử lý tại những thời điểm chuyển tiếp này ở mức tối thiểu. Đồng thời, nếu +nhiều hoạt động trong ứng dụng của bạn gắn kết với cùng dịch vụ và có sự chuyển tiếp giữa +hai trong số những hoạt động đó, dịch vụ có thể bị hủy và tạo lại khi hoạt động hiện tại bỏ gắn kết +(trong khi tạm dừng) trước khi hoạt động tiếp theo gắn kết (trong khi tiếp tục). (Sự chuyển tiếp hoạt động này đối với cách mà các hoạt động +phối hợp vòng đời của chúng được mô tả trong tài liệu Hoạt động +.)

    +
+ +

Để biết thêm mã ví dụ, thể hiện cách gắn kết với một dịch vụ, hãy xem lớp {@code +RemoteService.java} trong ApiDemos.

+ + + + + +

Quản lý Vòng đời của một Dịch vụ Gắn kết

+ +

Khi một dịch vụ bị bỏ gắn kết khỏi tất cả máy khách, hệ thống Android sẽ hủy nó (trừ khi nó cũng +được bắt đầu bằng {@link android.app.Service#onStartCommand onStartCommand()}). Như vậy, bạn không phải + quản lý vòng đời dịch vụ của mình nếu nó thuần túy là một +dịch vụ gắn kết—hệ thống Android sẽ quản lý nó cho bạn dựa trên việc nó có gắn kết với bất kỳ máy khách nào không.

+ +

Tuy nhiên, nếu bạn chọn triển khai phương pháp gọi lại {@link android.app.Service#onStartCommand +onStartCommand()}, vậy thì bạn phải dừng dịch vụ một cách tường minh, vì dịch vụ +lúc này đang được coi là được bắt đầu. Trong trường hợp này, dịch vụ sẽ chạy cho tới khi dịch vụ +tự dừng bằng {@link android.app.Service#stopSelf()} hoặc một thành phần khác sẽ gọi {@link +android.content.Context#stopService stopService()}, bất kể nó có gắn kết với bất kỳ máy khách +nào không.

+ +

Ngoài ra, nếu dịch vụ của bạn được bắt đầu và chấp nhận gắn kết, lúc đó khi hệ thống gọi +phương pháp {@link android.app.Service#onUnbind onUnbind()} của bạn, bạn có thể tùy chọn trả về +{@code true} nếu bạn muốn nhận một lệnh gọi tới {@link android.app.Service#onRebind +onRebind()} vào lần tới khi một máy khách gắn kết với dịch vụ (thay vì nhận một lệnh gọi tới {@link +android.app.Service#onBind onBind()}). {@link android.app.Service#onRebind +onRebind()} sẽ trả về rỗng, nhưng máy khách vẫn nhận được {@link android.os.IBinder} trong gọi lại +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} của mình. +Hình 1 bên dưới minh họa lô-gic cho loại vòng đời này.

+ + + +

Hình 1. Vòng đời của một dịch vụ được bắt đầu +và cũng cho phép gắn kết.

+ + +

Để biết thêm thông tin về vòng đời của một dịch vụ được bắt đầu, hãy xem tài liệu Dịch vụ.

+ + + + diff --git a/docs/html-intl/intl/vi/guide/components/fragments.jd b/docs/html-intl/intl/vi/guide/components/fragments.jd new file mode 100644 index 0000000000000000000000000000000000000000..95d9c76337fc86b75f7dfda9a5e8d3ba749d37d5 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=Phân đoạn +parent.title=Hoạt động +parent.link=activities.html +@jd:body + + + +

{@link android.app.Fragment} biểu diễn một hành vi hay một phần giao diện người dùng trong một +{@link android.app.Activity}. Bạn có thể kết hợp nhiều phân đoạn trong một hoạt động duy nhất để xây dựng một +UI nhiều bảng và sử dụng lại phân đoạn trong nhiều hoạt động. Bạn có thể coi phân đoạn như là một +phần mô-đun của một hoạt động, có vòng đời của chính nó, nhận các sự kiện đầu vào của chính nó, và +bạn có thể thêm hoặc gỡ bỏ trong khi hoạt động đang chạy (kiểu như một "hoạt động con" mà +bạn có thể sử dụng lại trong các hoạt động khác nhau).

+ +

Phân đoạn phải luôn được nhúng trong một hoạt động và vòng đời của phân đoạn bị ảnh hưởng trực tiếp bởi +vòng đời của hoạt động chủ. Ví dụ, khi hoạt động bị tạm dừng, tất cả +phân đoạn trong nó cũng vậy, và khi hoạt động bị hủy, tất cả phân đoạn cũng vậy. Tuy nhiên, trong khi một +hoạt động đang chạy (nó ở trong trạng thái vòng đời được tiếp tục), bạn có thể +thao tác từng phân đoạn độc lập, chẳng hạn như thêm hay xóa chúng. Khi bạn thực hiện một +giao tác phân đoạn, bạn cũng có thể thêm nó vào một ngăn xếp được quản lý bởi +hoạt động đó—từng mục nhập vào ngăn xếp trong hoạt động là một bản ghi giao tác phân đoạn +đã xảy ra. Ngăn xếp cho phép người dùng đảo ngược một giao tác phân đoạn (điều hướng ngược lại), +bằng cách nhấn nút Quay lại.

+ +

Khi bạn thêm một phân đoạn như một phần trong bố trí hoạt động của mình, nó sẽ ở trong một {@link +android.view.ViewGroup} bên trong phân cấp dạng xem của hoạt động đó và phân đoạn này sẽ định nghĩa bố trí +dạng xem của chính nó. +Bạn có thể chèn một phân đoạn vào bố trí hoạt động của mình bằng cách khai báo phân đoạn trong tệp +bố trí của hoạt động, dưới dạng một phần tử {@code <fragment>}, hoặc từ mã ứng dụng của bạn bằng cách thêm nó vào một +{@link android.view.ViewGroup} hiện hữu. Tuy nhiên, không bắt buộc phải có một phân đoạn là một bộ phận của bố trí hoạt động +; bạn cũng có thể sử dụng một phân đoạn mà không cần UI của chính nó như một trình thực hiện vô hình cho hoạt động +.

+ +

Tài liệu này mô tả cách xây dựng ứng dụng của bạn để sử dụng phân đoạn, bao gồm +cách các phân đoạn có thể duy trì trạng thái của chúng khi được thêm vào ngăn xếp của hoạt động, chia sẻ +các sự kiện với hoạt động và các phân đoạn khác trong hoạt động, đóng góp vào thanh hành động của hoạt động +và nhiều thông tin khác.

+ + +

Triết lý Thiết kế

+ +

Android giới thiệu phân đoạn trong phiên bản Android 3.0 (API mức 11), chủ yếu nhằm hỗ trợ +các thiết kế UI động và linh hoạt hơn trên màn hình lớn, chẳng hạn như máy tính bảng. Vì +màn hình của máy tính bảng lớn hơn nhiều màn hình của thiết bị cầm tay, có nhiều khoảng trống hơn để kết hợp và +trao đổi các thành phần UI. Phân đoạn cho phép những thiết kế như vậy mà không cần bạn phải quản lý những thay đổi +phức tạp về phân cấp dạng xem. Bằng cách chia bố trí của một hoạt động thành các phân đoạn, bạn có thể +sửa đổi diện mạo của hoạt động vào thời gian chạy và giữ những thay đổi đó trong một ngăn xếp +được quản lý bởi hoạt động.

+ +

Ví dụ, một ứng dụng tin tức có thể sử dụng một phân đoạn để hiển thị một danh sách bài viết ở +bên trái và một phân đoạn khác để hiển thị một bài viết ở bên phải—cả hai phân đoạn đều xuất hiện trong một +hoạt động, bên cạnh nhau, và từng phân đoạn có tập phương pháp gọi lại vòng đời riêng và xử lý +các sự kiện nhập liệu người dùng riêng của mình. Vì thế, thay vì sử dụng một hoạt động để chọn một bài viết và một +hoạt động khác để đọc bài viết, người dùng có thể chọn một bài viết và đọc nó trong cùng +hoạt động, như được minh họa trong bố trí máy tính bảng trong hình 1.

+ +

Bạn nên thiết kế từng phân đoạn như một thành phần hoạt động dạng mô-đun và có thể sử dụng lại. Đó là bởi +mỗi phân đoạn sẽ định nghĩa bố trí và hành vi của chính nó với các phương pháp gọi lại vòng đời của chính nó, bạn có thể +bao gồm một phân đoạn trong nhiều hoạt động, vì thế bạn nên thiết kế để tái sử dụng và tránh trực tiếp +thao tác một phân đoạn từ một phân đoạn khác. Điều này đặc biệt quan trọng vì một phân đoạn +mô-đun cho phép bạn thay đổi kết hợp phân đoạn của mình cho các kích cỡ màn hình khác nhau. Khi thiết kế +ứng dụng của bạn để hỗ trợ cả máy tính bảng và thiết bị cầm tay, bạn có thể sử dụng lại phân đoạn của mình trong các cấu hình +bố trí khác nhau nhằm tối ưu hóa trải nghiệm người dùng dựa trên không gian màn hình có sẵn. Ví +dụ, trên một thiết bị cầm tay, có thể cần phải tách riêng các phân đoạn để cung cấp một UI đơn bảng khi mà +không thể làm vừa khít nhiều hơn một phân đoạn trong cùng hoạt động.

+ + +

Hình 1. Ví dụ về cách hai mô-đun UI được định nghĩa +bởi các phân đoạn có thể được kết hợp thành một hoạt động đối với thiết kế máy tính bảng, nhưng được tách riêng đối với +thiết kế thiết bị cầm tay.

+ +

Ví dụ—để tiếp tục với ví dụ về ứng dụng tin tức—ứng dụng có thể nhúng +hai phân đoạn trong Hoạt động A, khi đang chạy trên một thiết bị có kích cỡ máy tính bảng. Tuy nhiên, trên một +màn hình kích cỡ thiết bị cầm tay, không có đủ khoảng trống cho cả hai phân đoạn, vì thế Hoạt động A chỉ +bao gồm phân đoạn cho danh sách bài viết, và khi người dùng chọn một bài viết, nó sẽ khởi động +Hoạt động B, hoạt động này chứa phân đoạn thứ hai là đọc bài viết. Vì thế, ứng dụng +hỗ trợ cả máy tính bảng và thiết bị cầm tay bằng cách sử dụng lại các phân đoạn theo các cách kết hợp khác nhau như được minh họa trong +hình 1.

+ +

Để biết thêm thông tin về việc thiết kế ứng dụng của bạn bằng các cách kết hợp phân đoạn khác nhau cho +cấu hình màn hình khác nhau, hãy xem hướng dẫn Hỗ trợ Máy tính bảng và Thiết bị cầm tay.

+ + + +

Tạo một Phân đoạn

+ +
+ +

Hình 2. Vòng đời của một phân đoạn (trong khi hoạt động +của nó đang chạy).

+
+ +

Để tạo một phân đoạn, bạn phải tạo một lớp con của {@link android.app.Fragment} (hoặc +một lớp con hiện tại của nó). Lớp {@link android.app.Fragment} có mã trông rất giống +một {@link android.app.Activity}. Nó chứa các phương pháp gọi lại tương tự như hoạt động, chẳng +hạn như {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()}, và {@link android.app.Fragment#onStop onStop()}. Trên +thực tế, nếu bạn đang chuyển đổi một ứng dụng Android hiện tại để sử dụng các phân đoạn, bạn có thể chỉ cần di chuyển +mã khỏi các phương pháp gọi lại của hoạt động của bạn vào các phương pháp gọi lại tương ứng của phân đoạn +của bạn.

+ +

Thường thì ít nhất bạn nên triển khai các phương pháp vòng đời sau:

+ +
+
{@link android.app.Fragment#onCreate onCreate()}
+
Hệ thống sẽ gọi phương pháp này khi tạo phân đoạn. Trong triển khai của mình, bạn nên +khởi tạo các thành phần thiết yếu của phân đoạn mà bạn muốn giữ lại khi phân đoạn +bị tạm dừng hoặc dừng hẳn, sau đó tiếp tục.
+
{@link android.app.Fragment#onCreateView onCreateView()}
+
Hệ thống sẽ gọi phương pháp này khi đến lúc phân đoạn vẽ giao diện người dùng của nó +lần đầu tiên. Để vẽ một UI cho phân đoạn của mình, bạn phải trả về một {@link android.view.View} từ phương pháp +này, đây là gốc của bố trí phân đoạn của bạn. Bạn có thể trả về giá trị rỗng nếu phân đoạn không +cung cấp UI.
+
{@link android.app.Activity#onPause onPause()}
+
Hệ thống gọi phương pháp này là dấu hiệu đầu tiên về việc người dùng đang rời khỏi +phân đoạn (mặc dù không phải lúc nào cũng có nghĩa rằng phân đoạn đang bị hủy). Trường hợp này thường là khi bạn +định thực hiện bất kỳ thay đổi nào vẫn cần có hiệu lực ngoài phiên của người dùng hiện thời (vì +người dùng có thể không quay lại).
+
+ +

Phần lớn ứng dụng nên triển khai ít nhất ba phương pháp sau đối với mọi phân đoạn, nhưng có một vài +phương pháp gọi lại khác mà bạn cũng nên sử dụng để xử lý các giai đoạn khác nhau trong +vòng đời của phân đoạn. Tất cả phương pháp gọi lại vòng đời được đề cập chi tiết hơn trong phần +về Xử lý Vòng đời của Phân đoạn.

+ + +

Cũng có một vài lớp con mà bạn có thể muốn mở rộng thay vì lớp cơ bản {@link +android.app.Fragment}:

+ +
+
{@link android.app.DialogFragment}
+
Hiển thị một hộp thoại trôi nổi. Sử dụng lớp này để tạo một hộp thoại là một phương án hay cho việc sử dụng các phương pháp trình trợ giúp +hộp thoại trong lớp {@link android.app.Activity}, vì bạn có thể +kết hợp một hộp thoại phân đoạn vào ngăn xếp của các phân đoạn được quản lý bởi hoạt động, +cho phép người dùng trả về một phân đoạn bị bỏ.
+ +
{@link android.app.ListFragment}
+
Hiển thị một danh sách các mục được quản lý bởi một trình điều hợp (chẳng hạn như một {@link +android.widget.SimpleCursorAdapter}), tương tự như {@link android.app.ListActivity}. Nó cung cấp +một vài phương pháp để quản lý một dạng xem danh sách, chẳng hạn như phương pháp gọi lại {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} để +xử lý các sự kiện nhấp.
+ +
{@link android.preference.PreferenceFragment}
+
Hiển thị một phân cấp các đối tượng {@link android.preference.Preference} dưới dạng một danh sách, tương tự như +{@link android.preference.PreferenceActivity}. Điều này hữu ích khi tạo một hoạt động "thiết đặt" +cho ứng dụng của bạn.
+
+ + +

Thêm một giao diện người dùng

+ +

Phân đoạn thường được sử dụng như một phần giao diện người dùng của hoạt động và đóng góp bố trí của +chính nó cho hoạt động.

+ +

Để cung cấp một bố trí cho một phân đoạn, bạn phải triển khai phương pháp gọi lại {@link +android.app.Fragment#onCreateView onCreateView()}, phương pháp này được hệ thống Android gọi +khi đến lúc phân đoạn vẽ bố trí của nó. Việc bạn triển khai phương pháp này phải trả về một +{@link android.view.View} là phần gốc cho bố trí phân đoạn của bạn.

+ +

Lưu ý: Nếu phân đoạn của bạn là một lớp con của {@link +android.app.ListFragment}, triển khai mặc định sẽ trả về một {@link android.widget.ListView} từ +{@link android.app.Fragment#onCreateView onCreateView()}, vì thế bạn không cần triển khai nó.

+ +

Để trả về một bố trí từ {@link +android.app.Fragment#onCreateView onCreateView()}, bạn có thể bung nó từ một tài nguyên bố trí được định nghĩa trong XML. Để +giúp bạn làm vậy, {@link android.app.Fragment#onCreateView onCreateView()} cung cấp một đối tượng +{@link android.view.LayoutInflater}.

+ +

Ví dụ, sau đây là một lớp con của {@link android.app.Fragment} với chức năng nạp một bố trí từ tệp +{@code example_fragment.xml}:

+ +
+public static class ExampleFragment extends Fragment {
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        return inflater.inflate(R.layout.example_fragment, container, false);
+    }
+}
+
+ + + +

Tham số {@code container} được chuyển tới {@link android.app.Fragment#onCreateView +onCreateView()} là {@link android.view.ViewGroup} mẹ (tức bố trí của hoạt động), trong đó +bố trí phân đoạn của bạn +sẽ được chèn vào. Tham số {@code savedInstanceState} là một {@link android.os.Bundle} có chức năng +cung cấp dữ liệu về thực thể trước đó của phân đoạn, nếu phân đoạn đang được tiếp tục +(việc khôi phục trạng thái được bàn kỹ hơn trong phần về Xử lý +Vòng đời của Phân đoạn).

+ +

Phương pháp {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} có +ba tham đối:

+
    +
  • ID tài nguyên của bố trí mà bạn muốn bung.
  • +
  • {@link android.view.ViewGroup} là mẹ của bố trí được bung. Việc chuyển {@code +container} có vai trò quan trọng để hệ thống áp dụng các tham số bố trí cho dạng xem gốc của bố trí +được bung, được quy định bởi dạng xem mẹ là nơi mà nó diễn ra trong đó.
  • +
  • Một boolean cho biết bố trí được bung có nên được gắn với {@link +android.view.ViewGroup} (tham số thứ hai) trong khi bung hay không. (Trong trường hợp này, điều này là +sai vì hệ thống đã đang chèn bố trí được bung vào {@code +container}—việc chuyển đúng sẽ tạo ra một nhóm dạng xem thừa trong bố trí cuối cùng.)
  • +
+ +

Giờ bạn đã thấy cách tạo một phân đoạn nhằm cung cấp một bố trí. Tiếp theo, bạn cần thêm +phân đoạn vào hoạt động của mình.

+ + + +

Thêm một phân đoạn vào một hoạt động

+ +

Thường thì một phân đoạn đóng góp một phần UI vào hoạt động chủ, nó được nhúng như một phần +trong phân cấp dạng xem tổng thể của hoạt động. Có hai cách mà bạn có thể thêm một phân đoạn vào bố trí +của hoạt động:

+ +
    +
  • Khai báo phân đoạn bên trong tệp bố trí của hoạt động. +

    Trong trường hợp này, bạn có thể +chỉ định các tính chất bố trí cho phân đoạn như thể nó là một dạng xem. Ví dụ, sau đây là tệp bố trí +cho một hoạt động có hai phân đoạn:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:orientation="horizontal"
    +    android:layout_width="match_parent"
    +    android:layout_height="match_parent">
    +    <fragment android:name="com.example.news.ArticleListFragment"
    +            android:id="@+id/list"
    +            android:layout_weight="1"
    +            android:layout_width="0dp"
    +            android:layout_height="match_parent" />
    +    <fragment android:name="com.example.news.ArticleReaderFragment"
    +            android:id="@+id/viewer"
    +            android:layout_weight="2"
    +            android:layout_width="0dp"
    +            android:layout_height="match_parent" />
    +</LinearLayout>
    +
    +

    Thuộc tính {@code android:name} trong {@code <fragment>} sẽ chỉ định lớp {@link +android.app.Fragment} để khởi tạo trong bố trí.

    + +

    Khi hệ thống tạo bố trí hoạt động này, nó sẽ khởi tạo từng phân đoạn được chỉ định trong bố trí +và gọi ra phương pháp {@link android.app.Fragment#onCreateView onCreateView()} cho từng phân đoạn, +để truy xuất bố trí của từng phân đoạn. Hệ thống sẽ chèn {@link android.view.View} được trả về bởi phân đoạn +trực tiếp thế chỗ phần tử {@code <fragment>}.

    + +
    +

    Lưu ý: Mỗi phân đoạn yêu cầu một mã định danh duy nhất +mà hệ thống có thể sử dụng để khôi phục phân đoạn nếu hoạt động bị khởi động lại (và bạn có thể sử dụng để +nắm bắt phân đoạn sẽ thực hiện giao tác, chẳng hạn như gỡ bỏ nó). Có ba cách để cung cấp ID cho một +phân đoạn:

    +
      +
    • Cung cấp thuộc tính {@code android:id} với một ID duy nhất.
    • +
    • Cung cấp thuộc tính {@code android:tag} với một xâu duy nhất.
    • +
    • Nếu bạn không cung cấp được thuộc tính nào, hệ thống sẽ sử dụng ID của dạng xem +của bộ chứa.
    • +
    +
    +
  • + +
  • Hoặc, bằng cách lập trình, thêm phân đoạn vào một {@link android.view.ViewGroup} hiện hữu. +

    Vào bất cứ lúc nào trong khi hoạt động của bạn đang chạy, bạn có thể thêm phân đoạn vào bố trí hoạt động của mình. Bạn +chỉ cần chỉ định một {@link +android.view.ViewGroup} là nơi mà bạn sẽ đặt phân đoạn vào.

    +

    Để thực hiện giao tác phân đoạn trong hoạt động của mình (chẳng hạn như thêm, gỡ bỏ, hay thay thế một +phân đoạn), bạn phải sử dụng các API từ {@link android.app.FragmentTransaction}. Bạn có thể nhận một thực thể +của {@link android.app.FragmentTransaction} từ {@link android.app.Activity} của mình như sau:

    + +
    +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}
    +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
    +
    + +

    Sau đó, bạn có thể thêm một phân đoạn bằng cách sử dụng phương pháp {@link +android.app.FragmentTransaction#add(int,Fragment) add()}, chỉ định phân đoạn sẽ thêm và +dạng xem mà bạn sẽ chèn nó vào. Ví dụ:

    + +
    +ExampleFragment fragment = new ExampleFragment();
    +fragmentTransaction.add(R.id.fragment_container, fragment);
    +fragmentTransaction.commit();
    +
    + +

    Tham đối đầu tiên được chuyển cho {@link android.app.FragmentTransaction#add(int,Fragment) add()} +là {@link android.view.ViewGroup}, là nơi mà phân đoạn sẽ được đặt vào, được chỉ định bởi +ID tài nguyên, và tham đối thứ hai là phân đoạn cần thêm.

    +

    Sau khi bạn đã thực hiện các thay đổi của mình bằng +{@link android.app.FragmentTransaction}, bạn phải +gọi {@link android.app.FragmentTransaction#commit} để các thay đổi có hiệu lực.

    +
  • +
+ + +

Thêm một phân đoạn không có UI

+ +

Các ví dụ nêu trên cho biết cách thêm một phân đoạn vào hoạt động của bạn để cung cấp một UI. Tuy nhiên, +bạn cũng có thể sử dụng một phân đoạn để cung cấp một hành vi chạy ngầm cho hoạt động mà không cần đưa +UI bổ sung.

+ +

Để thêm một phân đoạn không có UI, hãy thêm phân đoạn từ hoạt động đang bằng cách sử dụng {@link +android.app.FragmentTransaction#add(Fragment,String)} (cung cấp một "tag" xâu duy nhất cho phân đoạn +, thay vì một ID dạng xem). Làm vậy sẽ thêm phân đoạn, nhưng vì không liên kết với một dạng xem +trong bố trí hoạt động, nó sẽ không nhận được lệnh gọi tới {@link +android.app.Fragment#onCreateView onCreateView()}. Vì thế, bạn không cần triển khai phương pháp đó.

+ +

Việc cung cấp tag xâu cho phân đoạn không chỉ áp dụng cho các phân đoạn không có UI—bạn cũng có thể +cung cấp tag xâu cho phân đoạn có UI—nhưng nếu phân đoạn không có +UI, khi đó, tag xâu là cách duy nhất để nhận biết nó. Nếu sau này bạn muốn nhận phân đoạn từ +hoạt động, bạn cần sử dụng {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

+ +

Để biết ví dụ về hoạt động sử dụng phân đoạn như một trình thực hiện nền, không có UI, hãy xem mẫu {@code +FragmentRetainInstance.java}, mẫu này có trong các mẫu SDK (có sẵn thông qua +Trình quản lý SDK Android) và nằm trên hệ thống của bạn như là +<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

+ + + +

Quản lý Phân đoạn

+ +

Để quản lý các phân đoạn trong hoạt động của mình, bạn cần sử dụng {@link android.app.FragmentManager}. Để +có nó, hãy gọi {@link android.app.Activity#getFragmentManager()} từ hoạt động của bạn.

+ +

Một số việc bạn có thể làm với {@link android.app.FragmentManager} bao gồm:

+ +
    +
  • Nhận các phân đoạn tồn tại trong hoạt động, bằng {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (đối với các phân đoạn cung cấp UI trong +bố trí hoạt động) hoặc {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (đối với các phân đoạn có hoặc không cung cấp UI).
  • +
  • Lấy phân đoạn ra khỏi ngăn xếp, bằng {@link +android.app.FragmentManager#popBackStack()} (mô phỏng một câu lệnh Quay lại của người dùng).
  • +
  • Đăng ký một đối tượng theo dõi cho những thay đổi đối với ngăn xếp, bằng {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.
  • +
+ +

Để biết thêm thông tin về những phương pháp này và phương pháp khác, hãy tham khảo tài liệu lớp {@link +android.app.FragmentManager}.

+ +

Như minh họa trong phần trước, bạn cũng có thể sử dụng {@link android.app.FragmentManager} +để mở một {@link android.app.FragmentTransaction}, nó cho phép bạn thực hiện các giao tác, ví dụ như +thêm hoặc gỡ bỏ phân đoạn.

+ + +

Thực hiện Giao tác Phân đoạn

+ +

Một tính năng tuyệt vời khi sử dụng phân đoạn trong hoạt động của bạn đó là khả năng thêm, gỡ bỏ, thay thế, +và thực hiện các hành động khác với chúng, để hồi đáp lại tương tác của người dùng. Mỗi tập hợp thay đổi mà bạn +thực thi cho hoạt động được gọi là một giao tác và bạn có thể thực hiện một giao tác bằng cách sử dụng các API trong {@link +android.app.FragmentTransaction}. Bạn cũng có thể lưu từng giao tác vào một ngăn xếp được quản lý bởi +hoạt động, cho phép người dùng điều hướng ngược lại thông qua những thay đổi phân đoạn (tương tự như điều hướng +ngược lại thông qua hoạt động).

+ +

Bạn có thể thu được một thực thể của {@link android.app.FragmentTransaction} từ {@link +android.app.FragmentManager} như sau:

+ +
+FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()};
+FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
+
+ +

Mỗi giao tác là một tập hợp những thay đổi mà bạn muốn thực hiện tại cùng thời điểm. Bạn có thể thiết lập +tất cả thay đổi mà mình muốn thực hiện đối với một giao tác cho trước bằng cách sử dụng các phương pháp như {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()}, +và {@link android.app.FragmentTransaction#replace replace()}. Sau đó, để áp dụng giao tác +cho hoạt động, bạn phải gọi {@link android.app.FragmentTransaction#commit()}.

+ + +

Trước khi bạn gọi {@link +android.app.FragmentTransaction#commit()}, tuy nhiên, bạn có thể muốn gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, để thêm giao tác +vào một ngăn xếp của các giao tác phân đoạn. Ngăn xếp này được quản lý bởi hoạt động và cho phép +người dùng trở về trạng thái phân đoạn trước đó, bằng cách nhấp nút Quay lại.

+ +

Ví dụ, sau đây là cách bạn có thể thay thế phân đoạn này bằng phân đoạn khác, và giữ nguyên +trạng thái trước đó của ngăn xếp:

+ +
+// Create new fragment and transaction
+Fragment newFragment = new ExampleFragment();
+FragmentTransaction transaction = getFragmentManager().beginTransaction();
+
+// Replace whatever is in the fragment_container view with this fragment,
+// and add the transaction to the back stack
+transaction.replace(R.id.fragment_container, newFragment);
+transaction.addToBackStack(null);
+
+// Commit the transaction
+transaction.commit();
+
+ +

Trong ví dụ này, {@code newFragment} thay thế mọi phân đoạn (nếu có) hiện đang ở trong +bộ chứa bố trí được nhận biết bởi ID {@code R.id.fragment_container}. Bằng cách gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, giao tác thay thế +được lưu vào ngăn xếp, vì thế người dùng có thể đảo ngược giao tác và mang +giao tác trước đó trở lại bằng cách nhấn nút Quay lại.

+ +

Nếu bạn thêm nhiều thay đổi vào giao tác (chẳng hạn như một {@link +android.app.FragmentTransaction#add add()} khác hoặc {@link android.app.FragmentTransaction#remove +remove()}) và gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, khi đó, tất cả thay đổi được áp dụng +trước khi bạn gọi {@link android.app.FragmentTransaction#commit commit()} đều được thêm vào +ngăn xếp như một giao tác riêng lẻ và nút Quay lại sẽ đảo ngược tất cả cùng nhau.

+ +

Thứ tự mà bạn thêm thay đổi vào một {@link android.app.FragmentTransaction} không quan trọng, +ngoại trừ:

+
    +
  • Bạn phải gọi {@link android.app.FragmentTransaction#commit()} cuối cùng
  • +
  • Nếu bạn thêm nhiều phân đoạn vào cùng bộ chứa, khi đó thứ tự mà +bạn thêm chúng sẽ xác định thứ tự chúng xuất hiện trong phân cấp dạng xem
  • +
+ +

Nếu bạn không gọi {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()} khi thực hiện một giao tác để xóa một phân đoạn, khi đó, phân đoạn đó sẽ bị +hủy khi giao tác được thực hiện và người dùng không thể điều hướng trở lại nó. Trong khi đó, nếu bạn +gọi {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} khi +gỡ bỏ một phân đoạn, khi đó phân đoạn bị dừng và sẽ được khôi phục nếu người dùng điều hướng +trở lại.

+ +

Mẹo: Với mỗi giao tác phân đoạn, bạn có thể áp dụng một hoạt ảnh +chuyển tiếp bằng cách gọi {@link android.app.FragmentTransaction#setTransition setTransition()} trước khi +thực thi.

+ +

Việc gọi {@link android.app.FragmentTransaction#commit()} không thực hiện giao tác +ngay lập tức. Thay vào đó, nó lập lịch biểu để chạy trên luồng UI của hoạt động (luồng "chính") ngay khi +luồng có thể làm vậy. Tuy nhiên, nếu cần, bạn có thể gọi {@link +android.app.FragmentManager#executePendingTransactions()} từ luồng UI của mình để ngay lập tức thực hiện +các giao tác được gửi bởi {@link android.app.FragmentTransaction#commit()}. Làm vậy +thường không cần thiết trừ khi giao tác đó là phụ thuộc cho các tác vụ ở những luồng khác.

+ +

Chú ý: Bạn có thể thực thi một giao tác bằng cách sử dụng {@link +android.app.FragmentTransaction#commit commit()} chỉ trước khi hoạt động lưu +trạng thái của nó (khi người dùng rời khỏi hoạt động). Nếu bạn định thực thi sau thời điểm đó sẽ phát sinh một lỗi +ngoại lệ. Nguyên nhân là vì trạng thái sau khi thực thi có thể bị mất nếu hoạt động +cần được khôi phục. Đối với những trường hợp mà bạn có thể mất thực thi, hãy sử dụng {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}.

+ + + + +

Giao tiếp với Hoạt động

+ +

Mặc dù {@link android.app.Fragment} được triển khai như một đối tượng độc lập với +{@link android.app.Activity} và có thể được sử dụng bên trong nhiều hoạt động, một thực thể đã cho của +phân đoạn sẽ được gắn kết trực tiếp với hoạt động chứa nó.

+ +

Cụ thể, phân đoạn có thể truy cập thực thể {@link android.app.Activity} bằng {@link +android.app.Fragment#getActivity()} và dễ dàng thực hiện các tác vụ như tìm một dạng xem trong bố trí +hoạt động:

+ +
+View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
+
+ +

Tương tự, hoạt động của bạn có thể gọi ra các phương pháp trong phân đoạn bằng cách thu được một tham chiếu tới +{@link android.app.Fragment} từ {@link android.app.FragmentManager}, bằng cách sử dụng {@link +android.app.FragmentManager#findFragmentById findFragmentById()} hoặc {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Ví dụ:

+ +
+ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
+
+ + +

Tạo gọi lại sự kiện cho hoạt động

+ +

Trong một số trường hợp, bạn có thể cần một phân đoạn để chia sẻ sự kiện với hoạt động. Một cách hay để làm điều này +đó là định nghĩa một giao diện gọi lại bên trong phân đoạn và yêu cầu hoạt động chủ triển khai +nó. Khi hoạt động nhận được một lệnh gọi lại thông qua giao diện, nó có thể chia sẻ thông tin với +các phân đoạn khác trong bố trí nếu cần.

+ +

Ví dụ, nếu một ứng dụng tin tức có hai phân đoạn trong một hoạt động—một để hiển thị danh sách +bài viết (phân đoạn A) và một để hiển thị một bài viết (phân đoạn B)—khi đó, phân đoạn A phải thông báo với +hoạt động khi nào thì một mục danh sách được chọn để nó có thể yêu cầu phân đoạn B hiển thị bài viết đó. Trong +trường hợp này, giao diện {@code OnArticleSelectedListener} sẽ được khai báo bên trong phân đoạn A:

+ +
+public static class FragmentA extends ListFragment {
+    ...
+    // Container Activity must implement this interface
+    public interface OnArticleSelectedListener {
+        public void onArticleSelected(Uri articleUri);
+    }
+    ...
+}
+
+ +

Khi đó, hoạt động lưu trữ phân đoạn sẽ triển khai giao diện {@code OnArticleSelectedListener} +và +khống chế {@code onArticleSelected()} để thông báo với phân đoạn B về sự kiện từ phân đoạn A. Để đảm bảo +rằng hoạt động chủ triển khai giao diện này, phương pháp gọi lại {@link +android.app.Fragment#onAttach onAttach()} của phân đoạn A (mà hệ thống gọi khi thêm +phân đoạn vào hoạt động) sẽ khởi tạo một thực thể của {@code OnArticleSelectedListener} bằng cách +đổi kiểu {@link android.app.Activity} mà được chuyển vào {@link android.app.Fragment#onAttach +onAttach()}:

+ +
+public static class FragmentA extends ListFragment {
+    OnArticleSelectedListener mListener;
+    ...
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        try {
+            mListener = (OnArticleSelectedListener) activity;
+        } catch (ClassCastException e) {
+            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
+        }
+    }
+    ...
+}
+
+ +

Nếu hoạt động chưa triển khai giao diện, khi đó phân đoạn sẽ đưa ra lỗi +{@link java.lang.ClassCastException}. +Nếu thành công, thành viên {@code mListener} giữ một tham chiếu tới triển khai +{@code OnArticleSelectedListener}của hoạt động, sao cho phân đoạn A có thể chia sẻ sự kiện với hoạt động bằng cách gọi các phương pháp +được định nghĩa bởi giao diện {@code OnArticleSelectedListener}. Ví dụ, nếu phân đoạn A là một +phần mở rộng của {@link android.app.ListFragment}, mỗi lần +người dùng nhấp vào một mục danh sách, hệ thống sẽ gọi ra {@link android.app.ListFragment#onListItemClick +onListItemClick()} trong phân đoạn, và nó lại gọi {@code onArticleSelected()} để chia sẻ +sự kiện với hoạt động:

+ +
+public static class FragmentA extends ListFragment {
+    OnArticleSelectedListener mListener;
+    ...
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        // Append the clicked item's row ID with the content provider Uri
+        Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
+        // Send the event and Uri to the host activity
+        mListener.onArticleSelected(noteUri);
+    }
+    ...
+}
+
+ +

Tham số {@code id} được chuyển vào {@link +android.app.ListFragment#onListItemClick onListItemClick()} là ID hàng của mục được nhấp, +nó được sử dụng bởi hoạt động (hoặc phân đoạn kia) để tải bài viết từ {@link +android.content.ContentProvider} của ứng dụng.

+ +

Bạn có thể xem thêm thông tin về +cách sử dụng một trình cung cấp nội dung trong tài liệu Trình cung cấp Nội dung.

+ + + +

Thêm mục vào Thanh Hành động

+ +

Phân đoạn của bạn có thể đóng góp các mục menu vào Menu Tùy chọn của hoạt động (và tiếp đó là cả Thanh Hành động) bằng cách triển khai +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Tuy nhiên, để +phương pháp này nhận lệnh gọi, bạn phải gọi {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} trong khi {@link +android.app.Fragment#onCreate(Bundle) onCreate()}, để cho biết rằng phân đoạn +sẽ muốn thêm mục vào Menu Tùy chọn (nếu không, phân đoạn sẽ không nhận được lệnh gọi tới +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).

+ +

Bất kỳ mục nào mà bạn thêm vào Menu Tùy chọn sau đó từ phân đoạn đều được nối với các mục menu +hiện tại. Phân đoạn cũng nhận các lệnh gọi lại tới {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} khi một mục menu +được chọn.

+ +

Bạn cũng có thể đăng ký một dạng xem trong bố trí phân đoạn của mình để cung cấp một menu ngữ cảnh bằng cách gọi {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Khi người dùng mở +menu ngữ cảnh, phân đoạn nhận một lệnh gọi tới {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}. Khi người dùng chọn một mục, phân đoạn nhận được một lệnh gọi tới {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.

+ +

Lưu ý: Mặc dù phân đoạn của bạn nhận được một lệnh gọi khi chọn mục +đối với từng mục menu mà nó thêm, trước tiên hoạt động sẽ nhận phương pháp gọi lại tương ứng khi người dùng +chọn một mục menu. Nếu việc triển khai gọi lại khi chọn mục của hoạt động không +xử lý mục được chọn, khi đó sự kiện được chuyển sang phương pháp gọi lại của phân đoạn. Điều này đúng đối với +Menu Tùy chọn và các menu ngữ cảnh.

+ +

Để biết thêm thông tin về các menu, xem các hướng dẫn cho nhà phát triển MenuThanh Hành động.

+ + + + +

Xử lý Vòng đời của Phân đoạn

+ +
+ +

Hình 3. Ảnh hưởng của vòng đời hoạt động tới vòng đời +của phân đoạn.

+
+ +

Việc quản lý vòng đời của một phân đoạn rất giống với quản lý vòng đời của một hoạt động. Giống như +hoạt động, phân đoạn có thể tồn tại ở ba trạng thái:

+ +
+
Tiếp tục
+
Phân đoạn hiển thị trong hoạt động đang chạy.
+ +
Tạm dừng
+
Một hoạt động khác ở trong tiền cảnh và có tiêu điểm, nhưng hoạt động mà phân đoạn +này nằm trong vẫn hiển thị (hoạt động tiền cảnh mờ một phần hoặc không +che phủ toàn bộ màn hình).
+ +
Dừng
+
Phân đoạn không hiển thị. Hoặc là hoạt động chủ đã bị dừng hoặc +phân đoạn đã được gỡ bỏ khỏi hoạt động, nhưng được thêm vào ngăn xếp. Phân đoạn dừng +vẫn còn hoạt động (tất cả thông tin về trạng thái và thành viên đều được hệ thống giữ lại). Tuy nhiên, nó không còn +hiển thị với người dùng nữa và sẽ bị tắt bỏ nếu hoạt động bị tắt bỏ.
+
+ +

Cũng như một hoạt động, bạn có thể giữ lại trạng thái của một phân đoạn bằng cách sử dụng {@link +android.os.Bundle}, trong trường hợp tiến trình của hoạt động bị tắt bỏ và bạn cần khôi phục +trạng thái của phân đoạn khi hoạt động được tạo lại. Bạn có thể lưu trạng thái trong phương pháp gọi lại {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} của phân đoạn và khôi phục nó trong +hoặc {@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()}, hoặc {@link +android.app.Fragment#onActivityCreated onActivityCreated()}. Để biết thêm thông tin về việc lưu +trạng thái, xem tài liệu Hoạt động +.

+ +

Sự khác nhau quan trọng nhất trong vòng đời giữa một hoạt động và một phân đoạn đó là cách chúng +được lưu trữ trong ngăn xếp tương ứng. Hoạt động được đặt vào một ngăn xếp gồm nhiều hoạt động +, được quản lý bởi hệ thống theo mặc định khi bị dừng (sao cho người dùng có thể điều hướng lại +nó bằng nút Quay lại như được đề cập trong Tác vụ và Ngăn xếp). +Tuy nhiên, phân đoạn chỉ được đặt vào một ngăn xếp do hoạt động chủ quản lý khi bạn +yêu cầu rõ ràng rằng trường hợp đó phải được lưu bằng cách gọi {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} trong một giao tác +gỡ bỏ phân đoạn.

+ +

Nếu không thì việc quản lý vòng đời của phân đoạn rất giống với việc quản lý vòng đời +của hoạt động. Vì thế, những nội dung áp dụng cho quản lý vòng đời của +hoạt động cũng áp dụng cho phân đoạn. Tuy nhiên, việc mà bạn cũng cần phải hiểu đó là cách +vòng đời của hoạt động ảnh hưởng tới vòng đời của phân đoạn.

+ +

Chú ý: Nếu bạn cần một đối tượng {@link android.content.Context} trong +{@link android.app.Fragment}của mình, bạn có thể gọi {@link android.app.Fragment#getActivity()}. +Tuy nhiên, nhớ chỉ được gọi {@link android.app.Fragment#getActivity()} khi phân đoạn được gắn với +một hoạt động. Khi phân đoạn chưa được gắn, hoặc bị gỡ trong khi kết thúc +vòng đời của nó, {@link android.app.Fragment#getActivity()} sẽ trả về rỗng.

+ + +

Phối hợp với vòng đời của hoạt động

+ +

Vòng đời của hoạt động mà phân đoạn có ở trong đó sẽ trực tiếp ảnh hưởng tới vòng đời của phân đoạn +, sao cho mỗi lệnh gọi lại vòng đời cho hoạt động đó sẽ dẫn tới một lệnh gọi lại tương tự cho từng +phân đoạn. Ví dụ, khi hoạt động nhận được {@link android.app.Activity#onPause}, mỗi +phân đoạn trong hoạt động sẽ nhận được {@link android.app.Fragment#onPause}.

+ +

Tuy nhiên, các phân đoạn có thêm một vài lệnh gọi lại vòng đời nhằm xử lý tương tác duy nhất với +hoạt động để thực hiện các hành động như xây dựng và hủy UI của phân đoạn. Những phương pháp gọi lại +bổ sung này là:

+ +
+
{@link android.app.Fragment#onAttach onAttach()}
+
Được gọi khi phân đoạn đã được liên kết với hoạt động {@link +android.app.Activity} được chuyển ở đây).
+
{@link android.app.Fragment#onCreateView onCreateView()}
+
Được gọi khi tạo phân cấp dạng xem được liên kết với phân đoạn.
+
{@link android.app.Fragment#onActivityCreated onActivityCreated()}
+
Được gọi khi phương pháp {@link android.app.Activity#onCreate +onCreate()} của hoạt động đã trả về.
+
{@link android.app.Fragment#onDestroyView onDestroyView()}
+
Được gọi khi phân cấp dạng xem được liên kết với phân đoạn đang được gỡ bỏ.
+
{@link android.app.Fragment#onDetach onDetach()}
+
Được gọi khi phân đoạn đang được bỏ liên kết khỏi hoạt động.
+
+ +

Tiến trình vòng đời của một phân đoạn, do bị ảnh hưởng bởi hoạt động chủ của nó, được minh họa +bởi hình 3. Trong hình này, bạn có thể thấy cách thức mỗi trạng thái nối tiếp nhau của hoạt động sẽ xác định +các phương pháp gọi lại nào mà một phân đoạn có thể nhận được. Ví dụ, khi hoạt động đã nhận được lệnh gọi lại {@link +android.app.Activity#onCreate onCreate()} của nó, phân đoạn trong hoạt động sẽ nhận được không quá +lệnh gọi lại {@link android.app.Fragment#onActivityCreated onActivityCreated()}.

+ +

Sau khi hoạt động đạt trạng thái tiếp tục, bạn có thể tự do thêm và gỡ bỏ phân đoạn vào +hoạt động. Vì thế, chỉ trong khi hoạt động ở trạng thái tiếp tục thì vòng đời của một phân đoạn +mới có thể thay đổi độc lập.

+ +

Tuy nhiên, khi hoạt động rời khỏi trạng thái tiếp tục, phân đoạn lại bị hoạt động đẩy qua vòng đời +của mình.

+ + + + +

Ví dụ

+ +

Để kết hợp mọi nội dung được đề cập trong tài liệu này, sau đây là một ví dụ về hoạt động +sử dụng hai phân đoạn để tạo một bố trí hai bảng. Hoạt động bên dưới bao gồm một phân đoạn để +hiển thị danh sách các vở kịch của Shakespeare và một phân đoạn khác để hiển thị tóm tắt về vở kịch khi được chọn +từ danh sách. Nó cũng minh họa cách cung cấp các cấu hình phân đoạn khác nhau, +dựa trên cấu hình màn hình.

+ +

Lưu ý: Mã nguồn hoàn chỉnh cho hoạt động này có sẵn trong +{@code +FragmentLayout.java}.

+ +

Hoạt động chính áp dụng một bố trí theo cách thông thường, trong {@link +android.app.Activity#onCreate onCreate()}:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +

Bố trí được áp dụng là {@code fragment_layout.xml}:

+ +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +

Khi sử dụng bố trí này, hệ thống sẽ khởi tạo {@code TitlesFragment} (liệt kê tên +các vở kịch) ngay khi hoạt động nạp bố trí, trong khi {@link android.widget.FrameLayout} +(nơi sẽ xuất hiện phân đoạn hiển thị tóm tắt về vở kịch) chiếm khoảng trống phía bên phải của +màn hình, nhưng ban đầu vẫn trống. Như bạn sẽ thấy bên dưới, mãi tới khi người dùng chọn một mục +từ danh sách thì một phân đoạn mới được đặt vào {@link android.widget.FrameLayout}.

+ +

Tuy nhiên, không phải tất cả cấu hình màn hình đều đủ rộng để hiển thị cả danh sách +các vở kịch và tóm tắt bên cạnh nhau. Vì thế, bố trí trên chỉ được sử dụng cho cấu hình +màn hình khổ ngang bằng cách lưu nó dưới dạng {@code res/layout-land/fragment_layout.xml}.

+ +

Vì thế, khi màn hình hướng đứng, hệ thống sẽ áp dụng bố trí sau, nó +được lưu tại {@code res/layout/fragment_layout.xml}:

+ +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +

Bố trí này chỉ bao gồm {@code TitlesFragment}. Điều này có nghĩa là, khi thiết bị ở +hướng đứng, chỉ danh sách tên vở kịch được hiển thị. Vì thế, khi người dùng nhấp vào một +mục danh sách trong cấu hình này, ứng dụng sẽ bắt đầu một hoạt động mới để hiển thị tóm tắt, +thay vì tải một phân đoạn thứ hai.

+ +

Tiếp theo, bạn có thể thấy cách hoàn thành điều này trong các lớp phân đoạn. Đầu tiên là {@code +TitlesFragment}, hiển thị danh sách tên các vở kịch của Shakespeare. Phân đoạn này sẽ mở rộng {@link +android.app.ListFragment} và dựa vào nó để xử lý hầu hết công việc về dạng xem danh sách.

+ +

Khi bạn kiểm tra đoạn mã này, hãy để ý rằng có hai hành vi có thể khi người dùng nhấp vào một +mục danh sách: phụ thuộc vào bố trí nào trong hai bố trí đang hiện hoạt, nó có thể hoặc tạo và hiển thị một phân đoạn +mới để hiển thị chi tiết trong cùng hoạt động (thêm phân đoạn vào {@link +android.widget.FrameLayout}), hoặc bắt đầu một hoạt động mới (tại đó phân đoạn có thể được hiển thị).

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +

Phân đoạn thứ hai, {@code DetailsFragment} sẽ hiển thị tóm tắt vở kịch cho mục được chọn từ +danh sách trong {@code TitlesFragment}:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +

Nhớ lại ở lớp {@code TitlesFragment} rằng, nếu người dùng nhấp vào một mục danh sách và bố trí +hiện tại không có dạng xem {@code R.id.details} (là nơi mà +{@code DetailsFragment} thuộc về), khi đó, ứng dụng sẽ bắt đầu hoạt động {@code DetailsActivity} +để hiển thị nội dung của mục đó.

+ +

Sau đây là {@code DetailsActivity}, nó chỉ đơn thuần nhúng {@code DetailsFragment} để hiển thị +tóm tắt vở kịch được chọn khi màn hình ở hướng đứng:

+ +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +

Lưu ý rằng hoạt động này tự kết thúc nếu cấu hình là khổ ngang, sao cho hoạt động +chính có thể chiếm lấy và hiển thị {@code DetailsFragment} bên cạnh {@code TitlesFragment}. +Điều này có thể xảy ra nếu người dùng bắt đầu {@code DetailsActivity} ở dạng hướng đứng, nhưng +sau đó xoay thành khổ ngang (làm vậy sẽ bắt đầu lại hoạt động hiện tại).

+ + +

Để biết thêm mẫu sử dụng phân đoạn (và toàn bộ tệp nguồn cho ví dụ này), +hãy xem ứng dụng mẫu API Demos có sẵn trong +ApiDemos (có thể tải xuống từ Thành phần SDK Mẫu).

+ + diff --git a/docs/html-intl/intl/vi/guide/components/fundamentals.jd b/docs/html-intl/intl/vi/guide/components/fundamentals.jd new file mode 100644 index 0000000000000000000000000000000000000000..4b70140723d95e8d2352647c66b03680078d4c4d --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=Đại cương về Ứng dụng +@jd:body + + + +

Ứng dụng Android được viết bằng ngôn ngữ lập trình Java. Bộ công cụ SDK Android sẽ biên dịch +mã của bạn—cùng với bất kỳ tệp dữ liệu và tài nguyên nào—vào một APK: một gói Android, +đó là một tệp lưu trữ có hậu tố {@code .apk}. Một tệp APK chứa tất cả nội dung +của một ứng dụng Android và là tệp mà các thiết bị dựa trên nền tảng Android sử dụng để cài đặt ứng dụng.

+ +

Sau khi được cài đặt lên một thiết bị, từng ứng dụng Android sẽ ở bên trong hộp cát bảo mật của chính nó:

+ +
    +
  • Hệ điều hành Android là một hệ thống Linux đa người dùng trong đó mỗi ứng dụng là một +người dùng khác nhau.
  • + +
  • Theo mặc định, hệ thống gán cho từng ứng dụng một ID người dùng Linux duy nhất (ID chỉ được sử dụng bởi +hệ thống và không xác định đối với ứng dụng). Hệ thống sẽ đặt quyền cho tất cả tệp trong một ứng dụng +sao cho chỉ ID người dùng được gán cho ứng dụng đó mới có thể truy cập chúng.
  • + +
  • Mỗi tiến trình có máy ảo (VM) riêng của mình, vì thế mã của một ứng dụng sẽ chạy độc lập với +các ứng dụng khác.
  • + +
  • Theo mặc định, mọi ứng dụng chạy trong tiến trình Linux của chính nó. Android khởi động tiến trình khi bất kỳ +thành phần nào của ứng dụng cần được thực thi, sau đó tắt tiến trình khi không còn +cần nữa hoặc khi hệ thống phải khôi phục bộ nhớ cho các ứng dụng khác.
  • +
+ +

Bằng cách này, hệ thống Android triển khai nguyên tắc đặc quyền ít nhất. Cụ thể, +theo mặc định, mỗi ứng dụng chỉ có thể truy cập vào các thành phần mà nó cần để thực hiện công việc của mình và +không hơn. Điều này tạo ra một môi trường rất bảo mật mà trong đó một ứng dụng không thể truy cập các bộ phận của +hệ thống mà nó không được cấp quyền.

+ +

Tuy nhiên, có nhiều cách để một ứng dụng chia sẻ dữ liệu với các ứng dụng khác và để một +ứng dụng truy cập vào các dịch vụ của hệ thống:

+ +
    +
  • Có thể sắp xếp để hai ứng dụng chia sẻ cùng ID người dùng Linux, trong trường hợp đó +chúng có thể truy cập các tệp của nhau. Để tiết kiệm tài nguyên của hệ thống, các ứng dụng có +cùng ID người dùng cũng có thể sắp xếp để chạy trong cùng tiến trình Linux và chia sẻ cùng VM (các +ứng dụng cũng phải được ký bằng cùng chứng chỉ).
  • +
  • Một ứng dụng có thể yêu cầu quyền truy cập dữ liệu của thiết bị chẳng hạn như +danh bạ của người dùng, tin nhắn SMS, thiết bị lưu trữ gắn được (thẻ SD), máy ảnh, Bluetooth và nhiều nữa. Tất cả +quyền ứng dụng đều phải được cấp bởi người dùng tại thời điểm cài đặt.
  • +
+ +

Đó là nội dung cơ bản về cách mà một ứng dụng Android tồn tại trong hệ thống. Phần còn lại của +tài liệu này giới thiệu với bạn về:

+
    +
  • Các thành phần khuôn khổ cốt lõi định nghĩa ứng dụng của bạn.
  • +
  • Tệp bản kê khai mà trong đó bạn khai báo các thành phần và tính năng yêu cầu của thiết bị cho ứng dụng +của bạn.
  • +
  • Các tài nguyên tách riêng với mã ứng dụng và cho phép ứng dụng của bạn +tối ưu hóa hành vi của nó cho nhiều loại cấu hình thiết bị đa dạng.
  • +
+ + + +

Thành phần của Ứng dụng

+ +

Thành phần của ứng dụng là những khối dựng thiết yếu của một ứng dụng Android. Mỗi +thành phần là một điểm khác nhau mà qua đó hệ thống có thể vào ứng dụng của bạn. Không phải tất cả +thành phần đều là các điểm nhập thực tế cho người dùng và một số phụ thuộc vào nhau, nhưng mỗi thành phần tồn tại +như một thực thể riêng và đóng một vai trò riêng—mỗi thành phần là một khối dựng duy nhất +giúp định nghĩa hành vi chung của ứng dụng của bạn.

+ +

Có bốn loại thành phần ứng dụng khác nhau. Mỗi loại có một mục đích riêng +và có một vòng đời riêng, xác định cách thành phần được tạo lập và hủy.

+ +

Sau đây là bốn loại thành phần ứng dụng:

+ +
+ +
Hoạt động
+ +
Một hoạt động biểu diễn một màn hình đơn với một giao diện người dùng. Ví dụ, +một ứng dụng e-mail có thể có một hoạt động với chức năng hiển thị một danh sách +e-mail mới, một hoạt động khác để soạn e-mail, và một hoạt động khác để đọc e-mail. Mặc dù +các hoạt động cùng nhau tạo thành một trải nghiệm người dùng gắn kết trong ứng dụng e-mail, mỗi hoạt động +lại độc lập với nhau. Như vậy, một ứng dụng khác có thể khởi động bất kỳ hoạt động nào +trong số này (nếu ứng dụng e-mail cho phép nó). Ví dụ, một ứng dụng máy ảnh có thể khởi động +hoạt động trong ứng dụng e-mail có chức năng soạn thư mới, để người dùng chia sẻ một bức ảnh. + +

Hoạt động được triển khai như một lớp con của {@link android.app.Activity} và bạn có thể tìm hiểu thêm +về nó trong hướng dẫn dành cho nhà phát triển Hoạt động +.

+
+ + +
Dịch vụ
+ +
Một dịch vụ là một thành phần chạy ngầm để thực hiện các thao tác +chạy lâu hoặc để thực hiện công việc cho các tiến trình từ xa. Dịch vụ +không cung cấp giao diện người dùng. Ví dụ, một dịch vụ có thể phát nhạc dưới nền trong khi +người dùng đang ở một ứng dụng khác, hoặc nó có thể tải dữ liệu qua mạng mà không +chặn người dùng tương tác với hoạt động. Một thành phần khác, chẳng hạn như một hoạt động, có thể khởi động +dịch vụ và để nó chạy hoặc gắn kết với nó để tương tác với nó. + +

Dịch vụ được triển khai như một lớp con của {@link android.app.Service} và bạn có thể tìm hiểu +thêm về nó trong hướng dẫn cho nhà phát triển Dịch vụ +.

+
+ + +
Trình cung cấp Nội dung
+ +
Một trình cung cấp nội dung sẽ quản lý một tập dữ liệu ứng dụng được chia sẻ. Bạn có thể lưu trữ dữ liệu trong +hệ thống tệp, một cơ sở dữ liệu SQLite, trên web, hay bất kỳ vị trí lưu trữ liên tục nào khác mà +ứng dụng của bạn có thể truy cập. Thông qua trình cung cấp nội dung, các ứng dụng khác có thể truy vấn hay thậm chí sửa đổi +dữ liệu (nếu trình cung cấp nội dung cho phép). Ví dụ, hệ thống Android cung cấp một trình cung cấp +nội dung có chức năng quản lý thông tin danh bạ của người dùng. Như vậy, bất kỳ ứng dụng nào có các quyền +phù hợp đều có thể truy vấn bất kỳ phần nào của trình cung cấp nội dung (chẳng hạn như {@link +android.provider.ContactsContract.Data}) để đọc và ghi thông tin về một người cụ thể. + +

Trình cung cấp nội dung cũng hữu ích với việc đọc và ghi dữ liệu riêng tư đối với +ứng dụng của bạn và không được chia sẻ. Ví dụ, ứng dụng mẫu Note Pad sử dụng một +trình cung cấp nội dung để lưu các ghi chú.

+ +

Trình cung cấp nội dung được triển khai như một lớp con của {@link android.content.ContentProvider} +và phải triển khai một tập các API tiêu chuẩn cho phép các ứng dụng khác thực hiện +giao tác. Để biết thêm thông tin, xem hướng dẫn cho nhà phát triển Trình cung cấp Nội dung +.

+
+ + +
Hàm nhận quảng bá
+ +
Một hàm nhận quảng bá (broadcast receiver) là một thành phần có chức năng hồi đáp lại các thông báo +quảng bá trên toàn hệ thống. Nhiều quảng bá khởi nguồn từ hệ thống—ví dụ, một quảng bá thông báo +rằng màn hình đã tắt, pin yếu, hoặc một bức ảnh được chụp. +Các ứng dụng cũng có thể khởi tạo quảng bá—ví dụ như để các ứng dụng khác biết rằng +một phần dữ liệu đã được tải xuống thiết bị và có sẵn để họ sử dụng. Mặc dù các hàm nhận quảng bá +không hiển thị giao diện người dùng, chúng có thể tạo một thông báo thanh trạng thái +để cảnh báo người tiếp nhận khi xảy ra một sự kiện quảng bá. Tuy nhiên trường hợp phổ biến hơn đó là hàm nhận quảng bá chỉ +là một "cổng kết nối" tới các thành phần khác và nhằm mục đích thực hiện lượng công việc rất ít. Ví +dụ, nó có thể khởi tạo một dịch vụ để thực hiện một số công việc dựa trên sự kiện. + +

Hàm nhận quảng bá được triển khai như một lớp con của {@link android.content.BroadcastReceiver} +và mỗi quảng bá được chuyển giao như một đối tượng {@link android.content.Intent}. Để biết thêm thông tin, +hãy xem lớp {@link android.content.BroadcastReceiver}.

+
+ +
+ + + +

Một khía cạnh độc đáo trong thiết kế hệ thống Android đó là bất kỳ ứng dụng nào cũng có thể khởi động một thành phần của +ứng dụng khác. Ví dụ, nếu bạn muốn người dùng chụp +ảnh bằng máy ảnh của thiết bị, có thể có một ứng dụng khác có chức năng đó và +ứng dụng của bạn có thể sử dụng nó thay vì phát triển một hoạt động để tự chụp ảnh. Bạn không +cần tích hợp hay thậm chí là liên kết với mã từ ứng dụng của máy ảnh. +Thay vào đó, bạn đơn giản có thể khởi động hoạt động đó trong ứng dụng máy ảnh có chức năng +chụp ảnh. Khi hoàn thành, ảnh thậm chí được trả về ứng dụng của bạn để bạn có thể sử dụng nó. Đối với người dùng, +có vẻ như máy ảnh là một bộ phận thực sự trong ứng dụng của bạn.

+ +

Khi hệ thống khởi động một thành phần, nó sẽ khởi động tiến trình cho ứng dụng đó (nếu tiến trình không +đang chạy) và khởi tạo các lớp cần thiết cho thành phần. Ví dụ, nếu ứng dụng +của bạn khởi động hoạt động trong ứng dụng máy ảnh có chức năng chụp ảnh, hoạt động đó +sẽ chạy trong tiến trình thuộc về ứng dụng máy ảnh chứ không chạy trong tiến trình của ứng dụng của bạn. +Vì thế, không như ứng dụng trên hầu hết các hệ thống khác, ứng dụng Android không có một điểm nhập +duy nhất (ví dụ, không có chức năng {@code main()}).

+ +

Vì hệ thống chạy từng ứng dụng trong một tiến trình riêng với các quyền của tệp mà +hạn chế truy cập vào các ứng dụng khác, ứng dụng của bạn không thể trực tiếp kích hoạt một thành phần từ +một ứng dụng khác. Tuy nhiên, hệ thống Android có thể. Vì thế, để kích hoạt một thành phần trong +một ứng dụng khác, bạn phải chuyển giao một thông báo tới hệ thống trong đó nêu rõ ý định của bạn để +khởi động một thành phần cụ thể. Sau đó, hệ thống sẽ kích hoạt thành phần cho bạn.

+ + +

Kích hoạt Thành phần

+ +

Ba trong bốn loại thành phần—hoạt động, dịch vụ và +hàm nhận quảng bá—sẽ được kích hoạt bằng một thông báo không đồng bộ gọi là ý định. +Ý định sẽ gắn kết từng thành phần với nhau vào thời gian chạy (bạn có thể nghĩ chúng như là +các hàm nhắn tin có chức năng yêu cầu một hành động từ các thành phần khác), dù thành phần đó thuộc +về ứng dụng của bạn hay ứng dụng khác.

+ +

Một ý định được tạo thành bằng một đối tượng {@link android.content.Intent}, nó định nghĩa một thông báo để +kích hoạt một thành phần cụ thể hoặc một loại thành phần cụ thể—tương ứng, một ý định +có thể biểu thị hoặc không biểu thị.

+ +

Đối với các hoạt động và dịch vụ, ý định có chức năng định nghĩa một hành động sẽ thực hiện (ví dụ, "xem" hoặc +"gửi" gì đó) và có thể chỉ định URI của dữ liệu để hành động dựa trên đó (ngoài những điều khác mà +thành phần được khởi động có thể cần biết). Ví dụ, một ý định có thể truyền tải một yêu cầu +để một hoạt động hiển thị một hình ảnh hay mở một trang web. Trong một số trường hợp, bạn có thể khởi động một +hoạt động để nhận kết quả, trong trường hợp đó, hoạt động cũng trả về +kết quả trong một {@link android.content.Intent} (ví dụ, bạn có thể phát hành một ý định để cho phép +người dùng chọn một liên lạc cá nhân và yêu cầu trả nó về cho bạn—ý định trả về bao gồm một +URI chỉ đến liên lạc được chọn).

+ +

Đối với hàm nhận quảng bá, ý định chỉ định nghĩa +thông báo đang được quảng bá (ví dụ, một quảng bá để báo rằng pin của thiết bị yếu +sẽ chỉ bao gồm một xâu hành động chỉ báo rằng "pin yếu").

+ +

Loại thành phần còn lại, trình cung cấp nội dung, không được kích hoạt bởi ý định. Thay vào đó, nó được +kích hoạt khi được nhằm tới bởi một yêu cầu từ một {@link android.content.ContentResolver}. Bộ giải quyết +nội dung xử lý tất cả giao tác trực tiếp với trình cung cấp nội dung sao cho thành phần mà +đang thực hiện giao tác với trình cung cấp sẽ không cần mà thay vào đó gọi các phương pháp trên đối tượng {@link +android.content.ContentResolver}. Điều này để lại một lớp tóm tắt giữa trình cung cấp +nội dung và thành phần yêu cầu thông tin (để bảo mật).

+ +

Có các phương pháp riêng để kích hoạt từng loại thành phần:

+
    +
  • Bạn có thể khởi động một hoạt động (hoặc giao cho nó việc gì mới để làm) bằng cách +chuyển một {@link android.content.Intent} đến {@link android.content.Context#startActivity +startActivity()} hoặc {@link android.app.Activity#startActivityForResult startActivityForResult()} +(khi bạn muốn hoạt động trả về một kết quả).
  • +
  • Bạn có thể khởi động một dịch vụ (hoặc gửi chỉ dẫn mới tới một dịch vụ đang diễn ra) bằng cách +chuyển một {@link android.content.Intent} đến {@link android.content.Context#startService +startService()}. Hoặc bạn có thể gắn kết với dịch vụ bằng cách chuyển một {@link android.content.Intent} đến +{@link android.content.Context#bindService bindService()}.
  • +
  • Bạn có thể khởi tạo một quảng bá bằng cách chuyển {@link android.content.Intent} tới các phương pháp như +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()}, hoặc {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.
  • +
  • Bạn có thể thực hiện một truy vấn tới một trình cung cấp nội dung bằng cách gọi {@link +android.content.ContentProvider#query query()} trên một {@link android.content.ContentResolver}.
  • +
+ +

Để biết thêm thông tin về việc sử dụng ý định, hãy xem tài liệu Ý định và Bộ lọc +Ý định. Bạn cũng có thể xem thêm thông tin về việc kích hoạt các thành phần cụ thể +trong những tài liệu sau: Hoạt động, Dịch vụ, {@link +android.content.BroadcastReceiver} và Trình cung cấp Nội dung.

+ + +

Tệp Bản kê khai

+ +

Trước khi hệ thống Android có thể khởi động một thành phần ứng dụng, hệ thống phải biết rằng +thành phần đó tồn tại bằng cách đọc tệp {@code AndroidManifest.xml} của ứng dụng (tệp +"bản kê khai"). Ứng dụng của bạn phải khai báo tất cả thành phần của nó trong tệp này, nó phải nằm ở gốc của +thư mục dự án của ứng dụng.

+ +

Bản kê khai làm nhiều việc bên cạnh việc khai báo các thành phần của ứng dụng, +chẳng hạn như:

+
    +
  • Xác định bất kỳ quyền của người dùng nào mà ứng dụng yêu cầu, chẳng hạn như truy cập Internet hay +truy cập đọc vào danh bạ của người dùng.
  • +
  • Khai báo Mức API +tối thiểu mà ứng dụng yêu cầu dựa trên những API mà ứng dụng sử dụng.
  • +
  • Khai báo các tính năng phần cứng và phần mềm được sử dụng hoặc yêu cầu bởi ứng dụng, chẳng hạn như máy ảnh, +dịch vụ Bluetooth, hoặc màn hình cảm ứng đa điểm.
  • +
  • Các thư viện API mà ứng dụng cần được liên kết với (ngoài các API khuôn khổ +Android), chẳng hạn như thư viện Google Maps +.
  • +
  • Và hơn thế nữa
  • +
+ + +

Khai báo các thành phần

+ +

Nhiệm vụ chính của bản kê khai là thông báo cho hệ thống về các thành phần của ứng dụng. Ví +dụ, một tệp bản kê khai có thể khai báo một hoạt động như sau:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<manifest ... >
+    <application android:icon="@drawable/app_icon.png" ... >
+        <activity android:name="com.example.project.ExampleActivity"
+                  android:label="@string/example_label" ... >
+        </activity>
+        ...
+    </application>
+</manifest>
+ +

Trong phần tử <application> +, thuộc tính {@code android:icon} sẽ trỏ đến các tài nguyên cho một biểu tượng có chức năng nhận biết +ứng dụng.

+ +

Trong phần tử <activity>, +thuộc tính {@code android:name} quy định tên lớp hoàn toàn đủ tiêu chuẩn của lớp con {@link +android.app.Activity} và các thuộc tính {@code android:label} quy định một xâu +để sử dụng làm nhãn hiển thị với người dùng đối với hoạt động.

+ +

Bạn phải khai báo tất cả thành phần của ứng dụng như sau:

+
    +
  • Các phần tử <activity> +cho hoạt động
  • +
  • Các phần tử <service> cho +dịch vụ
  • +
  • Các phần tử <receiver> +cho hàm nhận quảng bá
  • +
  • Các phần tử <provider> +cho trình cung cấp nội dung
  • +
+ +

Các hoạt động, dịch vụ và trình cung cấp nội dung mà bạn bao gồm trong nguồn của mình nhưng không khai báo +trong bản kê khai sẽ không hiển thị với hệ thống và hệ quả là không bao giờ chạy được. Tuy nhiên, +hàm nhận +quảng bá có thể hoặc được khai báo trong bản kê khai hoặc được tạo linh hoạt trong mã (dạng đối tượng +{@link android.content.BroadcastReceiver}) và được đăng ký với hệ thống bằng cách gọi +{@link android.content.Context#registerReceiver registerReceiver()}.

+ +

Để tìm hiểu thêm về cách cấu trúc tệp bản kê khai cho ứng dụng của mình, hãy xem tài liệu Tệp AndroidManifest.xml +.

+ + + +

Khai báo các khả năng của thành phần

+ +

Như đã nêu bên trên trong phần Kích hoạt các Thành phần, bạn có thể sử dụng một +{@link android.content.Intent} để khởi động các hoạt động, dịch vụ và hàm nhận quảng bá. Bạn có thể làm vậy bằng cách +công khai chỉ định thành phần đích (sử dụng tên lớp thành phần) trong ý định. Tuy nhiên, +sức mạnh thực sự của ý định nằm trong khái niệm ý định không biểu thị. Ý định không biểu thị +đơn thuần mô tả kiểu hành động cần thực hiện (và có thể có cả dữ liệu mà bạn muốn +thực hiện hành động) và cho phép hệ thống tìm một thành phần trên thiết bị có khả năng thực hiện +hành động và khởi động nó. Nếu có nhiều thành phần có thể thực hiện hành động được mô tả bởi +ý định, khi đó người dùng chọn ý định sẽ sử dụng.

+ +

Cách hệ thống nhận biết các thành phần có khả năng hồi đáp lại một ý định là bằng cách so sánh +ý định nhận được với các bộ lọc ý định được cung cấp trong tệp bản kê khai của các ứng dụng khác trên +thiết bị.

+ +

Khi bạn khai báo một hoạt động trong bản kê khai ứng dụng của mình, bạn có thể tùy chọn bao gồm +các bộ lọc ý định có chức năng khai báo các khả năng của hoạt động sao cho nó có thể hồi đáp lại ý định +từ các ứng dụng khác. Bạn có thể khai báo một bộ lọc ý định cho thành phần của mình bằng cách +thêm một phần tử {@code +<intent-filter>} làm con của phần tử công khai của thành phần đó.

+ +

Ví dụ, nếu bạn đã xây dựng một ứng dụng e-mail có một hoạt động soạn e-mail mới, bạn có thể +khai báo bộ lọc ý định đó để trả lời các ý định "gửi" (để gửi một e-mail mới) như sau:

+
+<manifest ... >
+    ...
+    <application ... >
+        <activity android:name="com.example.project.ComposeEmailActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <data android:type="*/*" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
+
+ +

Sau đó, nếu một ứng dụng khác tạo một ý định với hành động {@link +android.content.Intent#ACTION_SEND} và chuyển nó cho {@link android.app.Activity#startActivity +startActivity()}, hệ thống có thể khởi động hoạt động của bạn để người dùng có thể soạn thảo và gửi một +e-mail.

+ +

Để tìm hiểu thêm về việc tạo các bộ lọc ý định, hãy xem tài liệu Ý định và Bộ lọc Ý định. +

+ + + +

Khai báo các yêu cầu của ứng dụng

+ +

Có nhiều loại thiết bị dựa trên nền tảng Android và không phải tất cả chúng đều cung cấp +các tính năng và khả năng như nhau. Để tránh việc ứng dụng của bạn bị cài đặt trên các thiết bị +thiếu những tính năng mà ứng dụng của bạn cần, điều quan trọng là bạn phải định nghĩa rõ ràng một hồ sơ cho +các kiểu thiết bị mà ứng dụng của bạn hỗ trợ bằng cách khai báo các yêu cầu về thiết bị và phần mềm trong tệp +bản kê khai của mình. Hầu hết những khai báo này đều chỉ mang tính chất thông báo và hệ thống không đọc +chúng, nhưng các dịch vụ bên ngoài như Google Play thì có đọc để cung cấp tính năng lọc +cho người dùng khi họ tìm kiếm ứng dụng từ thiết bị của mình.

+ +

Ví dụ, nếu ứng dụng của bạn yêu cầu máy ảnh và sử dụng các API được giới thiệu trong Android 2.1 (API Mức 7), +bạn cần khai báo những điều này như yêu cầu trong tệp bản kê khai của mình như sau:

+ +
+<manifest ... >
+    <uses-feature android:name="android.hardware.camera.any"
+                  android:required="true" />
+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
+    ...
+</manifest>
+
+ +

Lúc này, những thiết bị mà không có máy ảnh và có một phiên bản +Android thấp hơn 2.1 sẽ không thể cài đặt ứng dụng của bạn từ Google Play.

+ +

Tuy nhiên, bạn cũng có thể khai báo rằng ứng dụng của bạn sử dụng máy ảnh, nhưng không +yêu cầu nó. Trong trường hợp đó, ứng dụng của bạn phải đặt thuộc tính {@code required} +thành {@code "false"} và kiểm tra tại thời gian chạy xem +thiết bị có máy ảnh không và vô hiệu hóa bất kỳ tính năng máy ảnh nào cho phù hợp.

+ +

Bạn có thể tìm hiểu thêm thông tin về cách bạn có thể quản lý tính tương thích của ứng dụng của bạn với các thiết bị khác nhau +trong tài liệu Tính tương thích với Thiết bị +.

+ + + +

Tài nguyên Ứng dụng

+ +

Một ứng dụng Android được soạn không chỉ có mã—nó còn yêu cầu các tài nguyên +tách riêng với mã nguồn, chẳng hạn như hình ảnh, tệp âm thanh và bất kỳ thứ gì liên quan tới trình chiếu +trực quan của ứng dụng. Ví dụ, bạn nên định nghĩa các hoạt cảnh, menu, kiểu, màu sắc, +và bố trí của giao diện người dùng của hoạt động bằng các tệp XML. Việc sử dụng các tài nguyên ứng dụng giúp dễ dàng +cập nhật các đặc điểm khác nhau trong ứng dụng của bạn mà không sửa đổi mã và—bằng cách cung cấp +các tập hợp tài nguyên thay thế—cho phép bạn tối ưu hóa ứng dụng của mình cho nhiều loại +cấu hình thiết bị (chẳng hạn như ngôn ngữ và kích cỡ màn hình khác nhau).

+ +

Đối với mọi tài nguyên mà bạn bao gồm trong dự án Android của mình, bộ công cụ xây dựng SDK định nghĩa một ID số nguyên +duy nhất mà bạn có thể sử dụng để tham chiếu tài nguyên từ mã ứng dụng của mình hoặc từ +các tài nguyên khác được định nghĩa trong XML. Ví dụ, nếu ứng dụng của bạn chứa một tệp hình ảnh có tên {@code +logo.png} (được lưu trong thư mục {@code res/drawable/}), bộ công cụ SDK sẽ khởi tạo một ID tài nguyên +đặt tên là {@code R.drawable.logo} mà bạn có thể sử dụng để tham chiếu hình ảnh và chèn nó vào trong giao diện người dùng +của mình.

+ +

Một trong những khía cạnh quan trọng nhất của việc cung cấp tài nguyên tách riêng với mã nguồn của bạn +là khả năng cho phép bạn cung cấp các tài nguyên thay thế cho các +cấu hình thiết bị khác nhau. Ví dụ, bằng cách định nghĩa các xâu UI trong XML, bạn có thể biên dịch xâu sang +các ngôn ngữ khác và lưu các xâu đó vào tệp riêng. Sau đó, dựa vào một hạn định ngôn ngữ +mà bạn nối với tên của thư mục tài nguyên (chẳng hạn như {@code res/values-fr/} đối với các giá trị xâu +tiếng Pháp) và thiết đặt ngôn ngữ của người dùng, hệ thống Android sẽ áp dụng các xâu ngôn ngữ phù hợp +cho UI của bạn.

+ +

Android hỗ trợ nhiều hạn định khác nhau cho các tài nguyên thay thế của bạn. Hạn định +là một xâu ngắn mà bạn bao gồm trong tên của các thư mục tài nguyên của mình nhằm +định nghĩa cấu hình thiết bị cho những tài nguyên đó nên được sử dụng. Lấy một +ví dụ khác, bạn nên thường xuyên tạo các bố trí khác nhau cho hoạt động của mình, tùy vào hướng và kích cỡ +màn hình của thiết bị. Ví dụ, khi màn hình thiết bị ở hướng +đứng (cao), bạn có thể muốn một bố trí có các nút thẳng đứng, nhưng khi màn hình ở hướng +khổ ngang (rộng), các nút nên được căn ngang. Để thay đổi bố trí +tùy vào hướng, bạn có thể định nghĩa hai bố trí khác nhau và áp dụng hạn định +phù hợp cho tên thư mục của từng bố trí. Sau đó, hệ thống sẽ tự động áp dụng bố trí +phù hợp tùy thuộc vào hướng hiện tại của thiết bị.

+ +

Để biết thêm thông tin về các loại tài nguyên khác nhau mà bạn có thể bao gồm trong ứng dụng của mình và cách +tạo các tài nguyên thay thế cho những cấu hình thiết bị khác nhau, hãy đọc Cung cấp Tài nguyên.

+ + + +
+
+

Tiếp tục đọc về:

+
+
Ý định và Bộ lọc Ý định +
+
Thông tin về cách sử dụng các API {@link android.content.Intent} để + kích hoạt các thành phần của ứng dụng, chẳng hạn như hoạt động và dịch vụ, và cách tạo các thành phần cho ứng dụng của bạn + có sẵn để cho các ứng dụng khác sử dụng.
+
Hoạt động
+
Thông tin về cách tạo một thực thể của lớp {@link android.app.Activity}, + có chức năng cung cấp một màn hình riêng trong ứng dụng của bạn với một giao diện người dùng.
+
Cung cấp Tài nguyên
+
Thông tin về cách các ứng dụng Android được cấu trúc để tách riêng các tài nguyên ứng dụng khỏi + mã ứng dụng, bao gồm cách bạn có thể cung cấp các tài nguyên thay thế cho những + cấu hình thiết bị cụ thể. +
+
+
+
+

Bạn cũng có thể quan tâm tới:

+
+
Tính tương thích của Thiết bị
+
Thông tin về Android hoạt động trên các loại thiết bị khác nhau và giới thiệu + về cách bạn có thể tối ưu hóa ứng dụng của mình cho từng thiết bị hoặc hạn chế tính sẵn có của ứng dụng của bạn + đối với các thiết bị khác nhau.
+
Quyền của Hệ thống
+
Thông tin về cách Android hạn chế truy cập của ứng dụng vào một số API nhất định bằng một hệ thống + quyền cần có sự đồng ý của người dùng cho phép ứng dụng của bạn có thể sử dụng các API đó.
+
+
+
+ diff --git a/docs/html-intl/intl/vi/guide/components/index.jd b/docs/html-intl/intl/vi/guide/components/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..966597d999c20cb89afb91b2f9fc86486d6d7a60 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=Thành phần Ứng dụng +page.landing=true +page.landing.intro=Khuôn khổ ứng dụng của Android cho phép bạn tạo lập nhiều ứng dụng đa dạng và sáng tạo bằng cách sử dụng một tập hợp các thành phần có thể tái sử dụng. Phần này giải thích cách bạn có thể xây dựng các thành phần định nghĩa các khối dựng cho ứng dụng của mình và cách kết nối chúng với nhau bằng cách sử dụng ý định. +page.metaDescription=Khuôn khổ ứng dụng của Android cho phép bạn tạo lập nhiều ứng dụng đa dạng và sáng tạo bằng cách sử dụng một tập hợp các thành phần có thể tái sử dụng. Phần này giải thích cách bạn có thể xây dựng các thành phần định nghĩa các khối dựng cho ứng dụng của mình và cách kết nối chúng với nhau bằng cách sử dụng ý định. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +
+ + + + + +
diff --git a/docs/html-intl/intl/vi/guide/components/intents-filters.jd b/docs/html-intl/intl/vi/guide/components/intents-filters.jd new file mode 100644 index 0000000000000000000000000000000000000000..cdc623f8e8ce93efd553d20db62992eb2d5dc041 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Ý định và Bộ lọc Ý định +page.tags="IntentFilter" +@jd:body + + + + + + +

{@link android.content.Intent} là một đối tượng nhắn tin mà bạn có thể sử dụng để yêu cầu một hành động +từ một thành phần ứng dụng khác. +Mặc dù các ý định sẽ tạo điều kiện cho giao tiếp giữa các thành phần bằng một vài cách, có ba +trường hợp sử dụng cơ bản:

+ +
    +
  • Để bắt đầu một hoạt động: +

    {@link android.app.Activity} biểu diễn một màn hình đơn trong một ứng dụng. Bạn có thể bắt đầu một thực thể +mới của một {@link android.app.Activity} bằng cách chuyển {@link android.content.Intent} +sang {@link android.content.Context#startActivity startActivity()}. {@link android.content.Intent} +mô tả hoạt động cần bắt đầu và mang theo mọi dữ liệu cần thiết.

    + +

    Nếu bạn muốn nhận một kết quả từ hoạt động khi nó hoàn thành, +hãy gọi {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Hoạt động của bạn nhận được kết quả +dưới dạng một đối tượng {@link android.content.Intent} riêng biệt trong lệnh gọi lại {@link +android.app.Activity#onActivityResult onActivityResult()} của hoạt động của bạn. +Để biết thêm thông tin, hãy xem hướng dẫn Hoạt động.

  • + +
  • Để bắt đầu một dịch vụ: +

    {@link android.app.Service} là một thành phần có chức năng thực hiện các thao tác dưới nền +mà không cần giao diện người dùng. Bạn có thể bắt đầu một dịch vụ để thực hiện một thao tác một lần +(chẳng hạn như tải xuống một tệp) bằng cách chuyển {@link android.content.Intent} +tới {@link android.content.Context#startService startService()}. {@link android.content.Intent} +mô tả dịch vụ cần bắt đầu và mang theo mọi dữ liệu cần thiết.

    + +

    Nếu dịch vụ được thiết kế với một giao diện máy khách-máy chủ, bạn có thể gắn kết với dịch vụ +từ một thành phần khác bằng cách chuyển {@link android.content.Intent} sang {@link +android.content.Context#bindService bindService()}. Để biết thêm thông tin, hãy xem hướng dẫn Dịch vụ.

  • + +
  • Để chuyển một quảng bá: +

    Quảng bá là một tin nhắn mà bất kỳ ứng dụng nào cũng có thể nhận được. Hệ thống sẽ chuyển các quảng bá +khác nhau tới các sự kiện hệ thống, chẳng hạn như khi hệ thống khởi động hoặc thiết bị bắt đầu sạc. +Bạn có thể chuyển một quảng bá tới các ứng dụng khác bằng cách chuyển một {@link android.content.Intent} +tới {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()}, hoặc {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.

    +
  • +
+ + + + +

Các Loại Ý định

+ +

Có hai loại ý định:

+ +
    +
  • Ý định biểu thị quy định thành phần cần bắt đầu theo tên (tên lớp hoàn toàn đạt tiêu chuẩn +). Thường bạn sẽ sử dụng một ý định biểu thị để bắt đầu một thành phần trong +ứng dụng của chính mình, vì bạn biết tên lớp của hoạt động hay dịch vụ mà mình muốn bắt đầu. Ví dụ +, bắt đầu một hoạt động mới để hồi đáp một hành động của người dùng hay bắt đầu một dịch vụ để tải xuống +tệp dưới nền.
  • + +
  • Ý định không biểu thị không chỉ định một thành phần cụ thể mà thay vào đó, sẽ khai báo một hành động thông thường +cần thực hiện, cho phép một thành phần từ một ứng dụng khác xử lý nó. Ví dụ, nếu bạn muốn +hiển thị cho người dùng một vị trí trên bản đồ, bạn có thể sử dụng một ý định không biểu thị để yêu cầu một ứng dụng +có khả năng khác hiển thị một vị trí được chỉ định trên bản đồ.
  • +
+ +

Khi bạn tạo một ý định biểu thị để bắt đầu một hoạt động hoặc dịch vụ, hệ thống ngay lập tức +sẽ bắt đầu thành phần ứng dụng được quy định trong đối tượng {@link android.content.Intent}.

+ +
+ +

Hình 1. Minh họa về cách một ý định không biểu thị được +chuyển thông qua hệ thống để bắt đầu một hoạt động khác: [1] Hoạt động A tạo một +{@link android.content.Intent} bằng một mô tả hành động và chuyển nó cho {@link +android.content.Context#startActivity startActivity()}. [2] Hệ thống Android tìm kiếm tất cả +ứng dụng xem có một bộ lọc ý định khớp với ý định đó không. Khi tìm thấy kết quả khớp, [3] hệ thống +sẽ bắt đầu hoạt động so khớp đó (Hoạt động B) bằng cách gọi ra phương pháp {@link +android.app.Activity#onCreate onCreate()} và chuyển nó cho {@link android.content.Intent}. +

+
+ +

Khi bạn tạo một ý định không biểu thị, hệ thống Android sẽ tìm kiếm thành phần phù hợp để bắt đầu +bằng cách so sánh nội dung của ý định với các bộ lọc ý định được khai báo trong tệp bản kê khai của các ứng dụng khác trên +thiết bị. Nếu ý định khớp với một bộ lọc ý định, hệ thống sẽ bắt đầu thành phần đó và chuyển cho nó +đối tượng {@link android.content.Intent}. Nếu có nhiều bộ lọc ý định tương thích, hệ thống +sẽ hiển thị một hộp thoại để người dùng có thể chọn ứng dụng sẽ sử dụng.

+ +

Bộ lọc ý định là một biểu thức trong tệp bản kê khai của một ứng dụng, có chức năng +chỉ định loại ý định mà thành phần +muốn nhận. Ví dụ, bằng cách khai báo một bộ lọc ý định cho một hoạt động, +bạn giúp các ứng dụng khác có thể trực tiếp bắt đầu hoạt động của mình với một loại ý định nhất định. +Tương tự, nếu bạn không khai báo bất kỳ bộ lọc ý định nào cho một hoạt động, khi đó nó chỉ có thể +được bắt đầu bằng một ý định biểu thị.

+ +

Chú ý: Để đảm bảo ứng dụng của bạn được bảo mật, luôn sử dụng một ý định +biểu thị khi bắt đầu một {@link android.app.Service} và không được +khai báo bộ lọc ý định cho các dịch vụ của bạn. Việc sử dụng một ý định không biểu thị để bắt đầu một dịch vụ sẽ là một nguy cơ +về bảo mật vì bạn không thể chắc chắn dịch vụ nào sẽ hồi đáp ý định đó, +và người dùng không thể thấy dịch vụ nào bắt đầu. Bắt đầu với Android 5.0 (API mức 21), hệ thống +sẽ đưa ra lỗi ngoại lệ nếu bạn gọi {@link android.content.Context#bindService bindService()} +bằng một ý định không biểu thị.

+ + + + + +

Xây dựng một Ý định

+ +

Đối tượng {@link android.content.Intent} mang thông tin mà hệ thống Android sử dụng +để xác định thành phần nào sẽ bắt đầu (chẳng hạn như tên thành phần chính xác hoặc thể loại +thành phần mà sẽ nhận ý định), cộng với thông tin mà thành phần nhận sử dụng để +thực hiện hành động cho phù hợp (chẳng hạn như hành động sẽ thực hiện và dữ liệu để dựa vào đó mà thực hiện).

+ + +

Thông tin chính chứa trong một {@link android.content.Intent} như sau:

+ +
+ +
Tên thành phần
+
Tên của thành phần sẽ bắt đầu. + +

Nội dung này không bắt buộc, nhưng đó là một thông tin trọng yếu để khiến một ý định trở nên +biểu thị, có nghĩa là ý định nên chỉ được chuyển tới thành phần ứng dụng +được xác định bởi tên thành phần đó. Nếu thiếu một tên thành phần, ý định trở thành không biểu thị và hệ thống +sẽ quyết định thành phần nào nhận ý định đó dựa trên các thông tin còn lại của ý định +(chẳng hạn như hành động, dữ liệu và thể loại—được mô tả bên dưới). Vì vậy, nếu bạn bắt đầu một thành phần +cụ thể trong ứng dụng của mình, bạn nên chỉ định tên thành phần.

+ +

Lưu ý: Khi bắt đầu một {@link android.app.Service}, bạn nên +luôn chỉ định tên thành phần. Nếu không, bạn không thể chắc chắn dịch vụ nào +sẽ hồi đáp ý định và người dùng không thể thấy dịch vụ nào bắt đầu.

+ +

Trường này của {@link android.content.Intent} là một đối tượng +{@link android.content.ComponentName} mà bạn có thể chỉ định bằng cách sử dụng một tên lớp +hoàn toàn đủ tiêu chuẩn của thành phần đích, bao gồm tên gói của ứng dụng. Ví dụ, +{@code com.example.ExampleActivity}. Bạn có thể đặt tên thành phần bằng {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()}, hoặc bằng + hàm dựng {@link android.content.Intent}.

+ +
+ +

Hành động
+
Một xâu quy định hành động thông thường sẽ thực hiện (chẳng hạn như xem hoặc chọn). + +

Trong trường hợp một ý định quảng bá, đây là hành động đã diễn ra và đang được báo cáo. +Hành động này quyết định phần lớn cách thức xác định cấu trúc phần còn lại của ý định—đặc biệt là +những gì chứa trong dữ liệu và phụ thêm. + +

Bạn có thể quy định các hành động của chính mình để các ý định bên trong ứng dụng của bạn sử dụng (hoặc để +các ứng dụng khác sử dụng nhằm gọi ra các thành phần trong ứng dụng của mình), nhưng bạn nên thường xuyên sử dụng hằng số hành động +được định nghĩa bởi lớp {@link android.content.Intent} hoặc các lớp khuôn khổ khác. Sau đây là một số +hành động thường dùng để bắt đầu một hoạt động:

+ +
+
{@link android.content.Intent#ACTION_VIEW}
+
Sử dụng hành động này trong một ý định với {@link + android.content.Context#startActivity startActivity()} khi bạn có một số thông tin mà + một hoạt động có thể hiển thị cho người dùng, chẳng hạn như ảnh để xem trong một ứng dụng bộ sưu tập ảnh, hay địa chỉ để + xem trong ứng dụng bản đồ.
+ +
{@link android.content.Intent#ACTION_SEND}
+
Còn được biết đến như là ý định "chia sẻ", bạn nên sử dụng kiểu này trong một ý định với {@link + android.content.Context#startActivity startActivity()} khi bạn có một số dữ liệu mà người dùng có thể + chia sẻ thông qua một ứng dụng khác, chẳng hạn như một ứng dụng e-mail hay ứng dụng chia sẻ mạng xã hội.
+
+ +

Xem tham chiếu lớp {@link android.content.Intent} để biết thêm +hằng số có chức năng định nghĩa các hành động thông thường. Những hành động khác được định nghĩa +ở phần khác trong khuôn khổ Android, chẳng hạn như trong {@link android.provider.Settings} đối với những hành động +có chức năng mở màn hình cụ thể trong ứng dụng Cài đặt của hệ thống.

+ +

Bạn có thể quy định hành động cho một ý định với {@link android.content.Intent#setAction +setAction()} hoặc với một hàm dựng {@link android.content.Intent}.

+ +

Nếu bạn định nghĩa các hành động của chính mình, nhớ nêu tên gói ứng dụng của bạn +làm tiền tố. Ví dụ:

+
static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
+
+ +
Dữ liệu
+
URI (một đối tượng {@link android.net.Uri}) tham chiếu dữ liệu sẽ được hành động dựa trên nó và/hoặc kiểu MIME +của dữ liệu đó. Kiểu dữ liệu được cung cấp thường sẽ bị chi phối bởi hành động của ý định. Ví +dụ, nếu hành động là {@link android.content.Intent#ACTION_EDIT}, dữ liệu cần chứa +URI của tài liệu cần chỉnh sửa. + +

Khi tạo một ý định, +một điều thường quan trọng đó là quy định kiểu dữ liệu (kiểu MIME của nó) ngoài URI của nó. +Ví dụ, một hoạt động có thể hiển thị hình ảnh có thể sẽ không +phát được tệp âm thanh, ngay cả khi định dạng URI có thể tương tự. +Vì thế, việc quy định kiểu MIME cho dữ liệu của bạn sẽ giúp hệ thống +Android tìm được thành phần tốt nhất để nhận ý định của bạn. +Tuy nhiên, kiểu MIME đôi khi có thể được suy ra từ URI—cụ thể, khi dữ liệu là một URI +{@code content:}, có chức năng cho biết dữ liệu nằm trên thiết bị và được kiểm soát bởi một +{@link android.content.ContentProvider}, điều này khiến kiểu MIME của dữ liệu hiển thị đối với hệ thống.

+ +

Để chỉ đặt URI dữ liệu, hãy gọi {@link android.content.Intent#setData setData()}. +Để chỉ đặt kiểu MIME, hãy gọi {@link android.content.Intent#setType setType()}. Nếu cần, bạn +bạn có thể công khai đặt cả hai với {@link +android.content.Intent#setDataAndType setDataAndType()}.

+ +

Chú ý: Nếu bạn muốn đặt cả URI và kiểu MIME, +không gọi {@link android.content.Intent#setData setData()} và +{@link android.content.Intent#setType setType()} vì chúng sẽ vô hiệu hóa giá trị của nhau. +Luôn sử dụng {@link android.content.Intent#setDataAndType setDataAndType()} để đặt cả +URI và kiểu MIME.

+
+ +

Thể loại
+
Một xâu chứa thông tin bổ sung về kiểu thành phần +sẽ xử lý ý định. Trong một ý định có thể chứa +nhiều mô tả thể loại, nhưng hầu hết các ý định lại không yêu cầu thể loại. +Sau đây là một số thể loại thường gặp: + +
+
{@link android.content.Intent#CATEGORY_BROWSABLE}
+
Hoạt động mục tiêu cho phép chính nó được bắt đầu bởi một trình duyệt web để hiển thị dữ liệu + được một liên kết tham chiếu—chẳng hạn như một hình ảnh hay thư e-mail. +
+
{@link android.content.Intent#CATEGORY_LAUNCHER}
+
Hoạt động là hoạt động ban đầu của một tác vụ và được liệt kê trong + trình khởi chạy ứng dụng của hệ thống. +
+
+ +

Xem mô tả lớp {@link android.content.Intent} để biết danh sách đầy đủ về các +thể loại.

+ +

Bạn có thể quy định một thể loại bằng {@link android.content.Intent#addCategory addCategory()}.

+
+
+ + +

Những tính chất này được liệt kê ở trên (tên thành phần, hành động, dữ liệu và thể loại) biểu hiện các +đặc điểm xác định của một ý định. Bằng cách đọc những tính chất này, hệ thống Android +có thể quyết định nó sẽ bắt đầu thành phần ứng dụng nào.

+ +

Tuy nhiên, một ý định có thể mang thông tin bổ sung không ảnh hưởng tới +cách nó được giải quyết đối với một thành phần ứng dụng. Một ý định cũng có thể cung cấp:

+ +
+
Phụ thêm
+
Các cặp khóa-giá trị mang thông tin bổ sung cần thiết để hoàn thành hành động được yêu cầu. +Giống như việc một số hành động sử dụng các kiểu URI dữ liệu riêng, một số hành động cũng sử dụng các phần phụ thêm riêng. + +

Bạn có thể thêm dữ liệu phụ thêm bằng các phương pháp {@link android.content.Intent#putExtra putExtra()} khác nhau, +mỗi phương pháp chấp nhận hai tham số: tên khóa và giá trị. +Bạn cũng có thể tạo một đối tượng {@link android.os.Bundle} bằng tất cả dữ liệu phụ thêm, sau đó chèn + {@link android.os.Bundle} vào {@link android.content.Intent} bằng {@link +android.content.Intent#putExtras putExtras()}.

+ +

Ví dụ, khi tạo một ý định để gửi một e-mail bằng +{@link android.content.Intent#ACTION_SEND}, bạn có thể chỉ định người nhận "tới" bằng khóa +{@link android.content.Intent#EXTRA_EMAIL}, và chỉ định "chủ đề" bằng khóa +{@link android.content.Intent#EXTRA_SUBJECT}.

+ +

Lớp {@link android.content.Intent} quy định nhiều hằng số {@code EXTRA_*} cho +các kiểu dữ liệu chuẩn hóa. Nếu bạn cần khai báo các khóa phụ thêm của riêng mình (cho những ý định +mà ứng dụng của bạn nhận), hãy chắc chắn nêu tên gói ứng dụng của bạn +làm tiền tố. Ví dụ:

+
static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
+
+ +
Cờ
+
Cờ được định nghĩa trong lớp {@link android.content.Intent} có chức năng như siêu dữ liệu cho +ý định. Cờ có thể chỉ lệnh hệ thống Android về cách khởi chạy một hoạt động (ví dụ, hoạt động sẽ thuộc về +tác vụ nào +) và cách xử lý sau khi nó được khởi chạy (ví dụ, nó có thuộc về danh sách hoạt động +gần đây hay không). + +

Để biết thêm thông tin, hãy xem phương pháp {@link android.content.Intent#setFlags setFlags()}.

+
+ +
+ + + + +

Ví dụ về ý định biểu thị

+ +

Ý định biểu thị là ý định mà bạn sử dụng để khởi chạy một thành phần ứng dụng cụ thể +chẳng hạn như một hoạt động hay dịch vụ cụ thể trong ứng dụng của bạn. Để tạo một ý định biểu thị, hãy định nghĩa +tên thành phần cho đối tượng {@link android.content.Intent} —tất cả các +tính chất ý định khác đều không bắt buộc.

+ +

Ví dụ, nếu bạn đã xây dựng một dịch vụ trong ứng dụng của mình, đặt tên là {@code DownloadService}, +được thiết kế để tải xuống một tệp từ web, bạn có thể bắt đầu nó bằng mã sau:

+ +
+// Executed in an Activity, so 'this' is the {@link android.content.Context}
+// The fileUrl is a string URL, such as "http://www.example.com/image.png"
+Intent downloadIntent = new Intent(this, DownloadService.class);
+downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl));
+startService(downloadIntent);
+
+ +

Hàm dựng {@link android.content.Intent#Intent(Context,Class)} +cung cấp cho ứng dụng {@link android.content.Context} và thành phần +một đối tượng {@link java.lang.Class}. Như vậy, +ý định này rõ ràng sẽ bắt đầu lớp {@code DownloadService} trong ứng dụng.

+ +

Để biết thêm thông tin về việc xây dựng và bắt đầu một dịch vụ, hãy xem hướng dẫn +Dịch vụ.

+ + + + +

Ví dụ về ý định không biểu thị

+ +

Ý định không biểu thị quy định một hành động mà có thể gọi ra bất kỳ ứng dụng nào trên thiết bị mà có +khả năng thực hiện hành động đó. Việc sử dụng ý định không biểu thị có ích khi ứng dụng của bạn không thể thực hiện +hành động, nhưng các ứng dụng khác có thể và bạn muốn người dùng chọn ứng dụng sẽ sử dụng.

+ +

Ví dụ, nếu bạn có nội dung mà mình muốn người dùng chia sẻ với người khác, hãy tạo một ý định +với hành động {@link android.content.Intent#ACTION_SEND} và +bổ sung phần phụ thêm quy định nội dung sẽ chia sẻ. Khi bạn gọi +{@link android.content.Context#startActivity startActivity()} bằng ý định đó, người dùng có thể +chọn một ứng dụng để chia sẻ nội dung thông qua đó.

+ +

Chú ý: Có thể là người dùng sẽ không có bất kỳ +ứng dụng nào xử lý được ý định không biểu thị mà bạn gửi tới {@link android.content.Context#startActivity +startActivity()}. Nếu chuyện đó xảy ra, phương pháp gọi sẽ thất bại và ứng dụng của bạn sẽ gặp lỗi. Để xác minh rằng +một hoạt động sẽ nhận được ý định, hãy gọi {@link android.content.Intent#resolveActivity +resolveActivity()} trên đối tượng {@link android.content.Intent} của bạn. Nếu kết quả không rỗng +thì có ít nhất một ứng dụng có thể xử lý ý định và sẽ an toàn nếu gọi +{@link android.content.Context#startActivity startActivity()}. Nếu kết quả rỗng, +bạn không nên sử dụng ý định và, nếu có thể, bạn nên vô hiệu hóa tính năng phát hành +ý định.

+ + +
+// Create the text message with a string
+Intent sendIntent = new Intent();
+sendIntent.setAction(Intent.ACTION_SEND);
+sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
+sendIntent.setType("text/plain");
+
+// Verify that the intent will resolve to an activity
+if (sendIntent.resolveActivity(getPackageManager()) != null) {
+    startActivity(sendIntent);
+}
+
+ +

Lưu ý: Trong trường hợp này, URI không được sử dụng, nhưng kiểu dữ liệu của ý định +sẽ được khai báo để quy định nội dung được thực hiện bởi phần phụ thêm.

+ + +

Khi {@link android.content.Context#startActivity startActivity()} được gọi, hệ thống +sẽ kiểm tra tất cả ứng dụng đã cài đặt để xác định những ứng dụng có thể xử lý kiểu ý định này (một +ý định với hành động {@link android.content.Intent#ACTION_SEND} và có mang dữ liệu +"văn bản/thuần"). Nếu chỉ có một ứng dụng có thể xử lý nó, ứng dụng đó sẽ mở ngay lập tức và được cấp cho +ý định. Nếu có nhiều hoạt động chấp nhận ý định, hệ thống +sẽ hiển thị một hộp thoại để người dùng có thể chọn ứng dụng sẽ sử dụng.

+ + +
+ +

Hình 2. Hộp thoại bộ chọn.

+
+ +

Bắt buộc một bộ chọn ứng dụng

+ +

Khi có nhiều hơn một ứng dụng hồi đáp ý định không biểu thị của bạn, +người dùng có thể chọn ứng dụng nào sẽ sử dụng và đặt ứng dụng đó làm lựa chọn mặc định cho +hành động. Điều này tốt khi thực hiện một hành động mà người dùng +có thể muốn sử dụng ứng dụng tương tự từ lúc này trở đi, chẳng hạn như khi mở một trang web (người dùng +thường thích ưu tiên sử dụng chỉ một trình duyệt web).

+ +

Tuy nhiên, nếu nhiều ứng dụng có thể hồi đáp ý định và người dùng có thể muốn sử dụng mỗi +lần một ứng dụng khác, bạn nên công khai hiển thị một hộp thoại bộ chọn. Hộp thoại bộ chọn yêu cầu +người dùng phải chọn ứng dụng sẽ sử dụng mỗi lần cho hành động (người dùng không thể chọn một ứng dụng mặc định cho +hành động). Ví dụ, khi ứng dụng của bạn thực hiện "chia sẻ" với hành động {@link +android.content.Intent#ACTION_SEND}, người dùng có thể muốn chia sẻ bằng cách sử dụng một ứng dụng khác tùy vào +tình hình thực tế của họ, vì thế bạn nên luôn sử dụng hộp thoại bộ chọn như minh họa trong hình 2.

+ + + + +

Để hiển thị bộ chọn, hãy tạo một {@link android.content.Intent} bằng cách sử dụng {@link +android.content.Intent#createChooser createChooser()} và chuyển nó sang {@link +android.app.Activity#startActivity startActivity()}. Ví dụ:

+ +
+Intent sendIntent = new Intent(Intent.ACTION_SEND);
+...
+
+// Always use string resources for UI text.
+// This says something like "Share this photo with"
+String title = getResources().getString(R.string.chooser_title);
+// Create intent to show the chooser dialog
+Intent chooser = Intent.createChooser(sendIntent, title);
+
+// Verify the original intent will resolve to at least one activity
+if (sendIntent.resolveActivity(getPackageManager()) != null) {
+    startActivity(chooser);
+}
+
+ +

Một hộp thoại hiển thị với một danh sách ứng dụng hồi đáp lại ý định được chuyển sang phương pháp {@link +android.content.Intent#createChooser createChooser()} và sử dụng văn bản được cung cấp làm +tiêu đề của hộp thoại.

+ + + + + + + + + +

Nhận một Ý định Không biểu thị

+ +

Để quảng cáo những ý định không biểu thị mà ứng dụng của bạn có thể nhận, hãy khai báo một hoặc nhiều bộ lọc ý định cho +từng thành phần ứng dụng của bạn với một phần tử {@code <intent-filter>} +trong tệp bản kê khai của mình. +Mỗi bộ lọc ý định sẽ quy định loại ý định mà nó chấp nhận dựa trên hành động, +dữ liệu và thể loại của ý định. Hệ thống sẽ chỉ chuyển một ý định không biểu thị tới thành phần ứng dụng của bạn nếu +ý định đó có thể chuyển qua một trong các bộ lọc ý định của bạn.

+ +

Lưu ý: Ý định biểu thị luôn được chuyển tới mục tiêu của mình, +không phụ thuộc vào bất kỳ bộ lọc ý định nào mà thành phần khai báo.

+ +

Một thành phần ứng dụng nên khai báo các bộ lọc riêng cho từng công việc duy nhất mà nó có thể thực hiện. +Ví dụ, một hoạt động trong một ứng dụng bộ sưu tập ảnh có thể có hai bộ lọc: một bộ lọc +để xem một hình ảnh và một bộ lọc để chỉnh sửa một hình ảnh. Khi hoạt động bắt đầu, +nó sẽ kiểm tra {@link android.content.Intent} và quyết định cách xử lý dựa trên thông tin +trong {@link android.content.Intent} (chẳng hạn như có hiển thị các điều khiển của trình chỉnh sửa hoặc không).

+ +

Mỗi bộ lọc ý định sẽ được định nghĩa bởi một phần tử {@code <intent-filter>} +trong tệp bản kê khai của ứng dụng, được lồng trong thành phần ứng dụng tương ứng (chẳng hạn như +một phần tử {@code <activity>} +). Bên trong {@code <intent-filter>}, +bạn có thể quy định loại ý định sẽ chấp nhận bằng cách sử dụng một hoặc nhiều +phần tử trong ba phần tử sau:

+ +
+
{@code <action>}
+
Khai báo hành động ý định được chấp nhận, trong thuộc tính {@code name}. Giá trị + phải là giá trị xâu ký tự của một hành động chứ không phải hằng số lớp.
+
{@code <data>}
+
Khai báo kiểu dữ liệu được chấp nhận, bằng cách sử dụng một hoặc nhiều thuộc tính quy định + các khía cạnh của URI dữ liệu (scheme, host, port, + path, v.v.) và kiểu MIME.
+
{@code <category>}
+
Khai báo thể loại ý định được chấp nhận, trong thuộc tính {@code name}. Giá trị + phải là giá trị xâu ký tự của một hành động chứ không phải hằng số lớp. + +

Lưu ý: Để nhận các ý định không biểu thị, bạn + phải nêu thể loại + {@link android.content.Intent#CATEGORY_DEFAULT} trong bộ lọc ý định. Các phương pháp + {@link android.app.Activity#startActivity startActivity()} và + {@link android.app.Activity#startActivityForResult startActivityForResult()} xử lý tất cả ý định + như thể chúng khai báo thể loại {@link android.content.Intent#CATEGORY_DEFAULT}. + Nếu bạn không khai báo thể loại này trong bộ lọc ý định của mình, không có ý định không biểu thị nào sẽ phân giải thành + hoạt động của bạn.

+
+
+ +

Ví dụ, sau đây là một khai báo hoạt động với một bộ lọc ý định để nhận một ý định +{@link android.content.Intent#ACTION_SEND} khi kiểu dữ liệu là văn bản:

+ +
+<activity android:name="ShareActivity">
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+    </intent-filter>
+</activity>
+
+ +

Không sao nếu tạo một bộ lọc chứa nhiều hơn một thực thể của +{@code <action>}, +{@code <data>}, hoặc +{@code <category>}. +Nếu làm vậy, bạn chỉ cần chắc chắn rằng thành phần có thể xử lý bất kỳ và tất cả các cách kết hợp +những phần tử bộ lọc đó.

+ +

Khi bạn muốn xử lý nhiều kiểu ý định, nhưng chỉ theo các cách kết hợp cụ thể giữa +hành động, dữ liệu và kiểu thể loại, khi đó bạn cần tạo nhiều bộ lọc ý định.

+ + + + +

Ý định không biểu thị sẽ được kiểm tra dựa trên một bộ lọc bằng cách so sánh ý định với từng phần tử trong số +ba phần tử. Để được chuyển tới thành phần, ý định phải vượt qua tất cả ba lần kiểm tra. +Nếu không khớp với thậm chí chỉ một lần thì hệ thống Android sẽ không chuyển ý định tới +thành phần. Tuy nhiên, vì một thành phần có thể có nhiều bộ lọc ý định, ý định mà không chuyển qua +một trong các bộ lọc của thành phần có thể chuyển qua trên một bộ lọc khác. +Bạn có thể tìm hiểu thêm thông tin về cách hệ thống giải quyết ý định trong phần bên dưới +về Giải quyết Ý định.

+ +

Chú ý: Để tránh vô ý chạy +{@link android.app.Service} của một ứng dụng khác, hãy luôn sử dụng một ý định biểu thị để bắt đầu dịch vụ của chính bạn và không được +khai báo các bộ lọc ý định cho dịch vụ của bạn.

+ +

Lưu ý: +Đối với tất cả hoạt động, bạn phải khai báo các bộ lọc ý định của mình trong một tệp bản kê khai. +Tuy nhiên, các bộ lọc cho hàm nhận quảng bá có thể được đăng ký linh hoạt bằng cách gọi +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}. Sau đó, bạn có thể bỏ đăng ký hàm nhận đó bằng {@link +android.content.Context#unregisterReceiver unregisterReceiver()}. Làm vậy sẽ cho phép ứng dụng của bạn +lắng nghe các quảng bá cụ thể chỉ trong một khoảng thời gian xác định trong khi ứng dụng của bạn +đang chạy.

+ + + + + + + +

Ví dụ về bộ lọc

+ +

Để hiểu hơn về một số hành vi của bộ lọc ý định, hãy xem đoạn mã HTML sau +từ tệp bản kê khai của một ứng dụng chia sẻ mạng xã hội.

+ +
+<activity android:name="MainActivity">
+    <!-- This activity is the main entry, should appear in app launcher -->
+    <intent-filter>
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+</activity>
+
+<activity android:name="ShareActivity">
+    <!-- This activity handles "SEND" actions with text data -->
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+    </intent-filter>
+    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
+    <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <action android:name="android.intent.action.SEND_MULTIPLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
+        <data android:mimeType="image/*"/>
+        <data android:mimeType="video/*"/>
+    </intent-filter>
+</activity>
+
+ +

Hoạt động thứ nhất, {@code MainActivity}, là điểm mục nhập chính của ứng dụng—hoạt động này +sẽ mở khi người dùng khởi tạo ban đầu ứng dụng bằng biểu tượng trình khởi chạy:

+
    +
  • Hành động {@link android.content.Intent#ACTION_MAIN} thể hiện + đây là điểm mục nhập chính và không yêu cầu bất kỳ dữ liệu ý định nào.
  • +
  • Thể loại {@link android.content.Intent#CATEGORY_LAUNCHER} cho biết rằng biểu tượng + của hoạt động này nên được đặt trong trình khởi chạy ứng dụng của hệ thống. Nếu phần tử {@code <activity>} + không quy định một biểu tượng bằng {@code icon}, khi đó hệ thống sẽ sử dụng biểu tượng từ phần tử {@code <application>} +.
  • +
+

Hai nội dung này phải được ghép đôi cùng nhau để hoạt động xuất hiện trong trình khởi chạy ứng dụng.

+ +

Hoạt động thứ hai, {@code ShareActivity}, có mục đích để tạo điều kiện chia sẻ nội dung văn bản và +phương tiện. Mặc dù người dùng có thể nhập hoạt động này bằng cách điều hướng tới nó từ {@code MainActivity}, +họ cũng có thể nhập {@code ShareActivity} trực tiếp từ một ứng dụng khác mà phát hành +ý định không biểu thị khớp với một trong hai bộ lọc ý định.

+ +

Lưu ý: Kiểu MIME, +{@code +application/vnd.google.panorama360+jpg}, là một kiểu dữ liệu đặc biệt quy định +ảnh chụp toàn cảnh mà bạn có thể xử lý bằng các API Google +panorama.

+ + + + + + + + + + + + + +

Sử dụng một Ý định Chờ

+ +

Đối tượng {@link android.app.PendingIntent} là một trình bao bọc xung quanh một đối tượng {@link +android.content.Intent}. Mục đích chính của một {@link android.app.PendingIntent} + là cấp quyền cho một ứng dụng ngoài +để sử dụng {@link android.content.Intent} chứa trong nó như thể nó được thực thi từ tiến trình +của chính ứng dụng của bạn.

+ +

Các trường hợp sử dụng chính đối với một ý định chờ bao gồm:

+
    +
  • Khai báo một ý định cần được thực thi khi người dùng thực hiện một hành động bằng Thông báo của bạn + ({@link android.app.NotificationManager} + của hệ thống Android thực thi {@link android.content.Intent}). +
  • Khai báo một ý định cần được thực thi khi người dùng thực hiện một hành động bằng + App Widget của bạn + (ứng dụng màn hình Trang chủ thực thi {@link android.content.Intent}). +
  • Khai báo một ý định cần được thực thi tại một thời điểm xác định trong tương lai ( +{@link android.app.AlarmManager} của hệ thống Android thực thi {@link android.content.Intent}). +
+ +

Vì mỗi đối tượng {@link android.content.Intent} được thiết kế để được xử lý bởi một +loại thành phần ứng dụng cụ thể (hoặc là {@link android.app.Activity}, {@link android.app.Service}, hay + {@link android.content.BroadcastReceiver}), vì thế {@link android.app.PendingIntent} cũng +phải được tạo lập với cân nhắc tương tự. Khi sử dụng một ý định chờ, ứng dụng của bạn sẽ không +thực thi ý định bằng một lệnh gọi chẳng hạn như {@link android.content.Context#startActivity +startActivity()}. Thay vào đó, bạn phải khai báo loại thành phần theo ý định khi bạn tạo lập +{@link android.app.PendingIntent} bằng cách gọi phương pháp trình tạo lập tương ứng:

+ +
    +
  • {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.app.Activity}.
  • +
  • {@link android.app.PendingIntent#getService PendingIntent.getService()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.app.Service}.
  • +
  • {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.content.BroadcastReceiver}.
  • +
+ +

Trừ khi ứng dụng của bạn đang nhận ý định chờ từ các ứng dụng khác, +các phương pháp để tạo lập {@link android.app.PendingIntent} trên là những phương pháp +{@link android.app.PendingIntent} duy nhất mà bạn sẽ cần.

+ +

Mỗi phương pháp sẽ lấy ứng dụng {@link android.content.Context} hiện tại, +{@link android.content.Intent} mà bạn muốn bao bọc, và một hoặc nhiều cờ quy định +cách thức sử dụng ý định (chẳng hạn như ý định có thể được sử dụng nhiều hơn một lần hay không).

+ +

Bạn có thể tham khảo thêm thông tin về việc sử dụng ý định chờ trong tài liệu cho từng +trường hợp sử dụng tương ứng chẳng hạn như trong hướng dẫn về API Thông báo +và App Widgets.

+ + + + + + + +

Giải quyết Ý định

+ + +

Khi hệ thống nhận được một ý định không biểu thị nhằm bắt đầu một hoạt động, nó sẽ tìm +hoạt động tốt nhất cho ý định đó bằng cách so sánh ý định với các bộ lọc ý định dựa trên ba phương diện:

+ +
    +
  • Hành động của ý định +
  • Dữ liệu của ý định (cả URI và kiểu dữ liệu) +
  • Thể loại của ý định +
+ +

Các phần sau mô tả cách một ý định được so khớp với (các) thành phần phù hợp +về phương diện bộ lọc ý định được khai báo như thế nào trong tệp bản kê khai của một ứng dụng.

+ + +

Kiểm tra hành động

+ +

Để quy định các hành động của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +{@code +<action>} hoặc nhiều hơn. Ví dụ:

+ +
+<intent-filter>
+    <action android:name="android.intent.action.EDIT" />
+    <action android:name="android.intent.action.VIEW" />
+    ...
+</intent-filter>
+
+ +

Để vượt qua bộ lọc này, hành động được quy định trong {@link android.content.Intent} + phải khớp với một trong các hành động được liệt kê trong bộ lọc.

+ +

Nếu bộ lọc không liệt kê bất kỳ hành động nào thì sẽ không có gì để +ý định so khớp, vì thế tất cả ý định sẽ không vượt qua kiểm tra. Tuy nhiên, nếu một {@link android.content.Intent} +không quy định một hành động, nó sẽ vượt qua kiểm tra (miễn là bộ lọc +chứa ít nhất một hành động).

+ + + +

Kiểm tra thể loại

+ +

Để quy định các thể loại của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +{@code +<category>} hoặc nhiều hơn. Ví dụ:

+ +
+<intent-filter>
+    <category android:name="android.intent.category.DEFAULT" />
+    <category android:name="android.intent.category.BROWSABLE" />
+    ...
+</intent-filter>
+
+ +

Để một ý định vượt qua kiểm tra thể loại, mỗi thể loại trong {@link android.content.Intent} +phải khớp với một thể loại trong bộ lọc. Trường hợp ngược lại là không cần thiết—bộ lọc ý định có thể +khai báo nhiều thể loại hơn được quy định trong {@link android.content.Intent} và +{@link android.content.Intent} sẽ vẫn vượt qua. Vì thế, ý định không có thể loại +luôn vượt qua kiểm tra này, không phụ thuộc vào những thể loại nào được khai báo trong bộ lọc.

+ +

Lưu ý: +Android sẽ tự động áp dụng thể loại {@link android.content.Intent#CATEGORY_DEFAULT} +cho tất cả ý định không biểu thị được chuyển tới {@link +android.content.Context#startActivity startActivity()} và {@link +android.app.Activity#startActivityForResult startActivityForResult()}. +Vì thế, nếu bạn muốn hoạt động của mình nhận ý định không biểu thị, nó phải +nêu một thể loại cho {@code "android.intent.category.DEFAULT"} trong các bộ lọc ý định của mình (như +được minh họa trong ví dụ {@code <intent-filter>} trước đó.

+ + + +

Kiểm tra dữ liệu

+ +

Để quy định dữ liệu của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +{@code +<data>} hoặc nhiều hơn. Ví dụ:

+ +
+<intent-filter>
+    <data android:mimeType="video/mpeg" android:scheme="http" ... />
+    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
+    ...
+</intent-filter>
+
+ +

Mỗi phần tử <data> +có thể quy định một cấu trúc URI và kiểu dữ liệu (kiểu phương tiện MIME). Có các thuộc tính +riêng — {@code scheme}, {@code host}, {@code port}, +và {@code path} — cho từng phần của URI: +

+ +

{@code <scheme>://<host>:<port>/<path>}

+ +

+Ví dụ: +

+ +

{@code content://com.example.project:200/folder/subfolder/etc}

+ +

Trong URI này, lược đồ là {@code content}, máy chủ là {@code com.example.project}, +cổng là {@code 200}, và đường dẫn là {@code folder/subfolder/etc}. +

+ +

Mỗi thuộc tính sau đều không bắt buộc trong một phần tử {@code <data>}, +nhưng có sự phụ thuộc mang tính chất tuyến tính:

+
    +
  • Nếu không quy định một lược đồ thì máy chủ bị bỏ qua.
  • +
  • Nếu không quy định một máy chủ thì cổng bị bỏ qua.
  • +
  • Nếu không quy định cả lược đồ và máy chủ thì đường dẫn bị bỏ qua.
  • +
+ +

Khi URI trong một ý định được so sánh với đặc tả URI trong một bộ lọc, +nó chỉ được so sánh với các bộ phận của URI được nêu trong bộ lọc. Ví dụ:

+
    +
  • Nếu một bộ lọc chỉ quy định một lược đồ, tất cả URI có lược đồ đó sẽ khớp +với bộ lọc.
  • +
  • Nếu một bộ lọc quy định một lược đồ và thẩm quyền nhưng không có đường dẫn, tất cả URI +với cùng lược đồ và thẩm quyền sẽ thông qua bộ lọc, không phụ thuộc vào đường dẫn của nó.
  • +
  • Nếu bộ lọc quy định một lược đồ, thẩm quyền và đường dẫn, chỉ những URI có cùng lược đồ, +thẩm quyền và đường dẫn mới thông qua bộ lọc.
  • +
+ +

Lưu ý: Đặc tả đường dẫn có thể +chứa một ký tự đại diện dấu sao (*) để yêu cầu chỉ khớp một phần với tên đường dẫn.

+ +

Kiểm tra dữ liệu so sánh cả URI và kiểu MIME trong ý định với một URI +và kiểu MIME được quy định trong bộ lọc. Các quy tắc như sau: +

+ +
    +
  1. Một ý định mà không chứa URI cũng như kiểu MIME sẽ chỉ vượt qua +kiểm tra nếu bộ lọc không quy định bất kỳ URI hay kiểu MIME nào.
  2. + +
  3. Một ý định chứa URI nhưng không có kiểu MIME (không biểu thị cũng như suy luận được từ +URI) sẽ chỉ vượt qua kiểm tra nếu URI của nó khớp với định dạng URI của bộ lọc +và bộ lọc tương tự không quy định một kiểu MIME.
  4. + +
  5. Một ý định chứa kiểu MIME nhưng không chứa URI sẽ chỉ vượt qua kiểm tra +nếu bộ lọc liệt kê cùng kiểu MIME và không quy định một định dạng URI.
  6. + +
  7. Ý định mà chứa cả URI và kiểu MIME (hoặc biểu thị hoặc suy ra được từ +URI) sẽ chỉ vượt qua phần kiểu MIME của kiểm tra nếu kiểu đó +khớp với kiểu được liệt kê trong bộ lọc. Nó vượt qua phần URI của kiểm tra +nếu URI của nó khớp với một URI trong bộ lọc hoặc nếu nó có một {@code content:} +hoặc {@code file:} URI và bộ lọc không quy định một URI. Nói cách khác, +một thành phần được giả định là hỗ trợ dữ liệu {@code content:} và {@code file:} nếu +bộ lọc của nó liệt kê chỉ một kiểu MIME.

  8. +
+ +

+Quy tắc cuối cùng này, quy tắc (d), phản ánh kỳ vọng +rằng các thành phần có thể nhận được dữ liệu cục bộ từ một tệp hoặc trình cung cấp nội dung. +Vì thế, các bộ lọc của chúng có thể chỉ liệt kê một kiểu dữ liệu và không cần công khai +nêu tên {@code content:} và các lược đồ {@code file:}. +Đây là một trường hợp điển hình. Phần tử {@code <data>} như + sau, ví dụ, sẽ thông báo cho Android biết rằng thành phần có thể nhận được dữ liệu ảnh từ một trình cung cấp +nội dung và sẽ hiển thị nó: +

+ +
+<intent-filter>
+    <data android:mimeType="image/*" />
+    ...
+</intent-filter>
+ +

+Vì hầu hết dữ liệu có sẵn đều được cấp phát bởi các trình cung cấp nội dung, những bộ lọc mà +quy định một kiểu dữ liệu chứ không phải URI có lẽ là phổ biến nhất. +

+ +

+Một cấu hình phổ biến khác đó là các bộ lọc có một lược đồ và một kiểu dữ liệu. Ví +dụ, một phần tử {@code <data>} +như sau thông báo cho Android rằng +thành phần có thể truy xuất dữ liệu video từ mạng để thực hiện hành động: +

+ +
+<intent-filter>
+    <data android:scheme="http" android:type="video/*" />
+    ...
+</intent-filter>
+ + + +

So khớp ý định

+ +

Các ý định được so khớp với các bộ lọc ý định không chỉ để khám phá một thành phần +mục tiêu cần kích hoạt, mà còn để khám phá điều gì đó về tập hợp +các thành phần trên thiết bị. Ví dụ, ứng dụng Trang chủ đưa trình khởi chạy ứng dụng +vào bằng cách tìm tất cả hoạt động có bộ lọc ý định mà quy định hành động +{@link android.content.Intent#ACTION_MAIN} và thể loại +{@link android.content.Intent#CATEGORY_LAUNCHER}.

+ +

Ứng dụng của bạn có thể sử dụng so khớp ý định theo cách tương tự. +{@link android.content.pm.PackageManager} có một tập hợp các phương pháp{@code query...()} +trả về tất cả thành phần có thể chấp nhận một ý định cụ thể, và +một chuỗi các phương pháp {@code resolve...()} tương tự để xác định thành phần +tốt nhất nhằm hồi đáp lại một ý định. Ví dụ, +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} sẽ trả về một danh sách tất cả hoạt động có thể thực hiện +ý định được chuyển qua như một tham đối, và {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} trả về một danh sách dịch vụ tương tự. +Cả hai phương pháp đều không kích hoạt các thành phần; chúng chỉ liệt kê những thành phần +có thể hồi đáp. Có một phương pháp tương tự, +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()}, dành cho hàm nhận quảng bá. +

+ + + + diff --git a/docs/html-intl/intl/vi/guide/components/loaders.jd b/docs/html-intl/intl/vi/guide/components/loaders.jd new file mode 100644 index 0000000000000000000000000000000000000000..b6d277f3d527702311e547f5702106bcca55bfbd --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=Trình tải +parent.title=Hoạt động +parent.link=activities.html +@jd:body +
+
+

Trong tài liệu này

+
    +
  1. Tổng quan về API Trình tải
  2. +
  3. Sử dụng các Trình tải trong một Ứng dụng +
      +
    1. +
    2. Khởi động một Trình tải
    3. +
    4. Khởi động lại một Trình tải
    5. +
    6. Sử dụng các Phương pháp Gọi lại LoaderManager
    7. +
    +
  4. +
  5. Ví dụ +
      +
    1. Thêm Ví dụ
    2. +
    +
  6. +
+ +

Lớp khóa

+
    +
  1. {@link android.app.LoaderManager}
  2. +
  3. {@link android.content.Loader}
  4. + +
+ +

Các mẫu liên quan

+
    +
  1. +LoaderCursor
  2. +
  3. +LoaderThrottle
  4. +
+
+
+ +

Được giới thiệu trong Android 3.0, trình tải giúp việc tải dữ liệu không đồng bộ +trong một hoạt động hoặc phân đoạn trở nên dễ dàng. Trình tải có những đặc điểm sau:

+
    +
  • Chúng sẵn có cho mọi {@link android.app.Activity} và {@link +android.app.Fragment}.
  • +
  • Chúng cung cấp khả năng tải dữ liệu không đồng bộ.
  • +
  • Chúng theo dõi nguồn dữ liệu của mình và chuyển giao kết quả mới khi nội dung +thay đổi.
  • +
  • Chúng tự động kết nối lại với con chạy của trình tải cuối cùng khi được +tạo lại sau khi cấu hình thay đổi. Vì thế, chúng không cần truy vấn lại dữ liệu +của mình.
  • +
+ +

Tổng quan về API Trình tải

+ +

Có nhiều lớp và giao diện có thể có liên quan trong khi sử dụng +các trình tải trong một ứng dụng. Chúng được tóm tắt trong bảng này.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Lớp/Giao diệnMô tả
{@link android.app.LoaderManager}Một lớp tóm tắt được liên kết với {@link android.app.Activity} hoặc +{@link android.app.Fragment} để quản lý một hoặc nhiều thực thể {@link +android.content.Loader}. Nó giúp ứng dụng quản lý +các thao tác chạy lâu hơn cùng với vòng đời {@link android.app.Activity} +hoặc {@link android.app.Fragment}; công dụng phổ biến nhất của lớp này là khi dùng với +{@link android.content.CursorLoader}, tuy nhiên, các ứng dụng được tự do ghi +trình tải của chính mình để tải các kiểu dữ liệu khác. +
+
+ Chỉ có một {@link android.app.LoaderManager} trên mỗi hoạt động hoặc phân đoạn. Nhưng một {@link android.app.LoaderManager} có thể có +nhiều trình tải.
{@link android.app.LoaderManager.LoaderCallbacks}Một giao diện gọi lại để một máy khách tương tác với {@link +android.app.LoaderManager}. Ví dụ, bạn sử dụng phương pháp gọi lại {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +để tạo một trình tải mới.
{@link android.content.Loader}Một lớp tóm tắt có vai trò thực hiện việc tải dữ liệu không đồng bộ. Đây là +lớp cơ bản cho một trình tải. Thông thường, bạn sẽ sử dụng {@link +android.content.CursorLoader}, nhưng bạn có thể triển khai lớp con của chính mình. Trong khi +các trình tải đang hoạt động, chúng sẽ theo dõi nguồn dữ liệu của mình và chuyển giao +kết quả mới khi nội dung thay đổi.
{@link android.content.AsyncTaskLoader}Trình tải tóm tắt có chức năng cung cấp {@link android.os.AsyncTask} để thực hiện công việc.
{@link android.content.CursorLoader}Một lớp con của {@link android.content.AsyncTaskLoader} có chức năng truy vấn +{@link android.content.ContentResolver} và trả về một {@link +android.database.Cursor}. Lớp này triển khai giao thức {@link +android.content.Loader} theo một cách chuẩn hóa để truy vấn các con chạy, +xây dựng trên {@link android.content.AsyncTaskLoader} để thực hiện truy vấn con chạy +trên một luồng nền sao cho nó không chặn UI của ứng dụng. Sử dụng +trình tải này là cách tốt nhất để tải dữ liệu không đồng bộ từ một {@link +android.content.ContentProvider}, thay vì phải thực hiện một truy vấn được quản lý thông qua +phân đoạn hoặc các API của hoạt động.
+ +

Các lớp và giao diện trong bảng trên là những thành phần thiết yếu +mà bạn sẽ sử dụng để triển khai một trình tải trong ứng dụng của mình. Bạn sẽ không cần tất cả chúng +cho từng trình tải mà bạn tạo lập, nhưng bạn sẽ luôn cần một tham chiếu tới {@link +android.app.LoaderManager} để khởi tạo một trình tải và triển khai +một lớp {@link android.content.Loader} chẳng hạn như {@link +android.content.CursorLoader}. Các phần sau đây trình bày với bạn cách sử dụng những +lớp và giao diện này trong một ứng dụng.

+ +

Sử dụng các Trình tải trong một Ứng dụng

+

Phần này mô tả cách sử dụng các trình tải trong một ứng dụng Android. Một +ứng dụng sử dụng trình tải thường bao gồm:

+
    +
  • Một {@link android.app.Activity} hoặc {@link android.app.Fragment}.
  • +
  • Một thực thể của {@link android.app.LoaderManager}.
  • +
  • Một {@link android.content.CursorLoader} để tải dữ liệu được dự phòng bởi một {@link +android.content.ContentProvider}. Hoặc cách khác, bạn có thể triển khai lớp con +của {@link android.content.Loader} hoặc {@link android.content.AsyncTaskLoader} của chính mình để tải +dữ liệu từ một số nguồn khác.
  • +
  • Một triển khai cho {@link android.app.LoaderManager.LoaderCallbacks}. +Đây là nơi bạn tạo trình tải mới và quản lý các tham chiếu của mình tới các +trình tải hiện có.
  • +
  • Một cách để hiển thị dữ liệu của trình tải, chẳng hạn như {@link +android.widget.SimpleCursorAdapter}.
  • +
  • Một nguồn dữ liệu, chẳng hạn như một {@link android.content.ContentProvider}, khi sử dụng một +{@link android.content.CursorLoader}.
  • +
+

Khởi động một Trình tải

+ +

{@link android.app.LoaderManager} quản lý một hoặc nhiều thực thể {@link +android.content.Loader} trong một {@link android.app.Activity} hoặc +{@link android.app.Fragment}. Chỉ có một {@link +android.app.LoaderManager} trên mỗi hoạt động hoặc phân đoạn.

+ +

Thông thường, bạn +sẽ khởi tạo một {@link android.content.Loader} bên trong phương pháp {@link +android.app.Activity#onCreate onCreate()} của hoạt động, hoặc trong phương pháp +{@link android.app.Fragment#onActivityCreated onActivityCreated()} của phân đoạn. Bạn +làm điều này như sau:

+ +
// Prepare the loader.  Either re-connect with an existing one,
+// or start a new one.
+getLoaderManager().initLoader(0, null, this);
+ +

Phương pháp {@link android.app.LoaderManager#initLoader initLoader()} sẽ lấy những +tham số sau:

+
    +
  • Một ID duy nhất xác định trình tải. Trong ví dụ này, ID là 0.
  • +
  • Các tham đối tùy chọn để cung cấp cho trình tải khi +xây dựng (null trong ví dụ này).
  • + +
  • Triển khai {@link android.app.LoaderManager.LoaderCallbacks}, phương pháp mà +{@link android.app.LoaderManager} gọi để báo cáo các sự kiện trình tải. Trong ví dụ này +, lớp cục bộ triển khai giao diện {@link +android.app.LoaderManager.LoaderCallbacks}, vì thế nó chuyển một tham chiếu +tới chính nó, {@code this}.
  • +
+

Lệnh gọi {@link android.app.LoaderManager#initLoader initLoader()} đảm bảo rằng một trình tải +được khởi tạo và hiện hoạt. Nó có hai kết quả có thể xảy ra:

+
    +
  • Nếu trình tải được quy định bởi ID đã tồn tại, trình tải được tạo lập cuối cùng +sẽ được sử dụng lại.
  • +
  • Nếu trình tải được quy định bởi ID không tồn tại, +{@link android.app.LoaderManager#initLoader initLoader()} sẽ kích khởi phương pháp +{@link android.app.LoaderManager.LoaderCallbacks}{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. +Đây là nơi bạn triển khai mã để khởi tạo và trả về một trình tải mới. +Để bàn thêm, hãy xem phần onCreateLoader.
  • +
+

Dù trong trường hợp nào, triển khai {@link android.app.LoaderManager.LoaderCallbacks} +đã cho được liên kết với trình tải, và sẽ được gọi khi +trạng thái của trình tải thay đổi. Nếu tại điểm thực hiện lệnh gọi này, hàm gọi đang trong trạng thái +được khởi động của nó, và trình tải được yêu cầu đã tồn tại và đã khởi tạo +dữ liệu của nó, khi đó hệ thống sẽ gọi {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +ngay lập tức (trong khi {@link android.app.LoaderManager#initLoader initLoader()}), +vì thế bạn phải sẵn sàng khi điều này xảy ra. Xem +onLoadFinished để thảo luận thêm về lệnh gọi lại này

+ +

Lưu ý rằng phương pháp {@link android.app.LoaderManager#initLoader initLoader()} +sẽ trả về {@link android.content.Loader} đã được tạo lập, nhưng bạn không +cần bắt lại một tham chiếu tới nó. {@link android.app.LoaderManager} tự động quản lý +vòng đời của trình tải. {@link android.app.LoaderManager} +khởi động và dừng tải khi cần và duy trì trạng thái của trình tải +và nội dung đi kèm của nó. Như hàm ý, bạn hiếm khi tương tác trực tiếp với các trình tải +(thông qua một ví dụ về việc sử dụng các phương pháp trình tải để tinh chỉnh hành vi +của một trình tải, hãy xem ví dụ LoaderThrottle). +Bạn thường sử dụng nhất là các phương pháp {@link +android.app.LoaderManager.LoaderCallbacks} để can thiệp vào tiến trình tải +khi diễn ra một sự kiện đặc biệt. Để thảo luận thêm về chủ đề này, hãy xem phần Sử dụng Phương pháp Gọi lại LoaderManager.

+ +

Khởi động lại một Trình tải

+ +

Khi bạn sử dụng {@link android.app.LoaderManager#initLoader initLoader()}, như +trình bày bên trên, nó sử dụng một trình tải hiện hữu với ID được quy định nếu có. +Nếu không có, nó sẽ tạo một trình tải. Nhưng đôi khi bạn muốn bỏ dữ liệu cũ của mình +và bắt đầu lại.

+ +

Để bỏ dữ liệu cũ của mình, hãy sử dụng {@link +android.app.LoaderManager#restartLoader restartLoader()}. Ví dụ, việc +triển khai {@link android.widget.SearchView.OnQueryTextListener} này sẽ khởi động lại +trình tải khi truy vấn của người dùng thay đổi. Trình tải cần được khởi động lại sao cho +nó có thể sử dụng bộ lọc tìm kiếm được điều chỉnh để thực hiện một truy vấn mới:

+ +
+public boolean onQueryTextChanged(String newText) {
+    // Called when the action bar search text has changed.  Update
+    // the search filter, and restart the loader to do a new query
+    // with this filter.
+    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+    getLoaderManager().restartLoader(0, null, this);
+    return true;
+}
+ +

Sử dụng các Phương pháp Gọi lại LoaderManager

+ +

{@link android.app.LoaderManager.LoaderCallbacks} là một giao diện gọi lại +cho phép một máy khách tương tác với {@link android.app.LoaderManager}.

+

Các trình tải, đặc biệt là {@link android.content.CursorLoader}, được kỳ vọng sẽ +giữ lại dữ liệu của chúng sau khi bị dừng. Điều này cho phép ứng dụng giữ lại +dữ liệu của chúng qua hoạt động hoặc các phương pháp {@link android.app.Activity#onStop +onStop()} và {@link android.app.Activity#onStart onStart()} của phân đoạn, sao cho khi +người dùng quay lại một ứng dụng, họ không phải chờ dữ liệu +tải lại. Bạn sử dụng các phương pháp {@link android.app.LoaderManager.LoaderCallbacks} +khi cần biết khi nào thì nên tạo một trình tải mới, và để thông báo với ứng dụng khi nào + thì đến lúc để dừng sử dụng dữ liệu của một trình tải.

+ +

{@link android.app.LoaderManager.LoaderCallbacks} bao gồm những phương pháp +sau:

+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — +Khởi tạo và trả về một {@link android.content.Loader} mới cho ID đã cho. +
+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +— Được gọi khi một trình tải được tạo trước đó đã hoàn tất việc tải. +
+
    +
  • {@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} + — Được gọi khi một trình tải được tạo trước đó đang được đặt lại, vì thế mà khiến dữ liệu +của nó không sẵn có. +
  • +
+

Những phương pháp này được mô tả chi tiết hơn trong các phần sau.

+ +

onCreateLoader

+ +

Khi bạn định truy cập một trình tải (ví dụ, thông qua {@link +android.app.LoaderManager#initLoader initLoader()}), nó kiểm tra xem +trình tải được quy định bởi ID có tồn tại không. Nếu không, nó sẽ kích khởi phương pháp {@link +android.app.LoaderManager.LoaderCallbacks} {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. Đây +là lúc bạn tạo một trình tải mới. Thông thường sẽ có một {@link +android.content.CursorLoader}, nhưng bạn có thể triển khai lớp con {@link +android.content.Loader} của chính mình.

+ +

Trong ví dụ này, phương pháp gọi lại {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +sẽ tạo một {@link android.content.CursorLoader}. Bạn phải xây dựng +{@link android.content.CursorLoader} bằng cách sử dụng phương pháp hàm dựng của nó mà yêu cầu +trọn bộ thông tin cần thiết để thực hiện một truy vấn tới {@link +android.content.ContentProvider}. Cụ thể, nó cần:

+
    +
  • uri — URI của nội dung cần truy xuất.
  • +
  • dự thảo — Một danh sách các cột sẽ trả về. Việc chuyển +null sẽ trả về tất cả cột, điều này không hiệu quả.
  • +
  • lựa chọn — Một bộ lọc khai báo các hàng nào sẽ trả về, +có định dạng như một mệnh đề SQL WHERE (không gồm chính mệnh đề WHERE). Việc chuyển +null sẽ trả về tất cả hàng cho URI đã cho.
  • +
  • selectionArgs — Bạn có thể thêm ?s vào lựa chọn, +chúng sẽ được thay thế bằng các giá trị từ selectionArgs, theo thứ tự xuất hiện trong +lựa chọn. Giá trị sẽ được gắn kết thành các Xâu.
  • +
  • sortOrder — Cách sắp xếp thứ tự các hàng, được định dạng như một mệnh đề SQL +ORDER BY (không bao gồm chính mệnh đề ORDER BY). Việc chuyển null sẽ +sử dụng thứ tự sắp xếp mặc định, điều này có thể dẫn đến kết quả không theo thứ tự.
  • +
+

Ví dụ:

+
+ // If non-null, this is the current filter the user has provided.
+String mCurFilter;
+...
+public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+    // This is called when a new Loader needs to be created.  This
+    // sample only has one Loader, so we don't care about the ID.
+    // First, pick the base URI to use depending on whether we are
+    // currently filtering.
+    Uri baseUri;
+    if (mCurFilter != null) {
+        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+                  Uri.encode(mCurFilter));
+    } else {
+        baseUri = Contacts.CONTENT_URI;
+    }
+
+    // Now create and return a CursorLoader that will take care of
+    // creating a Cursor for the data being displayed.
+    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+            + Contacts.DISPLAY_NAME + " != '' ))";
+    return new CursorLoader(getActivity(), baseUri,
+            CONTACTS_SUMMARY_PROJECTION, select, null,
+            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+}
+

onLoadFinished

+ +

Phương pháp này được gọi khi một trình tải được tạo trước đó đã hoàn thành việc tải của mình. +Phương pháp này được bảo đảm sẽ được gọi trước khi giải phóng dữ liệu cuối cùng +được cung cấp cho trình tải này. Tại điểm này, bạn nên loại bỏ mọi trường hợp sử dụng +dữ liệu cũ (do nó sẽ được giải phóng sớm), nhưng không nên +tự mình giải phóng dữ liệu do trình tải sở hữu dữ liệu và sẽ đảm nhận việc này.

+ + +

Trình tải sẽ giải phóng dữ liệu sau khi nó biết ứng dụng đang không còn +sử dụng nó nữa. Ví dụ, nếu dữ liệu là một con chạy từ một {@link +android.content.CursorLoader}, bạn không nên tự mình gọi {@link +android.database.Cursor#close close()} trên dữ liệu đó. Nếu con chạy đang được đặt +trong một {@link android.widget.CursorAdapter}, bạn nên sử dụng phương pháp {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} sao cho + {@link android.database.Cursor} cũ không bị đóng. Ví dụ:

+ +
+// This is the Adapter being used to display the list's data.
SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in.  (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}
+ +

onLoaderReset

+ +

Phương pháp này được gọi khi một trình tải được tạo trước đó đang được đặt lại, vì thế mà khiến +dữ liệu của nó không sẵn có. Lệnh gọi lại này cho phép bạn tìm hiểu xem khi nào thì dữ liệu +sẽ được giải phóng để bạn có thể loại bỏ tham chiếu của mình tới nó.  

+

Sự triển khai này gọi ra +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} +với một giá trị null:

+ +
+// This is the Adapter being used to display the list's data.
+SimpleCursorAdapter mAdapter;
+...
+
+public void onLoaderReset(Loader<Cursor> loader) {
+    // This is called when the last Cursor provided to onLoadFinished()
+    // above is about to be closed.  We need to make sure we are no
+    // longer using it.
+    mAdapter.swapCursor(null);
+}
+ + +

Ví dụ

+ +

Lấy một ví dụ, sau đây là triển khai đầy đủ của {@link +android.app.Fragment} có chức năng hiển thị một {@link android.widget.ListView} chứa +kết quả của một truy vấn đối với trình cung cấp nội dung danh bạ. Nó sử dụng một {@link +android.content.CursorLoader} để quản lý truy vấn trên trình cung cấp.

+ +

Để một ứng dụng truy cập danh bạ của một người dùng, như minh họa trong ví dụ này, bản kê khai +của nó phải bao gồm quyền +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.

+ +
+public static class CursorLoaderListFragment extends ListFragment
+        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
+
+    // This is the Adapter being used to display the list's data.
+    SimpleCursorAdapter mAdapter;
+
+    // If non-null, this is the current filter the user has provided.
+    String mCurFilter;
+
+    @Override public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+
+        // Give some text to display if there is no data.  In a real
+        // application this would come from a resource.
+        setEmptyText("No phone numbers");
+
+        // We have a menu item to show in action bar.
+        setHasOptionsMenu(true);
+
+        // Create an empty adapter we will use to display the loaded data.
+        mAdapter = new SimpleCursorAdapter(getActivity(),
+                android.R.layout.simple_list_item_2, null,
+                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
+                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
+        setListAdapter(mAdapter);
+
+        // Prepare the loader.  Either re-connect with an existing one,
+        // or start a new one.
+        getLoaderManager().initLoader(0, null, this);
+    }
+
+    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        // Place an action bar item for searching.
+        MenuItem item = menu.add("Search");
+        item.setIcon(android.R.drawable.ic_menu_search);
+        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        SearchView sv = new SearchView(getActivity());
+        sv.setOnQueryTextListener(this);
+        item.setActionView(sv);
+    }
+
+    public boolean onQueryTextChange(String newText) {
+        // Called when the action bar search text has changed.  Update
+        // the search filter, and restart the loader to do a new query
+        // with this filter.
+        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
+        getLoaderManager().restartLoader(0, null, this);
+        return true;
+    }
+
+    @Override public boolean onQueryTextSubmit(String query) {
+        // Don't care about this.
+        return true;
+    }
+
+    @Override public void onListItemClick(ListView l, View v, int position, long id) {
+        // Insert desired behavior here.
+        Log.i("FragmentComplexList", "Item clicked: " + id);
+    }
+
+    // These are the Contacts rows that we will retrieve.
+    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
+        Contacts._ID,
+        Contacts.DISPLAY_NAME,
+        Contacts.CONTACT_STATUS,
+        Contacts.CONTACT_PRESENCE,
+        Contacts.PHOTO_ID,
+        Contacts.LOOKUP_KEY,
+    };
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        // This is called when a new Loader needs to be created.  This
+        // sample only has one Loader, so we don't care about the ID.
+        // First, pick the base URI to use depending on whether we are
+        // currently filtering.
+        Uri baseUri;
+        if (mCurFilter != null) {
+            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
+                    Uri.encode(mCurFilter));
+        } else {
+            baseUri = Contacts.CONTENT_URI;
+        }
+
+        // Now create and return a CursorLoader that will take care of
+        // creating a Cursor for the data being displayed.
+        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
+                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
+                + Contacts.DISPLAY_NAME + " != '' ))";
+        return new CursorLoader(getActivity(), baseUri,
+                CONTACTS_SUMMARY_PROJECTION, select, null,
+                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
+    }
+
+    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        // Swap the new cursor in.  (The framework will take care of closing the
+        // old cursor once we return.)
+        mAdapter.swapCursor(data);
+    }
+
+    public void onLoaderReset(Loader<Cursor> loader) {
+        // This is called when the last Cursor provided to onLoadFinished()
+        // above is about to be closed.  We need to make sure we are no
+        // longer using it.
+        mAdapter.swapCursor(null);
+    }
+}
+

Thêm Ví dụ

+ +

Có một vài mẫu khác trong ApiDemos để +minh họa cách sử dụng các trình tải:

+
    +
  • +LoaderCursor — Một phiên bản hoàn chỉnh của +đoạn mã HTML trình bày ở trên.
  • +
  • LoaderThrottle — Một ví dụ về cách sử dụng điều chỉnh để giảm +số truy vấn mà một trình cung cấp nội dung thực hiện khi dữ liệu của nó thay đổi.
  • +
+ +

Để biết thông tin về việc tải xuống và cài đặt các mẫu SDK, hãy xem phần Tải +Mẫu.

+ diff --git a/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd b/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd new file mode 100644 index 0000000000000000000000000000000000000000..390ca156a1f05f4025232d1fc7cf58f12697363a --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=Tiến trình và Luồng +page.tags=vòng đời, nền + +@jd:body + + + +

Khi một thành phần ứng dụng bắt đầu và ứng dụng không có bất kỳ thành phần nào khác +đang chạy, hệ thống Android sẽ khởi động một tiến trình Linux mới cho ứng dụng bằng một luồng +thực thi đơn lẻ. Theo mặc định, tất cả thành phần của cùng ứng dụng sẽ chạy trong cùng tiến trình và luồng +(được gọi là luồng "chính"). Nếu một thành phần ứng dụng bắt đầu và đã tồn tại một tiến trình +cho ứng dụng đó (bởi một thành phần khác từ ứng dụng đã tồn tại), khi đó thành phần được +bắt đầu bên trong tiến trình đó và sử dụng cùng luồng thực thi. Tuy nhiên, bạn có thể sắp xếp cho +các thành phần khác nhau trong ứng dụng của mình để chạy trong các tiến trình riêng biệt, và bạn có thể tạo thêm +luồng cho bất kỳ tiến trình nào.

+ +

Tài liệu này trình bày về cách các tiến trình và luồng vận hành trong một ứng dụng Android.

+ + +

Tiến trình

+ +

Theo mặc định, tất cả thành phần của cùng ứng dụng sẽ chạy trong cùng tiến trình và hầu hết các ứng dụng +sẽ không thay đổi điều này. Tuy nhiên, nếu bạn thấy rằng mình cần kiểm soát một thành phần +cụ thể thuộc về một tiến trình nào đó, bạn có thể làm vậy trong tệp bản kê khai.

+ +

Mục nhập bản kê khai đối với mỗi loại phần tử thành phần—{@code +<activity>}, {@code +<service>}, {@code +<receiver>}, và {@code +<provider>}—sẽ hỗ trợ một thuộc tính {@code android:process} mà có thể quy định một +tiến trình mà thành phần đó sẽ chạy trong đó. Bạn có thể đặt thuộc tính này sao cho từng thành phần chạy +trong chính tiến trình của nó hoặc sao cho một số thành phần chia sẻ một tiến trình trong khi các thành phần khác thì không. Bạn cũng có thể đặt +{@code android:process} sao cho các thành phần của những ứng dụng khác nhau chạy trong cùng +tiến trình—với điều kiện rằng ứng dụng chia sẻ cùng ID người dùng Linux và được ký bằng cùng các +chứng chỉ.

+ +

Phần tử {@code +<application>} cũng hỗ trợ một thuộc tính {@code android:process}, để đặt một +giá trị mặc định áp dụng cho tất cả thành phần.

+ +

Android có thể quyết định tắt một tiến trình tại một thời điểm nào đó, khi bộ nhớ thấp và theo yêu cầu của các +tiến trình khác đang phục vụ người dùng tức thì hơn. Các thành phần +ứng dụng đang chạy trong tiến trình bị tắt bỏ thì sau đó sẽ bị hủy. Tiến trình được +khởi động lại cho những thành phần đó khi lại có việc cho chúng thực hiện.

+ +

Khi quyết định bỏ những tiến trình nào, hệ thống Android sẽ cân nhắc tầm quan trọng tương đối so với +người dùng. Ví dụ, hệ thống sẵn sàng hơn khi tắt một tiến trình lưu trữ các hoạt động không còn +hiển thị trên màn hình, so với một tiến trình lưu trữ các hoạt động đang hiển thị. Vì thế, quyết định về việc có +chấm dứt một tiến trình hay không phụ thuộc vào trạng thái của các thành phần đang chạy trong tiến trình đó. Các quy tắc +được sử dụng để quyết định sẽ chấm dứt tiến trình nào được trình bày ở bên dưới.

+ + +

Vòng đời tiến trình

+ +

Hệ thống Android cố gắng duy trì một tiến trình ứng dụng lâu nhất có thể, nhưng +cuối cùng thì nó cũng cần loại bỏ các tiến trình cũ để lấy lại bộ nhớ cho các tiến trình mới hoặc quan trọng hơn. Để +xác định giữ lại những tiến trình nào +và loại bỏ những tiến trình nào, hệ thống sẽ đặt từng tiến trình vào một "phân cấp tầm quan trọng" dựa trên +những thành phần đang chạy trong tiến trình và trạng thái của những thành phần đó. Những tiến trình có tầm quan trọng +thấp nhất bị loại bỏ trước, rồi đến những tiến trình có tầm quan trọng thấp thứ hai, và cứ thế tiếp tục, miễn là còn cần thiết +để khôi phục tài nguyên của hệ thống.

+ +

Có năm cấp trong phân cấp tầm quan trọng. Danh sách sau trình bày các loại +tiến trình khác nhau theo thứ tự tầm quan trọng (tiến trình thứ nhất là quan trọng nhất và được +tắt bỏ sau cùng):

+ +
    +
  1. Tiến trình tiền cảnh +

    Một tiến trình được yêu cầu cho việc mà người dùng đang thực hiện. Một + tiến trình được coi là đang trong tiền cảnh nếu bất kỳ điều nào sau đây là đúng:

    + +
      +
    • Nó lưu trữ một {@link android.app.Activity} mà người dùng đang tương tác với (phương pháp của {@link +android.app.Activity}, {@link android.app.Activity#onResume onResume()}, đã được +gọi).
    • + +
    • Nó lưu trữ một {@link android.app.Service} gắn kết với hoạt động mà người dùng đang +tương tác với.
    • + +
    • Nó lưu trữ một {@link android.app.Service} đang chạy "trong tiền cảnh"—mà +dịch vụ đã gọi {@link android.app.Service#startForeground startForeground()}. + +
    • Nó lưu trữ một {@link android.app.Service} mà đang thực thi một trong các lệnh +gọi lại của vòng đời của nó ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart +onStart()}, hoặc {@link android.app.Service#onDestroy onDestroy()}).
    • + +
    • Nó lưu trữ một {@link android.content.BroadcastReceiver} mà đang thực thi phương pháp {@link + android.content.BroadcastReceiver#onReceive onReceive()} của nó.
    • +
    + +

    Nhìn chung, tại bất kỳ thời điểm xác định nào cũng chỉ tồn tại một vài tiến trình tiền cảnh. Chúng chỉ bị tắt bỏ +như một giải pháp cuối cùng—nếu bộ nhớ quá thấp tới mức chúng đều không thể tiếp tục chạy được. Nhìn chung, tại thời điểm +đó, thiết bị đã đạt tới trạng thái phân trang bộ nhớ, vì thế việc tắt bỏ một số tiến trình tiền cảnh là +bắt buộc để đảm bảo giao diện người dùng có phản hồi.

  2. + +
  3. Tiến trình hiển thị +

    Một tiến trình mà không có bất kỳ thành phần tiền cảnh nào, nhưng vẫn có thể + ảnh hưởng tới nội dung mà người dùng nhìn thấy trên màn hình. Một tiến trình được coi là hiển thị nếu một trong hai + điều kiện sau là đúng:

    + +
      +
    • Nó lưu trữ một {@link android.app.Activity} mà không nằm trong tiền cảnh, nhưng vẫn +hiển thị với người dùng (phương pháp {@link android.app.Activity#onPause onPause()} của nó đã được gọi). +Điều này có thể xảy ra, ví dụ, nếu hoạt động tiền cảnh đã bắt đầu một hộp thoại, nó cho phép +hoạt động trước được nhìn thấy phía sau nó.
    • + +
    • Nó lưu trữ một {@link android.app.Service} được gắn kết với một hoạt động +hiển thị (hoặc tiền cảnh).
    • +
    + +

    Một tiến trình tiền cảnh được coi là cực kỳ quan trọng và sẽ không bị tắt bỏ trừ khi làm vậy +là bắt buộc để giữ cho tất cả tiến trình tiền cảnh chạy.

    +
  4. + +
  5. Tiến trình dịch vụ +

    Một tiến trình mà đang chạy một dịch vụ đã được bắt đầu bằng phương pháp {@link +android.content.Context#startService startService()} và không rơi vào một trong hai +thể loại cao hơn. Mặc dù tiến trình dịch vụ không trực tiếp gắn với bất kỳ thứ gì mà người dùng thấy, chúng +thường đang làm những việc mà người dùng quan tâm đến (chẳng hạn như phát nhạc chạy ngầm hoặc +tải xuống dữ liệu trên mạng), vì thế hệ thống vẫn giữ chúng chạy trừ khi không có đủ bộ nhớ để +duy trì chúng cùng với tất cả tiến trình tiền cảnh và hiển thị.

    +
  6. + +
  7. Tiến trình nền +

    Một tiến trình lưu trữ một hoạt động mà hiện tại không hiển thị với người dùng (phương pháp +{@link android.app.Activity#onStop onStop()} của hoạt động đã được gọi). Những tiến trình này không có tác động +trực tiếp tới trải nghiệm người dùng, và hệ thống có thể bỏ chúng đi vào bất cứ lúc nào để lấy lại bộ nhớ cho một +tiến trình tiền cảnh, +hiển thị hoặc dịch vụ. Thường thì có nhiều tiến trình ngầm đang chạy, vì thế chúng được giữ +trong một danh sách LRU (ít sử dụng gần đây nhất) để đảm bảo rằng tiến trình với hoạt động +mà người dùng nhìn thấy gần đây nhất là tiến trình cuối cùng sẽ bị tắt bỏ. Nếu một hoạt động triển khai các phương pháp vòng đời của nó +đúng cách, và lưu trạng thái hiện tại của nó, việc tắt bỏ tiến trình của hoạt động đó sẽ không có ảnh hưởng có thể thấy được tới +trải nghiệm người dùng, vì khi người dùng điều hướng lại hoạt động đó, hoạt động sẽ khôi phục +tất cả trạng thái hiển thị của nó. Xem tài liệu Hoạt động +để biết thông tin về việc lưu và khôi phục trạng thái.

    +
  8. + +
  9. Tiến trình trống +

    Một tiến trình mà không giữ bất kỳ thành phần ứng dụng hiện hoạt nào. Lý do duy nhất để giữ cho +kiểu tiến trình này hoạt động đó là nhằm mục đích lưu bộ nhớ ẩn, để cải thiện thời gian khởi động vào lần tới khi thành phần +cần chạy trong nó. Hệ thống thường tắt bỏ những tiến trình này để cân bằng tài nguyên tổng thể +của hệ thống giữa các bộ đệm ẩn tiến trình và bộ đệm ẩn nhân liên quan.

    +
  10. +
+ + +

Android xếp hạng một tiến trình ở mức cao nhất mà nó có thể, dựa vào tầm quan trọng của +các thành phần đang hoạt động trong tiến trình đó. Ví dụ, nếu một tiến trình lưu giữ một dịch vụ và hoạt động +hiển thị, tiến trình đó sẽ được xếp hạng là tiến trình hiển thị chứ không phải tiến trình dịch vụ.

+ +

Ngoài ra, xếp hạng của một tiến trình có thể tăng bởi các tiến trình khác phụ thuộc vào +nó—một tiến trình mà đang phục vụ một tiến trình khác không thể bị xếp thấp hơn tiến trình mà nó +đang phục vụ. Ví dụ, nếu một trình cung cấp nội dung trong tiến trình A đang phục vụ một máy khách trong tiến trình B, hoặc nếu một +dịch vụ trong tiến trình A được gắn kết với một thành phần trong tiến trình B, ít nhất tiến trình A sẽ luôn được coi +là quan trọng như tiến trình B.

+ +

Do một tiến trình đang chạy một dịch vụ được xếp hạng cao hơn một tiến trình có các hoạt động nền, +một hoạt động mà khởi động một thao tác nhấp giữ có thể làm tốt việc khởi động một dịch vụ cho thao tác đó, thay vì +chỉ tạo một luồng trình thực hiện—nhất là khi thao tác đó sẽ có thể diễn ra lâu hơn hoạt động. +Ví dụ, một hoạt động mà đang tải một ảnh lên một trang web nên bắt đầu một dịch vụ để thực hiện +việc tải lên sao cho việc tải lên có thể tiếp tục chạy ngầm ngay cả khi người dùng rời khỏi hoạt động. +Việc sử dụng một dịch vụ sẽ bảo đảm rằng thao tác ít nhất sẽ có mức ưu tiên như "tiến trình dịch vụ", +không phụ thuộc vào điều xảy ra với hoạt động. Đây cũng chính là lý do hàm nhận quảng bá nên +sử dụng dịch vụ thay vì chỉ đưa các thao tác tốn thời gian vào một luồng.

+ + + + +

Luồng

+ +

Khi một ứng dụng được khởi chạy, hệ thống sẽ tạo một luồng thực thi cho ứng dụng, +gọi là luồng "chính." Luồng này rất quan trọng bởi nó phụ trách phân phối các sự kiện tới +những widget giao diện người dùng phù hợp, bao gồm các sự kiện vẽ. Nó cũng là luồng mà +trong đó ứng dụng của bạn tương tác với các thành phần từ bộ công cụ UI của Android (các thành phần từ các gói {@link +android.widget} và {@link android.view}). Như vậy, luồng chính đôi khi cũng được gọi là +luồng UI.

+ +

Hệ thống không tạo một luồng riêng cho từng thực thể của thành phần. Tất cả +thành phần chạy trong cùng tiến trình đều được khởi tạo trong luồng UI, và các lệnh gọi của hệ thống tới +từng thành phần được phân phối từ luồng đó. Hệ quả là các phương pháp hồi đáp lại lệnh +gọi lại của hệ thống (chẳng hạn như {@link android.view.View#onKeyDown onKeyDown()} để báo cáo hành động của người dùng +hoặc một phương pháp gọi lại vòng đời) sẽ luôn chạy trong luồng UI của tiến trình.

+ +

Ví dụ, khi người dùng chạm vào một nút trên màn hình, luồng UI của ứng dụng của bạn sẽ phân phối +sự kiện chạm tới widget, đến lượt mình, widget sẽ đặt trạng thái được nhấn và đăng một yêu cầu vô hiệu hóa tới +hàng đợi sự kiện. Luồng UI loại yêu cầu khỏi hàng đợi và thông báo với widget rằng nó nên tự vẽ lại +.

+ +

Khi ứng dụng của bạn thực hiện công việc nặng để hồi đáp tương tác của người dùng, mô hình luồng đơn nhất +này có thể dẫn đến hiệu năng kém trừ khi bạn triển khai ứng dụng của mình một cách phù hợp. Cụ thể, nếu +mọi thứ đang xảy ra trong luồng UI, việc thực hiện những thao tác kéo dài như truy cập mạng hay +truy vấn cơ sở dữ liệu sẽ chặn toàn bộ UI. Khi luồng bị chặn, không sự kiện nào có thể được phân phối, +bao gồm cả sự kiện vẽ. Từ phương diện của người dùng, ứng dụng +có vẻ như đang bị treo. Thậm chí tệ hơn, nếu luồng UI bị chặn trong lâu hơn vài giây +(hiện tại là khoảng 5 giây), người dùng sẽ được hiển thị hộp thoại không phổ biến "ứng dụng +không phản hồi" (ANR). Khi đó, người dùng có thể quyết định thoát ứng dụng của mình và gỡ cài đặt nó +nếu họ không thoải mái.

+ +

Ngoài ra, bộ công cụ UI của Android không an toàn với luồng. Vì vậy, bạn không được thao tác +UI của mình từ một luồng trình thực hiện—bạn phải thực hiện tất cả thao tác đối với giao diện người dùng của mình từ luồng +UI. Vì vậy, có hai quy tắc đơn giản đối với mô hình luồng đơn lẻ của Android:

+ +
    +
  1. Không được chặn luồng UI +
  2. Không được truy cập bộ công cụ UI của Android từ bên ngoài luồng UI +
+ +

Luồng trình thực hiện

+ +

Vì mô hình luồng đơn lẻ nêu trên, điều thiết yếu đối với tính phản hồi của UI +ứng dụng của bạn đó là bạn không được chặn luồng UI. Nếu bạn có thao tác cần thực hiện +không mang tính chất tức thời, bạn nên đảm bảo thực hiện chúng trong các luồng riêng (luồng “chạy ngầm" hoặc +"trình thực hiện").

+ +

Ví dụ, bên dưới là một số mã cho một đối tượng theo dõi nhấp có chức năng tải xuống một hình ảnh từ một luồng +riêng và hiển thị nó trong một {@link android.widget.ImageView}:

+ +
+public void onClick(View v) {
+    new Thread(new Runnable() {
+        public void run() {
+            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
+            mImageView.setImageBitmap(b);
+        }
+    }).start();
+}
+
+ +

Thoạt đầu, điều này có vẻ như diễn ra ổn thỏa, vì nó tạo một luồng mới để xử lý thao tác +mạng. Tuy nhiên, nó vi phạm quy tắc thứ hai của mô hình luồng đơn nhất: không được truy cập +bộ công cụ UI của Android từ bên ngoài luồng UI—mẫu này sửa đổi {@link +android.widget.ImageView} từ luồng trình thực hiện thay vì từ luồng UI. Điều này có thể dẫn đến +hành vi bất ngờ, không được định nghĩa mà có thể gây khó khăn và tốn thời gian theo dõi.

+ +

Để sửa vấn đề này, Android giới thiệu một vài cách để truy cập luồng UI từ các luồng +khác. Sau đây là một danh sách các phương pháp có thể trợ giúp:

+ +
    +
  • {@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}
  • +
  • {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}
  • +
  • {@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}
  • +
+ +

Ví dụ, bạn có thể sửa mã trên bằng cách sử dụng phương pháp {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)}:

+ +
+public void onClick(View v) {
+    new Thread(new Runnable() {
+        public void run() {
+            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
+            mImageView.post(new Runnable() {
+                public void run() {
+                    mImageView.setImageBitmap(bitmap);
+                }
+            });
+        }
+    }).start();
+}
+
+ +

Giờ thì triển khai này đã an toàn với luồng: thao tác mạng được thực hiện từ một luồng riêng +trong khi {@link android.widget.ImageView} được thao tác từ luồng UI.

+ +

Tuy nhiên, khi mà sự phức tạp của thao tác tăng lên, kiểu mã này có thể bị phức tạp hóa và +khó duy trì. Để xử lý những tương tác phức tạp hơn bằng một luồng trình thực hiện, bạn có thể cân nhắc +sử dụng một {@link android.os.Handler} trong luồng trình thực hiện của mình, để xử lý các thư được chuyển từ luồng +UI. Mặc dù vậy, giải pháp tốt nhất là mở rộng lớp {@link android.os.AsyncTask}, +điều này sẽ đơn giản hóa việc thực thi các tác vụ của luồng trình thực hiện cần tương tác với UI.

+ + +

Sử dụng AsyncTask

+ +

{@link android.os.AsyncTask} cho phép bạn thực hiện công việc không đồng bộ trên giao diện +người dùng của mình. Nó thực hiện các thao tác chặn trong một luồng trình thực hiện rồi phát hành kết quả trên +luồng UI mà không yêu cầu bạn tự xử lý các luồng và/hoặc trình xử lý.

+ +

Để sử dụng nó, bạn phải tạo lớp con {@link android.os.AsyncTask} và triển khai phương pháp gọi lại {@link +android.os.AsyncTask#doInBackground doInBackground()}, phương pháp này chạy trong một tập hợp +các luồng chạy ngầm. Để cập nhật UI của mình, bạn nên triển khai {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, nó sẽ mang lại kết quả từ {@link +android.os.AsyncTask#doInBackground doInBackground()} và chạy trong luồng UI, vì thế bạn có thể nâng cấp +UI của mình một cách an toàn. Sau đó, bạn có thể chạy tác vụ bằng cách gọi {@link android.os.AsyncTask#execute execute()} +từ luồng UI.

+ +

Ví dụ, bạn có thể triển khai ví dụ trước bằng cách sử dụng {@link android.os.AsyncTask} theo +cách này:

+ +
+public void onClick(View v) {
+    new DownloadImageTask().execute("http://example.com/image.png");
+}
+
+private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
+    /** The system calls this to perform work in a worker thread and
+      * delivers it the parameters given to AsyncTask.execute() */
+    protected Bitmap doInBackground(String... urls) {
+        return loadImageFromNetwork(urls[0]);
+    }
+    
+    /** The system calls this to perform work in the UI thread and delivers
+      * the result from doInBackground() */
+    protected void onPostExecute(Bitmap result) {
+        mImageView.setImageBitmap(result);
+    }
+}
+
+ +

Lúc này, UI an toàn và mã đơn giản hơn, vì nó tách riêng công việc thành +phần sẽ được thực hiện trên một luồng trình thực hiện và phần sẽ được thực hiện trên luồng UI.

+ +

Bạn nên đọc tài liệu tham khảo {@link android.os.AsyncTask}để hiểu đầy đủ về +cách sử dụng lớp này, nhưng sau đây là phần trình bày tổng quan nhanh về hoạt động của nó:

+ +
    +
  • Bạn có thể quy định loại tham số, các giá trị tiến độ, và giá trị +cuối cùng của tác vụ, bằng cách sử dụng các kiểu chung
  • +
  • Phương pháp {@link android.os.AsyncTask#doInBackground doInBackground()} sẽ tự động thực thi +trên một luồng trình thực hiện
  • +
  • {@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, và {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} đều được gọi ra trên luồng UI
  • +
  • Giá trị được trả về bởi {@link android.os.AsyncTask#doInBackground doInBackground()} được gửi tới +{@link android.os.AsyncTask#onPostExecute onPostExecute()}
  • +
  • Bạn có thể gọi {@link android.os.AsyncTask#publishProgress publishProgress()} vào bất cứ lúc nào trong {@link +android.os.AsyncTask#doInBackground doInBackground()} để thực thi {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} trên luồng UI
  • +
  • Bạn có thể hủy bỏ tác vụ vào bất cứ lúc nào từ bất kỳ luồng nào
  • +
+ +

Chú ý: Một vấn đề khác mà bạn có thể gặp phải khi sử dụng một luồng +trình thực hiện đó là những lần khởi động lại bất ngờ trong hoạt động của bạn do một thay đổi trong cấu hình thời gian chạy +(chẳng hạn như khi người dùng thay đổi hướng màn hình), điều này có thể làm hỏng luồng trình thực hiện của bạn. Để +xem cách bạn có thể duy trì tác vụ của mình khi diễn ra một trong những lần khởi động lại này và cách hủy bỏ tác vụ cho phù hợp +khi hoạt động bị hủy, hãy xem mã nguồn cho ứng dụng mẫu Shelves.

+ + +

Phương pháp an toàn với luồng

+ +

Trong một số tình huống, các phương pháp bạn triển khai có thể được gọi ra từ nhiều hơn một luồng, và vì thế +phải được ghi sao cho an toàn với luồng.

+ +

Điều này chủ yếu đúng với các phương pháp mà có thể được gọi từ xa—chẳng hạn như các phương pháp trong một dịch vụ gắn kết. Khi một lệnh gọi trên một +phương pháp được triển khai trong một {@link android.os.IBinder} khởi đầu trong cùng tiến trình mà +{@link android.os.IBinder IBinder} đang chạy, phương pháp đó sẽ được thực thi trong luồng của hàm gọi. +Tuy nhiên, khi lệnh gọi khởi đầu trong một tiến trình khác, phương pháp sẽ được thực thi trong một luồng được chọn từ +một tập hợp các luồng mà hệ thống duy trì trong cùng tiến trình như {@link android.os.IBinder +IBinder} (nó không được thực thi trong luồng UI của tiến trình). Ví dụ, trong khi phương pháp +{@link android.app.Service#onBind onBind()} của dịch vụ sẽ được gọi từ luồng UI của tiến trình +của dịch vụ, các phương pháp được triển khai trong đối tượng mà {@link android.app.Service#onBind +onBind()} trả về (ví dụ, một lớp con triển khai các phương pháp RPC) sẽ được gọi từ các luồng +trong tập hợp. Vì một dịch vụ có thể có nhiều hơn một máy khách, nhiều hơn một luồng tập hợp có thể sử dụng +cùng một phương pháp {@link android.os.IBinder IBinder} tại cùng một thời điểm. Vì thế, các phương pháp {@link android.os.IBinder +IBinder} phải được triển khai sao cho an toàn với luồng.

+ +

Tương tự, một trình cung cấp nội dung có thể nhận các yêu cầu dữ liệu khởi nguồn trong các tiến trình khác. +Mặc dù các lớp {@link android.content.ContentResolver} và {@link android.content.ContentProvider} +ẩn đi chi tiết về cách truyền thông liên tiến trình được quản lý, các phương pháp {@link +android.content.ContentProvider} hồi đáp những yêu cầu đó—các phương pháp {@link +android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert +insert()}, {@link android.content.ContentProvider#delete delete()}, {@link +android.content.ContentProvider#update update()}, và {@link android.content.ContentProvider#getType +getType()}—được gọi từ một tập hợp luồng trong tiến trình của trình cung cấp nội dung, chứ không phải luồng +UI cho tiến trình đó. Vì những phương pháp này có thể được gọi từ bất kỳ số lượng luồng nào tại +cùng thời điểm, chúng cũng phải được triển khai sao cho an toàn với luồng.

+ + +

Truyền thông Liên Tiến trình

+ +

Android cung cấp một cơ chế cho truyền thông liên tiến trình (IPC) bằng cách sử dụng các lệnh gọi thủ tục từ xa +(RPC), trong đó một phương pháp được gọi bởi một hoạt động hoặc thành phần ứng dụng khác, nhưng được thực thi +từ xa (trong một tiến trình khác), với bất kỳ kết quả nào được trả về +hàm gọi. Điều này đòi hỏi việc phân tích một lệnh gọi phương pháp và dữ liệu của nó về cấp độ mà hệ điều hành +có thể hiểu được, truyền phát nó từ tiến trình và khoảng trống địa chỉ cục bộ đến tiến trình và +khoảng trống địa chỉ từ xa, sau đó tổ hợp lại và phát hành lại lệnh gọi ở đó. Sau đó, các giá trị trả về được +phát theo hướng ngược lại. Android cung cấp tất cả mã để thực hiện những giao tác +IPC này, vì thế bạn có thể tập trung vào việc định nghĩa và triển khai giao diện lập trình RPC.

+ +

Để thực hiện IPC, ứng dụng của bạn phải liên kết với một dịch vụ, bằng cách sử dụng {@link +android.content.Context#bindService bindService()}. Để biết thêm thông tin, hãy xem hướng dẫn cho nhà phát triển Dịch vụ.

+ + + diff --git a/docs/html-intl/intl/vi/guide/components/recents.jd b/docs/html-intl/intl/vi/guide/components/recents.jd new file mode 100644 index 0000000000000000000000000000000000000000..0a176145f9caa72d9b2b19552d2da69ac5340016 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=Màn hình Tổng quan +page.tags="recents","overview" + +@jd:body + + + +

Màn hình tổng quan (còn được gọi là màn hình gần đây, danh sách tác vụ gần đây, hay ứng dụng gần đây) +là một UI cấp hệ thống liệt kê các +hoạt độngtác vụ mới được truy cập gần đây. Người dùng +có thể điều hướng qua danh sách này và chọn một tác vụ để tiếp tục, hoặc người dùng có thể loại bỏ một tác vụ khỏi +danh sách bằng cách trượt nhanh nó đi. Với việc phát hành Android 5.0 (API mức 21), nhiều thực thể của +hoạt động tương tự chứa các tài liệu khác nhau có thể xuất hiện dưới dạng các tác vụ trong màn hình tổng quan. Ví dụ, +Google Drive có thể có một tác vụ cho từng tài liệu trong một vài tài liệu Google. Mỗi tài liệu xuất hiện thành một +tác vụ trong màn hình tổng quan.

+ + +

Hình 1. Màn hình tổng quan hiển thị ba tài liệu Google Drive +, mỗi tài liệu được biểu diễn như một tác vụ riêng.

+ +

Thường thì bạn sẽ cho phép hệ thống định nghĩa cách tác vụ và +hoạt động của mình được biểu diễn như thế nào trong màn hình tổng quan, và bạn không cần sửa đổi hành vi này. +Tuy nhiên, ứng dụng của bạn có thể xác định cách thức và thời gian các hoạt động xuất hiện trong màn hình tổng quan. Lớp +{@link android.app.ActivityManager.AppTask} cho phép bạn quản lý tác vụ, và cờ hoạt động của +lớp {@link android.content.Intent} cho phép bạn quy định khi nào thì một hoạt động được thêm hoặc loại bỏ khỏi +màn hình tổng quan. Đồng thời, thuộc tính +<activity> cho phép bạn đặt hành vi trong bản kê khai.

+ +

Thêm Tác vụ vào Màn hình Tổng quan

+ +

Sử dụng cờ của lớp {@link android.content.Intent} để thêm một tác vụ cho phép kiểm soát nhiều hơn +đối với thời điểm và cách thức một tài liệu được mở hoặc mở lại trong màn hình tổng quan. Khi sử dụng các thuộc tính +<activity> +, bạn có thể chọn giữa luôn mở tài liệu trong một tác vụ mới hoặc sử dụng lại một +tác vụ hiện có cho tài liệu.

+ +

Sử dụng cờ Ý định để thêm một tác vụ

+ +

Khi tạo một tài liệu mới cho hoạt động của bạn, bạn gọi phương pháp +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +của lớp {@link android.app.ActivityManager.AppTask}, chuyển cho nó ý định có +chức năng khởi chạy hoạt động. Để chèn một ngắt lô-gic sao cho hệ thống coi hoạt động của bạn như một tác vụ +mới trong màn hình tổng quan, hãy chuyển cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +trong phương pháp {@link android.content.Intent#addFlags(int) addFlags()} của {@link android.content.Intent} +có chức năng khởi chạy hoạt động.

+ +

Lưu ý: Cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +thay thế cờ {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, +được rút bớt kể từ phiên bản Android 5.0 (API mức 21).

+ +

Nếu bạn đặt cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} khi tạo +tài liệu mới, hệ thống sẽ luôn tạo một tác vụ mới lấy hoạt động mục tiêu đó làm gốc. +Thiết đặt này cho phép mở cùng tài liệu trong nhiều hơn một tác vụ. Đoạn mã sau thể hiện +cách mà hoạt động chính thực hiện điều này:

+ +

+DocumentCentricActivity.java

+
+public void createNewDocument(View view) {
+      final Intent newDocumentIntent = newDocumentIntent();
+      if (useMultipleTasks) {
+          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+      }
+      startActivity(newDocumentIntent);
+  }
+
+  private Intent newDocumentIntent() {
+      boolean useMultipleTasks = mCheckbox.isChecked();
+      final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
+      newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+      newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
+      return newDocumentIntent;
+  }
+
+  private static int incrementAndGet() {
+      Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
+      return mDocumentCounter++;
+  }
+}
+
+ +

Lưu ý: Các hoạt động được khởi chạy bằng cờ {@code FLAG_ACTIVITY_NEW_DOCUMENT} +phải có giá trị thuộc tính {@code android:launchMode="standard"} (mặc định) được đặt trong +bản kê khai.

+ +

Khi hoạt động chính khởi chạy một hoạt động mới, hệ thống sẽ tìm kiếm thông qua các tác vụ hiện tại để +xem tác vụ nào có ý định khớp với tên thành phần ý định và dữ liệu Ý định cho hoạt động đó. Nếu tác vụ +không được tìm thấy, hoặc ý định chứa cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} +thì một tác vụ mới sẽ được tạo lấy hoạt động làm gốc. Nếu tìm thấy, nó sẽ mang tác vụ đó +tới phía trước và chuyển ý định mới tới {@link android.app.Activity#onNewIntent onNewIntent()}. +Hoạt động mới sẽ nhận ý định và tạo một tài liệu mới trong màn hình tổng quan, như trong ví dụ +sau:

+ +

+NewDocumentActivity.java

+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_new_document);
+    mDocumentCount = getIntent()
+            .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
+    mDocumentCounterTextView = (TextView) findViewById(
+            R.id.hello_new_document_text_view);
+    setDocumentCounterText(R.string.hello_new_document_counter);
+}
+
+@Override
+protected void onNewIntent(Intent intent) {
+    super.onNewIntent(intent);
+    /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
+    is reused to create a new document.
+     */
+    setDocumentCounterText(R.string.reusing_document_counter);
+}
+
+ + +

Sử dụng thuộc tính hoạt động để thêm một tác vụ

+ +

Một hoạt động cũng có thể quy định trong bản kê khai của nó rằng nó luôn khởi chạy vào một tác vụ mới bằng cách sử dụng +thuộc tính <activity> +, +{@code android:documentLaunchMode}. Thuộc tính này có bốn giá trị tạo ra hiệu ứng +sau khi người dùng mở một tài liệu bằng ứng dụng:

+ +
+
"{@code intoExisting}"
+
Hoạt động sử dụng lại một tác vụ hiện có cho tài liệu. Điều này giống như khi thiết đặt cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} mà không thiết đặt cờ + {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, như được mô tả trong phần + Sử dụng cờ Ý định để thêm một tác vụ bên trên.
+ +
"{@code always}"
+
Hoạt động tạo một tác vụ mới cho tài liệu, ngay cả khi tài liệu đã được mở. Sử dụng + giá trị này giống như thiết đặt cả cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + và {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.
+ +
"{@code none”}"
+
Hoạt động không tạo một tác vụ mới cho tài liệu. Màn hình tổng quan xử lý hoạt động + như theo mặc định: nó hiển thị một tác vụ đơn lẻ cho ứng dụng, tác vụ này + tiếp tục từ bất kỳ hoạt động nào mà người dùng đã gọi ra cuối cùng.
+ +
"{@code never}"
+
Hoạt động không tạo một tác vụ mới cho tài liệu. Việc thiết đặt giá trị này sẽ khống chế + hành vi của {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + và cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, nếu một trong hai được đặt + trong ý định, và màn hình tổng quan sẽ hiển thị một tác vụ đơn lẻ cho ứng dụng, tác vụ này tiếp tục từ + bất kỳ hoạt động nào mà người dùng đã gọi ra cuối cùng.
+
+ +

Lưu ý: Đối với những giá trị ngoài {@code none} và {@code never} +hoạt động phải được định nghĩa bằng {@code launchMode="standard"}. Nếu thuộc tính này không được quy định thì +{@code documentLaunchMode="none"} sẽ được sử dụng.

+ +

Loại bỏ Tác vụ

+ +

Theo mặc định, một tác vụ tài liệu sẽ tự động được loại bỏ khỏi màn hình tổng quan khi hoạt động của nó +hoàn thành. Bạn có thể khống chế hành vi này bằng lớp {@link android.app.ActivityManager.AppTask}, +bằng một cờ {@link android.content.Intent}, hoặc bằng một thuộc tính +<activity>.

+ +

Bạn có thể luôn loại trừ hoàn toàn một tác vụ khỏi màn hình tổng quan bằng cách thiết đặt thuộc tính +<activity> +, +{@code android:excludeFromRecents} thành {@code true}.

+ +

Bạn có thể thiết đặt số lượng tác vụ tối đa mà ứng dụng của bạn có thể bao gồm trong màn hình tổng quan bằng cách đặt thuộc tính + <activity> + {@code android:maxRecents} + thành một giá trị số nguyên. Mặc định là 16. Khi đạt được số tác vụ tối đa, +tác vụ ít sử dụng gần đây nhất sẽ bị loại bỏ khỏi màn hình tổng quan. Giá trị tối đa {@code android:maxRecents} +bằng 50 (25 trên các thiết bị có bộ nhớ thấp); giá trị thấp hơn 1 không hợp lệ.

+ +

Sử dụng lớp AppTask để loại bỏ tác vụ

+ +

Trong hoạt động mà tạo một tác vụ mới trong màn hình tổng quan, bạn có thể +quy định khi nào thì loại bỏ tác vụ và hoàn thành tất cả các hoạt động gắn liền với nó bằng cách gọi +phương pháp {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}.

+ +

+NewDocumentActivity.java

+
+public void onRemoveFromRecents(View view) {
+    // The document is no longer needed; remove its task.
+    finishAndRemoveTask();
+}
+
+ +

Lưu ý: Sử dụng phương pháp +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} +sẽ khống chế việc sử dụng tag {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, +như được trình bày ở bên dưới.

+ +

Giữ lại tác vụ đã hoàn thành

+ +

Nếu bạn muốn giữ lại một tác vụ trong màn hình tổng quan, ngay cả khi hoạt động của nó đã hoàn thành, hãy chuyển +cờ {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} trong phương pháp +{@link android.content.Intent#addFlags(int) addFlags()} của Ý định mà khởi chạy hoạt động.

+ +

+DocumentCentricActivity.java

+
+private Intent newDocumentIntent() {
+    final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
+    newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+      android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
+    newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
+    return newDocumentIntent;
+}
+
+ +

Để đạt được cùng kết quả như vậy, hãy đặt thuộc tính +<activity> + +{@code android:autoRemoveFromRecents} thành {@code false}. Giá trị mặc định bằng {@code true} +đối với các hoạt động tài liệu, và {@code false} đối với các hoạt động thông thường. Việc sử dụng thuộc tính này sẽ khống chế +cờ {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, như đã trình bày trước đó.

+ + + + + + + diff --git a/docs/html-intl/intl/vi/guide/components/services.jd b/docs/html-intl/intl/vi/guide/components/services.jd new file mode 100644 index 0000000000000000000000000000000000000000..9e3e6c75eccf589d42e2f92fb79be58ff0060f9d --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=Dịch vụ +@jd:body + + + + +

{@link android.app.Service} là một thành phần ứng dụng có khả năng thực hiện +các thao tác chạy kéo dài trong nền và không cung cấp giao diện người dùng. Một +thành phần ứng dụng khác có thể bắt đầu một dịch vụ và nó sẽ tiếp tục chạy ngầm ngay cả khi người dùng +chuyển sang một ứng dụng khác. Ngoài ra, một thành phần có thể gắn kết với một dịch vụ để +tương tác với nó và thậm chí thực hiện truyền thông liên tiến trình (IPC). Ví dụ, một dịch vụ có thể +xử lý các giao dịch mạng, phát nhạc, thực hiện I/O tệp, hoặc tương tác với một trình cung cấp nội dung, tất cả +đều xuất phát từ nền.

+ +

Về cơ bản, một dịch vụ có thể có hai dạng:

+ +
+
Được bắt đầu
+
Dịch vụ có dạng "được bắt đầu" khi một thành phần ứng dụng (chẳng hạn như một hoạt động) bắt đầu nó bằng cách +gọi {@link android.content.Context#startService startService()}. Sau khi được bắt đầu, dịch vụ +có thể chạy ngầm vô thời hạn, ngay cả khi thành phần bắt đầu nó bị hủy. Thông thường, +dịch vụ được bắt đầu sẽ thực hiện một thao tác đơn lẻ và không trả về kết quả cho hàm gọi. +Ví dụ, nó có thể tải xuống hoặc tải lên một tệp thông qua mạng. Khi thao tác được hoàn thành, dịch vụ +tự nó sẽ dừng lại.
+
Gắn kết
+
Dịch vụ có dạng "gắn kết" khi một thành phần ứng dụng gắn kết với nó bằng cách gọi {@link +android.content.Context#bindService bindService()}. Dịch vụ gắn kết sẽ đưa ra +một giao diện máy khách-máy chủ cho phép các thành phần tương tác với dịch vụ, gửi yêu cầu, nhận kết quả, và thậm chí +làm vậy thông qua truyền thông liên tiến trình (IPC). Dịch vụ gắn kết chỉ chạy trong khi +một thành phần ứng dụng khác được gắn kết với nó. Nhiều thành phần có thể gắn kết cùng lúc với dịch vụ, +nhưng khi tất cả bị bỏ gắn kết thì dịch vụ sẽ bị hủy.
+
+ +

Mặc dù tài liệu này thường đề cập tới hai loại dịch vụ riêng rẽ, dịch vụ +của bạn có thể hoạt động theo cả hai cách—nó có thể được bắt đầu (để chạy vô thời hạn) và cũng cho phép gắn kết. +Đó đơn giản là vấn đề bạn có triển khai một cặp phương pháp gọi lại hay không: {@link +android.app.Service#onStartCommand onStartCommand()} để cho phép thành phần bắt đầu nó và {@link +android.app.Service#onBind onBind()} để cho phép nó gắn kết.

+ +

Không phụ thuộc vào việc ứng dụng của bạn được bắt đầu, gắn kết, hay cả hai, bất kỳ thành phần ứng dụng nào +cũng có thể sử dụng dịch vụ (thậm chí từ một ứng dụng riêng biệt), giống như cách mà bất kỳ thành phần nào cũng có thể sử dụng +một hoạt động—bằng cách bắt đầu nó bằng một {@link android.content.Intent}. Tuy nhiên, bạn có thể khai báo +dịch vụ là riêng tư trong tệp bản kê khai, và chặn truy cập từ các ứng dụng khác. Điều này +được trình bày kỹ hơn trong phần về Khai báo dịch vụ trong +bản kê khai.

+ +

Chú ý: Một dịch vụ chạy trong +luồng chính của tiến trình lưu trữ của nó—dịch vụ không tạo luồng của chính nó +và không chạy trong một tiến trình riêng biệt (trừ khi bạn quy định khác). Điều này có nghĩa +là, nếu dịch vụ của bạn định thực hiện bất kỳ công việc nặng nào đối với CPU hay chặn các thao tác (chẳng hạn như phát lại MP3 +hay kết nối mạng), bạn nên tạo một luồng mới bên trong dịch vụ để thực hiện công việc đó. Bằng cách sử dụng +một luồng riêng biệt, bạn sẽ giảm rủi ro gặp lỗi Ứng dụng Không Hồi đáp (ANR) và luồng chính của ứng dụng có thể +vẫn dành riêng cho tương tác giữa người dùng với các hoạt động của bạn.

+ + +

Nội dung Cơ bản

+ + + +

Để tạo một dịch vụ, bạn phải tạo một lớp con của {@link android.app.Service} (hoặc một +trong các lớp con hiện tại của nó). Trong triển khai của mình, bạn cần khống chế một số phương pháp gọi lại có chức năng +xử lý những khía cạnh chính trong vòng đời của dịch vụ và cung cấp một cơ chế để các thành phần gắn kết với +dịch vụ đó, nếu phù hợp. Những phương pháp gọi lại quan trọng nhất mà bạn nên khống chế là:

+ +
+
{@link android.app.Service#onStartCommand onStartCommand()}
+
Hệ thống sẽ gọi phương pháp này khi một thành phần khác, chẳng hạn như một hoạt động, +yêu cầu dịch vụ phải được bắt đầu, bằng cách gọi {@link android.content.Context#startService +startService()}. Sau khi phương pháp này thực thi, dịch vụ sẽ được bắt đầu và có thể chạy vô thời hạn trong +nền. Nếu bạn triển khai điều này, bạn có trách nhiệm dừng dịch vụ khi +công việc của nó được hoàn thành, bằng cách gọi {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}. (Nếu chỉ muốn cung cấp khả năng gắn kết, bạn không +cần triển khai phương pháp này.)
+
{@link android.app.Service#onBind onBind()}
+
Hệ thống sẽ gọi phương pháp này khi một thành phần khác muốn gắn kết với +dịch vụ (chẳng hạn như để thực hiện RPC), bằng cách gọi {@link android.content.Context#bindService +bindService()}. Trong triển khai phương pháp này của mình, bạn phải cung cấp một giao diện mà các máy khách +sử dụng để giao tiếp với dịch vụ, bằng cách trả về {@link android.os.IBinder}. Bạn phải luôn +triển khai phương pháp này, nhưng nếu bạn không muốn cho phép gắn kết thì bạn nên trả về rỗng.
+
{@link android.app.Service#onCreate()}
+
Hệ thống sẽ gọi phương pháp này khi dịch vụ được tạo lập lần đầu, để thực hiện quy trình thiết lập một lần +(trước khi nó có thể gọi hoặc {@link android.app.Service#onStartCommand onStartCommand()} hoặc +{@link android.app.Service#onBind onBind()}). Nếu dịch vụ đã đang chạy, phương pháp này sẽ không được +gọi.
+
{@link android.app.Service#onDestroy()}
+
Hệ thống sẽ gọi phương pháp này khi dịch vụ không còn được sử dụng và đang bị hủy. +Dịch vụ của bạn sẽ triển khai phương pháp này để dọn dẹp mọi tài nguyên như luồng, đối tượng theo dõi +được đăng ký, hàm nhận, v.v... Đây là lệnh gọi cuối cùng mà dịch vụ nhận được.
+
+ +

Nếu một thành phần bắt đầu dịch vụ bằng cách gọi {@link +android.content.Context#startService startService()} (kết quả là một lệnh gọi tới {@link +android.app.Service#onStartCommand onStartCommand()}), khi đó dịch vụ +sẽ vẫn chạy tới khi tự nó dừng bằng {@link android.app.Service#stopSelf()} hoặc một +thành phần khác dừng nó bằng cách gọi {@link android.content.Context#stopService stopService()}.

+ +

Nếu một thành phần gọi +{@link android.content.Context#bindService bindService()} để tạo dịch vụ (và {@link +android.app.Service#onStartCommand onStartCommand()} không được gọi), khi đó dịch vụ sẽ chỉ chạy +khi nào mà thành phần đó còn gắn kết với nó. Sau khi dịch vụ được bỏ gắn kết khỏi tất cả máy khách, hệ thống +sẽ hủy nó.

+ +

Hệ thống Android sẽ buộc dừng một dịch vụ chỉ khi bộ nhớ thấp và nó phải khôi phục tài nguyên +của hệ thống cho hoạt động có tiêu điểm của người dùng. Nếu dịch vụ gắn kết với một hoạt động mà có tiêu điểm +của người dùng, khi đó sẽ có ít khả năng nó sẽ bị tắt bỏ hơn, và nếu dịch vụ được khai báo là chạy trong tiền cảnh (đề cập sau), khi đó nó sẽ hầu như không bao giờ bị tắt bỏ. +Mặt khác, nếu dịch vụ được bắt đầu và chạy trong thời gian dài, hệ thống sẽ hạ thấp vị trí của nó +trong danh sách tác vụ chạy ngầm qua thời gian và dịch vụ sẽ rất có thể bị +tắt bỏ—nếu dịch vụ của bạn được bắt đầu, khi đó bạn phải thiết kế nó để +xử lý việc khởi động lại do hệ thống một cách uyển chuyển. Nếu hệ thống tắt bỏ dịch vụ của bạn, nó sẽ khởi động lại dịch vụ ngay khi tài nguyên +có sẵn trở lại (mặc dù điều này cũng phụ thuộc vào giá trị mà bạn trả về từ {@link +android.app.Service#onStartCommand onStartCommand()}, vấn đề này sẽ được bàn sau). Để biết thêm thông tin +về thời điểm mà hệ thống có thể hủy một dịch vụ, hãy xem tài liệu Tiến trình và Luồng +.

+ +

Trong những phần sau, bạn sẽ thấy cách bạn có thể tạo từng loại dịch vụ và cách sử dụng +nó từ các thành phần ứng dụng khác.

+ + + +

Khai báo một dịch vụ trong bản kê khai

+ +

Giống như hoạt động (và các thành phần khác), bạn phải khai báo tất cả dịch vụ trong tệp bản kê khai +của ứng dụng của mình.

+ +

Để khai báo dịch vụ của bạn, hãy thêm một phần tử {@code <service>} làm +con của phần tử {@code <application>} +. Ví dụ:

+ +
+<manifest ... >
+  ...
+  <application ... >
+      <service android:name=".ExampleService" />
+      ...
+  </application>
+</manifest>
+
+ +

Xem tham chiếu phần tử {@code <service>} +để biết thêm thông tin về việc khai báo dịch vụ của bạn trong bản kê khai.

+ +

Có các thuộc tính khác mà bạn có thể bao gồm trong phần tử {@code <service>} để +định nghĩa các tính chất chẳng hạn như những quyền cần để bắt đầu dịch vụ và tiến trình mà +dịch vụ sẽ chạy trong đó. Thuộc tính {@code android:name} +là thuộc tính bắt buộc duy nhất—nó quy định tên lớp của dịch vụ. Một khi +bạn phát hành ứng dụng của mình, bạn không nên thay đổi tên này, vì nếu bạn làm vậy, bạn sẽ gặp rủi ro làm gãy +mã do sự phụ thuộc vào các ý định biểu thị để bắt đầu hoặc gắn kết dịch vụ (đọc bài đăng blog, Những Điều +Không Thay Đổi Được). + +

Để đảm bảo ứng dụng của bạn được bảo mật, luôn sử dụng một ý định biểu thị khi bắt đầu hoặc gắn kết +{@link android.app.Service} của bạn và không được khai báo bộ lọc ý định cho dịch vụ. Nếu +điều trọng yếu là bạn phải cho phép một chút không rõ ràng về dịch vụ nào sẽ bắt đầu, bạn có thể +cung cấp bộ lọc ý định cho dịch vụ của mình và loại bỏ tên thành phần khỏi {@link +android.content.Intent}, nhưng sau đó bạn có thể đặt gói cho ý định bằng {@link +android.content.Intent#setPackage setPackage()}, điều này cung cấp sự không rõ ràng vừa đủ cho +dịch vụ mục tiêu đó.

+ +

Ngoài ra, bạn có thể đảm bảo rằng dịch vụ của mình chỉ sẵn có cho ứng dụng của bạn bằng cách +đưa vào thuộc tính {@code android:exported} +và đặt nó thành {@code "false"}. Điều này sẽ dừng việc các ứng dụng khác bắt đầu +dịch vụ của bạn, ngay cả khi sử dụng một ý định biểu thị.

+ + + + +

Tạo một Dịch vụ được Bắt đầu

+ +

Dịch vụ được bắt đầu là dịch vụ mà một thành phần khác bắt đầu bằng cách gọi {@link +android.content.Context#startService startService()}, kết quả là một lệnh gọi tới phương pháp +{@link android.app.Service#onStartCommand onStartCommand()} của dịch vụ.

+ +

Khi một dịch vụ được bắt đầu, nó có một vòng đời độc lập với +thành phần đã bắt đầu nó và dịch vụ có thể chạy ngầm vô thời hạn, ngay cả khi +thành phần bắt đầu nó bị hủy. Như vậy, dịch vụ sẽ tự dừng khi làm xong công việc của nó +bằng cách gọi {@link android.app.Service#stopSelf stopSelf()}, hoặc một thành phần khác có thể dừng nó +bằng cách gọi {@link android.content.Context#stopService stopService()}.

+ +

Một thành phần ứng dụng chẳng hạn như một hoạt động có thể bắt đầu dịch vụ bằng cách gọi {@link +android.content.Context#startService startService()} và chuyển một {@link android.content.Intent} +trong đó quy định dịch vụ và bao gồm bất kỳ dữ liệu nào để cho dịch vụ sử dụng. Dịch vụ sẽ nhận +{@link android.content.Intent} này trong phương pháp {@link android.app.Service#onStartCommand +onStartCommand()}.

+ +

Ví dụ, giả sử một hoạt động cần lưu một số dữ liệu vào cơ sở dữ liệu trực tuyến. Hoạt động có thể +bắt đầu một dịch vụ đồng hành và truyền cho nó dữ liệu để lưu bằng cách chuyển một ý định tới {@link +android.content.Context#startService startService()}. Dịch vụ sẽ nhận ý định trong {@link +android.app.Service#onStartCommand onStartCommand()}, kết nối với Internet và thực hiện +giao tác cơ sở dữ liệu. Khi giao tác được thực hiện, dịch vụ sẽ tự dừng lại và nó bị +hủy.

+ +

Chú ý: Một dịch vụ sẽ chạy trong cùng tiến trình như ứng dụng +mà nó được khai báo trong đó và trong luồng chính của ứng dụng đó theo mặc định. Vì vậy, nếu dịch vụ của bạn +thực hiện các thao tác tăng cường hoặc chặn trong khi người dùng tương tác với một hoạt động từ cùng +ứng dụng, dịch vụ sẽ làm chậm hiệu năng của hoạt động. Để tránh tác động tới hiệu năng của +ứng dụng, bạn nên bắt đầu một luồng mới bên trong dịch vụ.

+ +

Thông thường, có hai lớp mà bạn có thể mở rộng để tạo một dịch vụ được bắt đầu:

+
+
{@link android.app.Service}
+
Đây là lớp cơ bản cho tất cả dịch vụ. Khi bạn mở rộng lớp này, điều quan trọng là +bạn tạo một luồng mới để thực hiện tất cả công việc của dịch vụ trong đó, do dịch vụ sử dụng luồng chính +của ứng dụng của bạn, theo mặc định, điều này có thể làm chậm hiệu năng của bất kỳ hoạt động nào mà ứng dụng +của bạn đang chạy.
+
{@link android.app.IntentService}
+
Đây là một lớp con của {@link android.app.Service} có chức năng sử dụng một luồng trình thực hiện để xử lý tất cả +yêu cầu bắt đầu một cách lần lượt. Đây là lựa chọn tốt nhất nếu bạn không yêu cầu dịch vụ của mình +xử lý đồng thời nhiều yêu cầu. Tất cả những gì bạn cần làm đó là triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, nó sẽ nhận ý định cho mỗi +yêu cầu bắt đầu để bạn có thể thực hiện công việc chạy ngầm.
+
+ +

Các phần sau mô tả cách bạn có thể triển khai dịch vụ của mình bằng cách sử dụng một trong các cách cho những lớp +này.

+ + +

Mở rộng lớp IntentService

+ +

Vì phần lớn các dịch vụ được bắt đầu không cần xử lý nhiều yêu cầu một cách đồng thời +(điều này thực sự có thể là một kịch bản tạo đa luồng nguy hiểm), có lẽ tốt nhất là nếu bạn +triển khai dịch vụ của mình bằng cách sử dụng lớp {@link android.app.IntentService}.

+ +

{@link android.app.IntentService} làm điều sau đây:

+ +
    +
  • Tạo một luồng trình thực hiện mặc định để thực thi tất cả ý định được chuyển tới {@link +android.app.Service#onStartCommand onStartCommand()} tách riêng với luồng +chính của ứng dụng của bạn.
  • +
  • Tạo một hàng đợi công việc để chuyển lần lượt từng ý định tới triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} của bạn, vì thế bạn không bao giờ phải +lo lắng về vấn đề tạo đa luồng.
  • +
  • Dừng dịch vụ sau khi tất cả yêu cầu bắt đầu đều đã được xử lý, vì thế bạn không bao giờ phải gọi +{@link android.app.Service#stopSelf}.
  • +
  • Cung cấp triển khai mặc định của {@link android.app.IntentService#onBind onBind()} mà +trả về rỗng.
  • +
  • Cung cấp triển khai mặc định của {@link android.app.IntentService#onStartCommand +onStartCommand()} mà gửi ý định tới hàng đợi công việc rồi tới triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} của bạn.
  • +
+ +

Tất cả đều nói lên một thực tế rằng tất cả những việc bạn cần làm đó là triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} để thực hiện công việc mà +máy khách cung cấp. (Mặc dù bạn cũng cần cung cấp một hàm dựng nhỏ cho dịch vụ.)

+ +

Sau đây là ví dụ về triển khai {@link android.app.IntentService}:

+ +
+public class HelloIntentService extends IntentService {
+
+  /**
+   * A constructor is required, and must call the super {@link android.app.IntentService#IntentService}
+   * constructor with a name for the worker thread.
+   */
+  public HelloIntentService() {
+      super("HelloIntentService");
+  }
+
+  /**
+   * The IntentService calls this method from the default worker thread with
+   * the intent that started the service. When this method returns, IntentService
+   * stops the service, as appropriate.
+   */
+  @Override
+  protected void onHandleIntent(Intent intent) {
+      // Normally we would do some work here, like download a file.
+      // For our sample, we just sleep for 5 seconds.
+      long endTime = System.currentTimeMillis() + 5*1000;
+      while (System.currentTimeMillis() < endTime) {
+          synchronized (this) {
+              try {
+                  wait(endTime - System.currentTimeMillis());
+              } catch (Exception e) {
+              }
+          }
+      }
+  }
+}
+
+ +

Đó là tất cả những gì bạn cần: một hàm dựng và triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.

+ +

Nếu bạn quyết định cũng khống chế các phương pháp gọi lại khác, chẳng hạn như {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()}, hoặc {@link +android.app.IntentService#onDestroy onDestroy()}, hãy nhớ gọi ra siêu triển khai, sao +cho {@link android.app.IntentService} có thể xử lý hợp lý vòng đời của luồng trình thực hiện.

+ +

Ví dụ, {@link android.app.IntentService#onStartCommand onStartCommand()} phải trả về +triển khai mặc định (đó là cách mà ý định được chuyển tới {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):

+ +
+@Override
+public int onStartCommand(Intent intent, int flags, int startId) {
+    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
+    return super.onStartCommand(intent,flags,startId);
+}
+
+ +

Bên cạnh {@link android.app.IntentService#onHandleIntent onHandleIntent()}, phương pháp duy nhất mà +từ đó bạn không cần gọi siêu lớp là {@link android.app.IntentService#onBind +onBind()} (nhưng bạn chỉ cần triển khai điều đó nếu dịch vụ của bạn cho phép gắn kết).

+ +

Trong phần tiếp theo, bạn sẽ thấy cách mà cùng loại dịch vụ được triển khai khi mở rộng +lớp {@link android.app.Service} cơ sở, nó có nhiều mã hơn nhưng có thể +phù hợp nếu bạn cần xử lý các yêu cầu bắt đầu đồng thời.

+ + +

Mở rộng lớp Dịch vụ

+ +

Như bạn thấy trong phần trước, sử dụng {@link android.app.IntentService} giúp việc +triển khai một dịch vụ được bắt đầu của bạn trở nên rất đơn giản. Tuy nhiên, nếu bạn cần dịch vụ của mình +thực hiện tạo đa luồng (thay vì xử lý các yêu cầu bắt đầu thông qua một hàng đợi công việc), khi đó bạn +có thể mở rộng lớp {@link android.app.Service} để xử lý từng ý định.

+ +

Để so sánh, đoạn mã mẫu sau là triển khai lớp {@link +android.app.Service} mà thực hiện chính xác cùng công việc như ví dụ bên trên bằng cách sử dụng {@link +android.app.IntentService}. Cụ thể, đối với mỗi yêu cầu bắt đầu, nó sẽ sử dụng một luồng trình thực hiện để thực hiện +công việc và chỉ xử lý lần lượt từng yêu cầu một.

+ +
+public class HelloService extends Service {
+  private Looper mServiceLooper;
+  private ServiceHandler mServiceHandler;
+
+  // Handler that receives messages from the thread
+  private final class ServiceHandler extends Handler {
+      public ServiceHandler(Looper looper) {
+          super(looper);
+      }
+      @Override
+      public void handleMessage(Message msg) {
+          // Normally we would do some work here, like download a file.
+          // For our sample, we just sleep for 5 seconds.
+          long endTime = System.currentTimeMillis() + 5*1000;
+          while (System.currentTimeMillis() < endTime) {
+              synchronized (this) {
+                  try {
+                      wait(endTime - System.currentTimeMillis());
+                  } catch (Exception e) {
+                  }
+              }
+          }
+          // Stop the service using the startId, so that we don't stop
+          // the service in the middle of handling another job
+          stopSelf(msg.arg1);
+      }
+  }
+
+  @Override
+  public void onCreate() {
+    // Start up the thread running the service.  Note that we create a
+    // separate thread because the service normally runs in the process's
+    // main thread, which we don't want to block.  We also make it
+    // background priority so CPU-intensive work will not disrupt our UI.
+    HandlerThread thread = new HandlerThread("ServiceStartArguments",
+            Process.THREAD_PRIORITY_BACKGROUND);
+    thread.start();
+
+    // Get the HandlerThread's Looper and use it for our Handler
+    mServiceLooper = thread.getLooper();
+    mServiceHandler = new ServiceHandler(mServiceLooper);
+  }
+
+  @Override
+  public int onStartCommand(Intent intent, int flags, int startId) {
+      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
+
+      // For each start request, send a message to start a job and deliver the
+      // start ID so we know which request we're stopping when we finish the job
+      Message msg = mServiceHandler.obtainMessage();
+      msg.arg1 = startId;
+      mServiceHandler.sendMessage(msg);
+
+      // If we get killed, after returning from here, restart
+      return START_STICKY;
+  }
+
+  @Override
+  public IBinder onBind(Intent intent) {
+      // We don't provide binding, so return null
+      return null;
+  }
+
+  @Override
+  public void onDestroy() {
+    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
+  }
+}
+
+ +

Như bạn có thể thấy, có nhiều việc hơn nhiều so với việc sử dụng {@link android.app.IntentService}.

+ +

Tuy nhiên, do bạn tự mình xử lý từng lệnh gọi đến {@link android.app.Service#onStartCommand +onStartCommand()}, bạn có thể thực hiện nhiều yêu cầu một cách đồng thời. Đó không phải là việc +mà ví dụ này làm, nhưng nếu đó là việc bạn muốn, vậy bạn có thể tạo một luồng mới cho từng +yêu cầu và ngay lập tức trả chúng về (thay vì đợi tới khi yêu cầu trước hoàn thành).

+ +

Để ý rằng phương pháp {@link android.app.Service#onStartCommand onStartCommand()} phải trả về một +số nguyên. Số nguyên là một giá trị mô tả cách hệ thống nên tiếp tục dịch vụ trong +trường hợp hệ thống tắt bỏ nó (như được đề cập ở trên, triển khai mặc định cho {@link +android.app.IntentService} sẽ xử lý điều này cho bạn dù bạn có thể sửa đổi nó). Giá trị trả về +từ {@link android.app.Service#onStartCommand onStartCommand()} phải là một trong các +hằng số sau:

+ +
+
{@link android.app.Service#START_NOT_STICKY}
+
Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, không được tạo lại dịch vụ đó, trừ khi có các ý định +đang chờ để được chuyển. Đây là lựa chọn an toàn nhất để tránh chạy dịch vụ của bạn khi không cần thiết +và khi ứng dụng của bạn có thể đơn thuần khởi động lại bất kỳ công việc chưa hoàn thành nào.
+
{@link android.app.Service#START_STICKY}
+
Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, hãy tạo lại dịch vụ và gọi {@link +android.app.Service#onStartCommand onStartCommand()}, nhưng không chuyển lại ý định cuối cùng. +Thay vào đó, hệ thống sẽ gọi {@link android.app.Service#onStartCommand onStartCommand()} bằng một +ý định rỗng, trừ khi có các ý định đang chờ để bắt đầu dịch vụ, trong trường hợp đó, +những ý định này sẽ được chuyển. Điều này phù hợp với các trình phát phương tiện (hoặc dịch vụ tương tự) mà không +đang thực thi lệnh, nhưng đang chạy vô thời hạn và chờ một tác vụ.
+
{@link android.app.Service#START_REDELIVER_INTENT}
+
Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, hãy tạo lại dịch vụ và gọi {@link +android.app.Service#onStartCommand onStartCommand()} bằng ý định cuối cùng được chuyển tới +dịch vụ. Mọi ý định chờ đều được chuyển lần lượt. Điều này phù hợp với các dịch vụ đang +chủ động thực hiện một công việc mà nên được tiếp tục ngay lập tức, chẳng hạn như tải xuống một tệp.
+
+

Để biết thêm chi tiết về những giá trị trả về này, hãy xem tài liệu tham khảo được liên kết cho từng +hằng số.

+ + + +

Bắt đầu một Dịch vụ

+ +

Bạn có thể bắt đầu một dịch vụ từ một hoạt động hoặc thành phần ứng dụng khác bằng cách chuyển một +{@link android.content.Intent} (quy định dịch vụ sẽ bắt đầu) đến {@link +android.content.Context#startService startService()}. Hệ thống Android sẽ gọi phương pháp {@link +android.app.Service#onStartCommand onStartCommand()} của dịch vụ và chuyển cho nó {@link +android.content.Intent}. (Bạn tuyệt đối không nên trực tiếp gọi {@link android.app.Service#onStartCommand +onStartCommand()}.)

+ +

Ví dụ, một hoạt động có thể bắt đầu dịch vụ ví dụ trong phần trước ({@code +HelloSevice}) bằng cách sử dụng một ý định biểu thị với {@link android.content.Context#startService +startService()}:

+ +
+Intent intent = new Intent(this, HelloService.class);
+startService(intent);
+
+ +

Phương pháp {@link android.content.Context#startService startService()} ngay lập tức trả về và +hệ thống Android sẽ gọi phương pháp {@link android.app.Service#onStartCommand +onStartCommand()} của dịch vụ. Nếu dịch vụ không đang chạy, trước tiên hệ thống sẽ gọi {@link +android.app.Service#onCreate onCreate()}, rồi gọi {@link android.app.Service#onStartCommand +onStartCommand()}.

+ +

Nếu dịch vụ cũng không cung cấp khả năng gắn kết, ý định được chuyển bằng {@link +android.content.Context#startService startService()} sẽ là phương thức giao tiếp duy nhất giữa +thành phần ứng dụng và dịch vụ. Tuy nhiên, nếu bạn muốn dịch vụ gửi một kết quả trở lại, khi đó +máy khách mà bắt đầu dịch vụ có thể tạo một {@link android.app.PendingIntent} cho một quảng bá +(bằng {@link android.app.PendingIntent#getBroadcast getBroadcast()}) và chuyển nó tới dịch vụ +trong {@link android.content.Intent} mà bắt đầu dịch vụ. Khi đó, dịch vụ có thể sử dụng +quảng bá để chuyển kết quả.

+ +

Nhiều yêu cầu bắt đầu dịch vụ sẽ dẫn đến nhiều lệnh gọi tương ứng tới +{@link android.app.Service#onStartCommand onStartCommand()} của dịch vụ. Tuy nhiên, chỉ có một yêu cầu dừng +dịch vụ (bằng {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}) là bắt buộc để dừng nó.

+ + +

Dừng một dịch vụ

+ +

Dịch vụ được bắt đầu phải quản lý vòng đời của chính nó. Cụ thể, hệ thống không dừng +hay hủy dịch vụ trừ khi nó phải khôi phục bộ nhớ của hệ thống và dịch vụ +sẽ tiếp tục chạy sau khi {@link android.app.Service#onStartCommand onStartCommand()} trả về. Vì vậy, +dịch vụ phải tự dừng bằng cách gọi {@link android.app.Service#stopSelf stopSelf()} hoặc một thành phần +khác có thể dừng nó bằng cách gọi {@link android.content.Context#stopService stopService()}.

+ +

Sau khi được yêu cầu dừng bằng {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}, hệ thống sẽ hủy dịch vụ ngay khi +có thể.

+ +

Tuy nhiên, nếu dịch vụ của bạn xử lý nhiều yêu cầu {@link +android.app.Service#onStartCommand onStartCommand()} đồng thời, khi đó bạn không nên dừng +dịch vụ khi bạn đã hoàn thành xử lý yêu cầu bắt đầu, vì bạn có thể đã nhận được một +yêu cầu bắt đầu mới kể từ thời điểm đó (dừng khi kết thúc yêu cầu thứ nhất sẽ chấm dứt yêu cầu thứ hai). Để tránh +vấn đề này, bạn có thể sử dụng {@link android.app.Service#stopSelf(int)} để đảm bảo rằng yêu cầu +dừng dịch vụ của bạn luôn được dựa trên yêu cầu bắt đầu gần đây nhất. Cụ thể, khi bạn gọi {@link +android.app.Service#stopSelf(int)}, bạn sẽ chuyển ID của yêu cầu bắt đầu (startId +được chuyển tới {@link android.app.Service#onStartCommand onStartCommand()}) mà yêu cầu dừng của bạn +tương ứng với. Khi đó, nếu dịch vụ đã nhận được một yêu cầu bắt đầu mới trước khi bạn có thể gọi {@link +android.app.Service#stopSelf(int)}, vậy ID sẽ không khớp và dịch vụ sẽ không dừng.

+ +

Chú ý: Điều quan trọng là ứng dụng của bạn dừng dịch vụ của nó +khi nó hoàn thành xong công việc để tránh lãng phí tài nguyên của hệ thống và tốn pin. Nếu cần, +các thành phần khác có thể dừng dịch vụ bằng cách gọi {@link +android.content.Context#stopService stopService()}. Ngay cả khi bạn kích hoạt gắn kết cho dịch vụ, +bạn phải luôn tự mình dừng dịch vụ nếu dịch vụ đã nhận được lệnh gọi tới {@link +android.app.Service#onStartCommand onStartCommand()}.

+ +

Để biết thêm thông tin về vòng đời của một dịch vụ, hãy xem phần bên dưới về Quản lý Vòng đời của một Dịch vụ.

+ + + +

Tạo một Dịch vụ Gắn kết

+ +

Dịch vụ gắn kết là một dịch vụ cho phép các thành phần ứng dụng gắn kết với nó bằng cách gọi {@link +android.content.Context#bindService bindService()} để tạo một kết nối lâu dài +(và thường không cho phép các thành phần bắt đầu nó bằng cách gọi {@link +android.content.Context#startService startService()}).

+ +

Bạn nên tạo một dịch vụ gắn kết khi muốn tương tác với dịch vụ từ hoạt động +và các thành phần khác trong ứng dụng của mình hoặc để hiển thị một số tính năng trong ứng dụng của bạn cho +các ứng dụng khác thông qua truyền thông liên tiến trình (IPC).

+ +

Để tạo một dịch vụ gắn kết, bạn phải triển khai phương pháp gọi lại {@link +android.app.Service#onBind onBind()} để trả về một {@link android.os.IBinder} mà +định nghĩa giao diện cho giao tiếp với dịch vụ đó. Khi đó, các thành phần ứng dụng khác có thể gọi +{@link android.content.Context#bindService bindService()} để truy xuất giao diện và +bắt đầu các phương pháp gọi trên dịch vụ. Dịch vụ tồn tại chỉ nhằm phục vụ thành phần ứng dụng mà +được gắn kết với nó, vì thế khi không có thành phần được gắn kết với dịch vụ, hệ thống sẽ hủy nó +(bạn không cần dừng một dịch vụ gắn kết theo cách phải làm khi dịch vụ được bắt đầu +thông qua {@link android.app.Service#onStartCommand onStartCommand()}).

+ +

Để tạo một dịch vụ gắn kết, điều đầu tiên bạn phải làm là định nghĩa giao diện quy định +cách thức mà một máy khách có thể giao tiếp với dịch vụ. Giao diện giữa dịch vụ và +máy khách này phải là một triển khai {@link android.os.IBinder} và được dịch vụ của bạn phải +trả về từ phương pháp gọi lại {@link android.app.Service#onBind +onBind()}. Sau khi máy khách nhận được {@link android.os.IBinder}, nó có thể bắt đầu +tương tác với dịch vụ thông qua giao diện đó.

+ +

Nhiều máy khách có thể gắn kết với dịch vụ đồng thời. Khi một máy khách hoàn thành tương tác với +dịch vụ, nó sẽ gọi {@link android.content.Context#unbindService unbindService()} để bỏ gắn kết. Sau khi +không còn máy khách nào được gắn kết với dịch vụ, hệ thống sẽ hủy dịch vụ.

+ +

Có nhiều cách để triển khai một dịch vụ gắn kết và triển khai sẽ phức tạp +hơn so với dịch vụ được bắt đầu, vì thế nội dung bàn về dịch vụ gắn kết được trình bày trong một +tài liệu riêng về Dịch vụ Gắn kết.

+ + + +

Gửi Thông báo tới Người dùng

+ +

Sau khi chạy, một dịch vụ có thể thông báo cho người dùng về sự kiện bằng cách sử dụng Thông báo Cửa sổ hoặc Thông báo Thanh Trạng thái.

+ +

Thông báo cửa sổ là một thông báo xuất hiện một lúc trên bề mặt của cửa sổ hiện tại +rồi biến mất, trong khi thông báo thanh trạng thái cung cấp một biểu tượng trong thanh trạng thái cùng một +thông báo, người dùng có thể chọn nó để thực hiện một hành động (chẳng hạn như bắt đầu một hoạt động).

+ +

Thông thường thông báo thanh trạng thái là kỹ thuật tốt nhất khi một công việc nền nào đó đã hoàn thành +(chẳng hạn như một tệp đã hoàn thành +việc tải xuống) và lúc này người dùng có thể hành động dựa trên nó. Khi người dùng chọn thông báo từ dạng xem mở rộng +, thông báo có thể bắt đầu một hoạt động (chẳng hạn như xem tệp được tải xuống).

+ +

Xem hướng dẫn dành cho nhà phát triển Thông báo Cửa sổ hoặc Thông báo Thanh Trạng thái +để biết thêm thông tin.

+ + + +

Chạy một Dịch vụ trong Tiền cảnh

+ +

Dịch vụ tiền cảnh là một dịch vụ được coi là điều mà +người dùng đang chủ động quan tâm, vì thế nó không được đề nghị để hệ thống tắt bỏ khi bộ nhớ thấp. Dịch vụ +tiền cảnh phải cung cấp một thông báo cho thanh trạng thái, nó được đặt dưới tiêu đề +"Đang diễn ra", điều này có nghĩa là thông báo không thể loại bỏ được trừ khi dịch vụ +bị dừng hoặc loại bỏ khỏi tiền cảnh.

+ +

Ví dụ, một trình chơi nhạc đang phát nhạc từ một dịch vụ nên được đặt để chạy trong +tiền cảnh, vì người dùng rõ ràng ý thức được +hoạt động của nó. Thông báo trong thanh trạng thái có thể cho biết bài hát đang chơi và cho phép +người dùng khởi chạy một hoạt động để tương tác với trình chơi nhạc.

+ +

Để yêu cầu dịch vụ của bạn chạy trong tiền cảnh, hãy gọi {@link +android.app.Service#startForeground startForeground()}. Phương pháp này dùng hai tham số: một số nguyên +để xác định duy nhất thông báo và {@link +android.app.Notification} cho thanh trạng thái. Ví dụ:

+ +
+Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
+        System.currentTimeMillis());
+Intent notificationIntent = new Intent(this, ExampleActivity.class);
+PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
+notification.setLatestEventInfo(this, getText(R.string.notification_title),
+        getText(R.string.notification_message), pendingIntent);
+startForeground(ONGOING_NOTIFICATION_ID, notification);
+
+ +

Chú ý: ID số nguyên mà bạn cấp cho {@link +android.app.Service#startForeground startForeground()} không được bằng 0.

+ + +

Để xóa bỏ dịch vụ khỏi tiền cảnh, hãy gọi {@link +android.app.Service#stopForeground stopForeground()}. Phương pháp này dùng một boolean, cho biết +có loại bỏ cả thông báo thanh trạng thái hay không. Phương pháp này không dừng +dịch vụ. Tuy nhiên, nếu bạn dừng dịch vụ trong khi nó vẫn đang chạy trong tiền cảnh, khi đó thông báo +cũng bị loại bỏ.

+ +

Để biết thêm thông tin về thông báo, hãy xem phần Tạo Thông báo +Thanh Trạng thái.

+ + + +

Quản lý Vòng đời của một Dịch vụ

+ +

Vòng đời của một dịch vụ đơn giản hơn nhiều so với vòng đời của một hoạt động. Tuy nhiên, một điều thậm chí còn quan trọng hơn +đó là bạn phải thật chú ý tới cách dịch vụ của bạn được tạo và hủy, bởi một dịch vụ +có thể chạy ngầm mà người dùng không biết.

+ +

Vòng đời của dịch vụ—từ khi nó được tạo tới khi nó bị hủy—có thể đi theo hai +con đường khác nhau:

+ +
    +
  • Dịch vụ được bắt đầu +

    Dịch vụ được tạo khi một thành phần khác gọi {@link +android.content.Context#startService startService()}. Sau đó, dịch vụ sẽ chạy vô thời hạn và phải +tự dừng bằng cách gọi {@link +android.app.Service#stopSelf() stopSelf()}. Một thành phần khác cũng có thể dừng +dịch vụ bằng cách gọi {@link android.content.Context#stopService +stopService()}. Khi dịch vụ bị dừng, hệ thống sẽ hủy nó.

  • + +
  • Dịch vụ gắn kết +

    Dịch vụ được tạo khi một thành phần khác (máy khách) gọi {@link +android.content.Context#bindService bindService()}. Khi đó, máy khách giao tiếp với dịch vụ +thông qua một giao diện {@link android.os.IBinder}. Máy khách có thể đóng kết nối bằng cách gọi +{@link android.content.Context#unbindService unbindService()}. Nhiều máy khách có thể gắn kết với +cùng dịch vụ và khi tất cả chúng bỏ gắn kết, hệ thống sẽ hủy dịch vụ. (Dịch vụ +không cần tự mình dừng.)

  • +
+ +

Hai con đường này hoàn toàn riêng biệt. Cụ thể, bạn có thể gắn kết với một dịch vụ đã +được bắt đầu bằng {@link android.content.Context#startService startService()}. Ví dụ, một dịch vụ +nhạc nền có thể được bắt đầu bằng cách gọi {@link android.content.Context#startService +startService()} bằng một {@link android.content.Intent} mà sẽ nhận biết nhạc để phát. Sau đó, +có thể là khi người dùng muốn thực thi một quyền điều khiển đối với trình phát đó hoặc lấy thông tin về +bài hát đang phát, hoạt động có thể gắn kết với dịch vụ bằng cách gọi {@link +android.content.Context#bindService bindService()}. Trong những trường hợp như vậy, {@link +android.content.Context#stopService stopService()} hoặc {@link android.app.Service#stopSelf +stopSelf()} không thực sự dừng dịch vụ tới khi tất cả máy khách bỏ gắn kết.

+ + +

Triển khai gọi lại vòng đời

+ +

Giống như một hoạt động, dịch vụ có các phương pháp gọi lại vòng đời mà bạn có thể triển khai để theo dõi +những thay đổi về trạng thái của dịch vụ và thực hiện công việc tại những thời điểm phù hợp. Dịch vụ khung sau +minh họa từng phương pháp vòng đời:

+ +
+public class ExampleService extends Service {
+    int mStartMode;       // indicates how to behave if the service is killed
+    IBinder mBinder;      // interface for clients that bind
+    boolean mAllowRebind; // indicates whether onRebind should be used
+
+    @Override
+    public void {@link android.app.Service#onCreate onCreate}() {
+        // The service is being created
+    }
+    @Override
+    public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) {
+        // The service is starting, due to a call to {@link android.content.Context#startService startService()}
+        return mStartMode;
+    }
+    @Override
+    public IBinder {@link android.app.Service#onBind onBind}(Intent intent) {
+        // A client is binding to the service with {@link android.content.Context#bindService bindService()}
+        return mBinder;
+    }
+    @Override
+    public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) {
+        // All clients have unbound with {@link android.content.Context#unbindService unbindService()}
+        return mAllowRebind;
+    }
+    @Override
+    public void {@link android.app.Service#onRebind onRebind}(Intent intent) {
+        // A client is binding to the service with {@link android.content.Context#bindService bindService()},
+        // after onUnbind() has already been called
+    }
+    @Override
+    public void {@link android.app.Service#onDestroy onDestroy}() {
+        // The service is no longer used and is being destroyed
+    }
+}
+
+ +

Lưu ý: Không như các phương pháp gọi lại vòng đời của hoạt động, bạn +không phải gọi triển khai siêu lớp với những phương pháp gọi lại này.

+ + +

Hình 2. Vòng đời dịch vụ. Sơ đồ phía bên trái +minh họa vòng đời khi dịch vụ được tạo bằng {@link android.content.Context#startService +startService()} và sơ đồ phía bên phải minh họa vòng đời khi dịch vụ được tạo +bằng {@link android.content.Context#bindService bindService()}.

+ +

Bằng việc triển khai những phương pháp này, bạn có thể theo dõi hai vòng lặp lồng nhau trong vòng đời của dịch vụ:

+ +
    +
  • Toàn bộ vòng đời của một dịch vụ xảy ra giữa thời điểm gọi {@link +android.app.Service#onCreate onCreate()} và thời điểm {@link +android.app.Service#onDestroy} trả về. Giống như hoạt động, dịch vụ thực hiện thiết lập ban đầu của nó trong +{@link android.app.Service#onCreate onCreate()} và giải phóng tất cả tài nguyên còn lại trong {@link +android.app.Service#onDestroy onDestroy()}. Ví dụ, một +dịch vụ phát lại nhạc có thể tạo luồng mà tại đó nhạc sẽ được phát trong {@link +android.app.Service#onCreate onCreate()}, sau đó dừng luồng trong {@link +android.app.Service#onDestroy onDestroy()}. + +

    Các phương pháp {@link android.app.Service#onCreate onCreate()} và {@link android.app.Service#onDestroy +onDestroy()} được gọi cho tất cả dịch vụ, dù +chúng được tạo bởi {@link android.content.Context#startService startService()} hay {@link +android.content.Context#bindService bindService()}.

  • + +
  • Vòng đời hiện hoạt của một dịch vụ sẽ bắt đầu bằng một lệnh gọi đến hoặc {@link +android.app.Service#onStartCommand onStartCommand()} hoặc {@link android.app.Service#onBind onBind()}. +Mỗi phương pháp sẽ được giao {@link +android.content.Intent} mà được chuyển tương ứng cho hoặc {@link android.content.Context#startService +startService()} hoặc {@link android.content.Context#bindService bindService()}. +

    Nếu dịch vụ được bắt đầu, vòng đời hiện hoạt sẽ chấm dứt tại cùng thời điểm khi toàn bộ vòng đời +chấm dứt (dịch vụ sẽ vẫn hiện hoạt ngay cả sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về). Nếu dịch vụ bị gắn kết, vòng đời hiện hoạt sẽ chấm dứt khi {@link +android.app.Service#onUnbind onUnbind()} trả về.

    +
  • +
+ +

Lưu ý: Mặc dù dịch vụ được bắt đầu bị dừng bởi một lệnh gọi đến +hoặc {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}, sẽ không có một lệnh gọi lại tương ứng cho +dịch vụ (không có lệnh gọi lại {@code onStop()}). Vì thế, trừ khi dịch vụ được gắn kết với một máy khách, +hệ thống sẽ hủy nó khi dịch vụ bị dừng—{@link +android.app.Service#onDestroy onDestroy()} là lệnh gọi lại duy nhất nhận được.

+ +

Hình 2 minh họa các phương pháp gọi lại điển hình cho một dịch vụ. Mặc dù hình tách riêng +các dịch vụ được tạo bởi {@link android.content.Context#startService startService()} với các dịch vụ +được tạo bởi {@link android.content.Context#bindService bindService()}, hãy +ghi nhớ rằng bất kỳ dịch vụ nào, dù được bắt đầu như thế nào, đều có thể cho phép máy khách gắn kết với nó. +Vì thế, một dịch vụ được bắt đầu từ đầu bằng {@link android.app.Service#onStartCommand +onStartCommand()} (bởi một máy khách gọi {@link android.content.Context#startService startService()}) +vẫn có thể nhận một lệnh gọi đến {@link android.app.Service#onBind onBind()} (khi máy khách gọi +{@link android.content.Context#bindService bindService()}).

+ +

Để biết thêm thông tin về việc tao một dịch vụ có tính năng gắn kết, hãy xem tài liệu Dịch vụ Gắn kết, +trong đó có thêm thông tin về phương pháp gọi lại {@link android.app.Service#onRebind onRebind()} +trong phần về Quản lý Vòng đời của +một Dịch vụ Gắn kết.

+ + + diff --git a/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd new file mode 100644 index 0000000000000000000000000000000000000000..85afffff5ba54d001383442aaf4a1affbad663cc --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=Tác vụ và Ngăn xếp +parent.title=Hoạt động +parent.link=activities.html +@jd:body + + + + +

Một ứng dụng thường chứa nhiều hoạt động. Mỗi hoạt động +nên được thiết kế xung quanh một kiểu hành động cụ thể mà người dùng có thể thực hiện và bắt đầu các hoạt động +khác. Ví dụ, một ứng dụng e-mail có thể có một hoạt động để hiển thị một danh sách các thư mới. +Khi người dùng chọn một thư, một hoạt động mới sẽ mở ra để xem thư đó.

+ +

Hoạt động thậm chí có thể bắt đầu các hoạt động tồn tại trong các ứng dụng khác trên thiết bị. Ví +dụ, nếu ứng dụng của bạn muốn gửi một thư e-mail, bạn có thể định nghĩa một ý định để thực hiện một hành động +"gửi" và bao gồm một số dữ liệu, chẳng hạn như địa chỉ e-mail và nội dung. Một hoạt động từ một ứng dụng +khác tự khai báo là có khả năng xử lý kiểu ý định này sẽ mở ra. Trong trường hợp này, +ý định sẽ gửi một e-mail, sao cho hoạt động "soạn" của một ứng dụng e-mail sẽ bắt đầu (nếu nhiều hoạt động +hỗ trợ cùng ý định, khi đó hệ thống cho phép người dùng chọn hoạt động sẽ sử dụng). Khi e-mail được +gửi, hoạt động của bạn tiếp tục và dường như hoạt động e-mail là một phần ứng dụng của bạn. Mặc dù +các hoạt động có thể đến từ những ứng dụng khác nhau, Android duy trì trải nghiệm người dùng +mượt mà này bằng cách giữ cả hai hoạt động trong cùng tác vụ.

+ +

Tác vụ là một tập hợp gồm nhiều hoạt động mà người dùng tương tác với +khi thực hiện một công việc nhất định. Các hoạt động được sắp xếp trong một chồng (ngăn xếp), theo +thứ tự mở mỗi hoạt động.

+ + + +

Màn hình Trang chủ của thiết bị là nơi bắt đầu đối với hầu hết tác vụ. Khi người dùng chạm vào một biểu tượng trong trình khởi chạy +ứng dụng + (hoặc lối tắt trên màn hình Trang chủ), tác vụ của ứng dụng đó sẽ tiến ra tiền cảnh. Nếu không +có tác vụ nào cho ứng dụng (ứng dụng chưa được sử dụng gần đây), khi đó một tác vụ mới +sẽ được tạo và hoạt động "chính" cho ứng dụng đó sẽ mở ra thành hoạt động gốc trong chồng.

+ +

Khi hoạt động hiện tại bắt đầu một hoạt động khác, hoạt động mới sẽ bị đẩy lên trên cùng của chồng và +có tiêu điểm. Hoạt động trước vẫn nằm trong chồng, nhưng bị dừng lại. Khi một hoạt động +dừng, hệ thống giữ lại trạng thái hiện tại của giao diện người dùng của hoạt động đó. Khi người dùng nhấn nút +Quay lại +, hoạt động hiện tại được bật khỏi trên cùng của chồng (hoạt động bị hủy) và +hoạt động trước tiếp tục (trạng thái trước đó của UI của nó được khôi phục). Các hoạt động trong chồng +không bao giờ được sắp xếp lại, mà chỉ bị đẩy và bật khỏi chồng—bị đẩy lên trên chồng khi được bắt đầu bởi +hoạt động hiện tại và bị bật khỏi chồng khi người dùng rời nó bằng cách sử dụng nút Quay lại. Như vậy, +ngăn xếp +vận hành như một cấu trúc đối tượng "vào cuối, ra đầu". Hình 1 minh họa +hành vi này cùng một dòng thời gian thể hiện tiến độ giữa các hoạt động dọc theo ngăn xếp +hiện tại ở từng thời điểm.

+ + +

Hình 1. Biểu diễn cách mỗi hoạt động mới trong một +tác vụ thêm một mục vào ngăn xếp. Khi người dùng nhấn nút Quay lại, hoạt động +hiện tại +bị hủy và hoạt động trước đó tiếp tục.

+ + +

Nếu người dùng tiếp tục nhấn Quay lại, khi đó mỗi hoạt động trong chồng bị bật khỏi để +hiện ra +hoạt động trước, tới khi người dùng quay lại màn hình Trang chủ (hoặc trở về hoạt động đang chạy khi +tác vụ bắt đầu). Khi tất cả hoạt động bị loại bỏ khỏi chồng, tác vụ sẽ không còn tồn tại.

+ +
+

Hình 2. Hai tác vụ: Tác vụ B nhận tương tác người dùng +trong tiền cảnh, trong khi Tác vụ A nằm dưới nền, chờ được tiếp tục.

+
+
+

Hình 3. Một hoạt động đơn lẻ được khởi tạo nhiều lần.

+
+ +

Tác vụ là một đơn vị dính kết, có thể di chuyển tới "nền" khi người dùng bắt đầu một tác vụ mới hoặc đi đến +màn hình Trang chủ, thông qua nút Home. Khi ở trong nền, tất cả hoạt động trong +tác vụ bị +dừng, nhưng ngăn xếp cho tác vụ vẫn không bị ảnh hưởng—tác vụ chỉ đơn thuần mất tiêu điểm trong khi +một tác vụ khác thay thế, như minh họa trong hình 2. Khi đó, một tác vụ có thể quay lại "tiền cảnh" để người dùng +có thể chọn ở nơi họ đã rời đi. Ví dụ, giả sử rằng tác vụ hiện tại (Tác vụ A) có ba +hoạt động trong chồng của mình—hai trong số đó nằm dưới hoạt động hiện tại. Người dùng nhấn nút Trang chủ +, sau đó +bắt đầu một ứng dụng mới từ trình khởi chạy ứng dụng. Khi màn hình Trang chủ xuất hiện, Tác vụ A đi vào +nền. Khi ứng dụng mới bắt đầu, hệ thống sẽ bắt đầu một tác vụ cho ứng dụng đó +(Tác vụ B) bằng chồng các hoạt động của chính mình. Sau khi tương tác với +ứng dụng đó, người dùng quay lại Trang chủ lần nữa và chọn ứng dụng +đã bắt đầu Tác vụ A lúc đầu. Lúc này, Tác vụ A đi đến +tiền cảnh—cả ba hoạt động trong chồng của nó đều giữ nguyên và hoạt động trên cùng +của chồng được tiếp tục. Tại +điểm này, người dùng cũng có thể chuyển trở lại Tác vụ B bằng cách đến Trang chủ và chọn biểu tượng ứng dụng +đã bắt đầu tác vụ đó (hoặc bằng cách chọn tác vụ của ứng dụng từ +màn hình tổng quan). +Đây là một ví dụ về đa nhiệm trên Android.

+ +

Lưu ý: Nhiều tác vụ có thể được lưu giữ cùng lúc trong nền. +Tuy nhiên, nếu người dùng đang chạy nhiều tác vụ nền tại cùng thời điểm, hệ thống có thể bắt đầu +hủy các hoạt động nền để khôi phục bộ nhớ, khiến trạng thái của hoạt động bị mất. +Xem phần sau đây về Trạng thái của hoạt động.

+ +

Vì các hoạt động trong ngăn xếp không bao giờ được sắp xếp lại, nếu ứng dụng của bạn cho phép +người dùng bắt đầu một hoạt động cụ thể từ nhiều hơn một hoạt động, một thực thể mới của +hoạt động đó sẽ được tạo và đẩy lên chồng (thay vì mang bất kỳ thực thể nào trước đó của +hoạt động lên trên cùng). Như vậy, một hoạt động trong ứng dụng của bạn có thể được tạo phiên bản nhiều +lần (thậm chí từ các tác vụ khác nhau), như minh họa trong hình 3. Như vậy, nếu người dùng điều hướng ngược lại +bằng cách sử dụng nút Quay lại, mỗi thực thể của hoạt động được hiển thị theo thứ tự được +mở (mỗi hoạt động +có trạng thái UI của chính chúng). Tuy nhiên, bạn có thể sửa đổi hành vi này nếu không muốn một hoạt động được +khởi tạo nhiều hơn một lần. Cách làm như vậy được bàn trong phần sau về Quản lý Tác vụ.

+ + +

Để tóm tắt hành vi mặc định đối với các hoạt động và tác vụ:

+ +
    +
  • Khi Hoạt động A bắt đầu Hoạt động B, Hoạt động A bị dừng, nhưng hệ thống giữ lại trạng thái của nó +(chẳng hạn như vị trí cuộn và văn bản được nhập vào các mẫu). +Nếu người dùng nhấn nút Quay lại khi đang trong Hoạt động B, Hoạt động A sẽ tiếp tục với trạng thái +được khôi phục.
  • +
  • Khi người dùng rời khỏi một tác vụ bằng cách nhấn nút Trang chủ, hoạt động hiện tại bị +dừng và +tác vụ của nó sẽ đưa xuống dưới nền. Hệ thống sẽ giữ lại trạng thái của mọi hoạt động trong tác vụ. Nếu +sau đó người dùng tiếp tục tác vụ bằng cách chọn biểu tượng trình khởi chạy đã bắt đầu tác vụ, tác vụ sẽ vào +tiền cảnh và tiếp tục hoạt động ở trên cùng của chồng.
  • +
  • Nếu người dùng nhấn nút Quay lại, hoạt động hiện tại bị bật khỏi chồng +và +bị hủy. Hoạt động trước đó ở trong chồng sẽ được tiếp tục. Khi một hoạt động bị hủy, hệ thống +không giữ lại trạng thái của hoạt động đó.
  • +
  • Hoạt động có thể được khởi tạo nhiều lần, thậm chí từ các tác vụ khác.
  • +
+ + +
+

Thiết kế Điều hướng

+

Để biết thêm về cách điều hướng ứng dụng hoạt động trên Android, hãy đọc hướng dẫn Điều hướng của Thiết kế Android.

+
+ + +

Lưu Trạng thái của Hoạt động

+ +

Như được đề cập ở trên, hành vi mặc định của hệ thống giữ nguyên trạng thái của một hoạt động khi nó bị +dừng. Bằng cách này, khi người dùng điều hướng trở lại một hoạt động trước đó, giao diện người dùng của nó sẽ xuất hiện + như lúc bị rời đi. Tuy nhiên, bạn có thể—và nên—chủ động giữ lại +trạng thái của các hoạt động của mình bằng cách sử dụng các phương pháp gọi lại, trong trường hợp hoạt động bị hủy và phải +được tạo lại.

+ +

Khi hệ thống dừng một trong các hoạt động của bạn (chẳng hạn như khi một hoạt động mới bắt đầu hoặc tác vụ +di chuyển về nền), hệ thống có thể hoàn toàn hủy hoạt động đó nếu nó cần khôi phục +bộ nhớ hệ thống. Khi điều này xảy ra, thông tin về trạng thái của hoạt động sẽ bị mất. Nếu điều này xảy ra, +hệ thống vẫn +biết rằng hoạt động có một vị trí trong ngăn xếp, nhưng khi hoạt động được đưa tới vị trí trên cùng +của chồng, hệ thống phải tạo lại nó (thay vì tiếp tục). Để tránh +làm mất công việc của người dùng, bạn nên chủ động giữ lại nó bằng cách triển khai phương pháp gọi lại +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} trong +hoạt động của mình.

+ +

Để biết thêm thông tin về cách lưu trạng thái hoạt động của mình, hãy xem tài liệu Hoạt động +.

+ + + +

Quản lý Tác vụ

+ +

Cách Android quản lý tác vụ và ngăn xếp, như được mô tả bên trên—bằng cách đặt tất cả +hoạt động được bắt đầu nối tiếp nhau vào cùng tác vụ và trong một chồng "vào cuối, ra đầu"—rất +hiệu quả đối với hầu hết ứng dụng và bạn không phải lo lắng về cách thức các hoạt động của mình được liên kết với +các tác vụ hay cách chúng tồn tại trong ngăn xếp. Tuy nhiên, bạn có thể quyết định rằng mình muốn gián đoạn +hành vi thông thường. Có thể bạn muốn một hoạt động trong ứng dụng của mình bắt đầu một tác vụ mới khi nó được +bắt đầu (thay vì được đặt trong tác vụ hiện tại); hoặc, khi bạn bắt đầu một hoạt động, bạn muốn +mang lên trước một thực thể hiện tại của nó (thay vì tạo một +thực thể mới trên cùng của ngăn xếp); hoặc, bạn muốn ngăn xếp của mình được xóa sạch tất cả các +hoạt động, ngoại trừ hoạt động gốc khi người dùng rời khỏi tác vụ.

+ +

Bạn có thể làm những điều này và nhiều điều khác với các thuộc tính trong phần tử bản kê khai +{@code <activity>} +và cờ trong ý định mà bạn chuyển cho +{@link android.app.Activity#startActivity startActivity()}.

+ +

Về mặt này, các thuộc tính +{@code <activity>} chính mà bạn có thể sử dụng là:

+ + + +

Và các cờ ý định chính mà bạn có thể sử dụng là:

+ +
    +
  • {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
  • +
  • {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
  • +
  • {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
  • +
+ +

Trong những phần sau, bạn sẽ thấy cách bạn có thể sử dụng những thuộc tính của bản kê khai +và cờ ý định này để định nghĩa cách các hoạt động được liên kết với tác vụ và hành vi của chúng như thế nào trong ngăn xếp.

+ +

Bên cạnh đó, có phần bàn riêng về việc cân nhắc cách các tác vụ và hoạt động có thể +được biểu diễn và quản lý trong màn hình tổng quan. Xem phần Màn hình Tổng quan +để biết thêm thông tin. Thông thường, bạn nên cho phép hệ thống định nghĩa tác vụ và các hoạt động +của bạn được biểu diễn như thế nào trong màn hình tổng quan, và bạn không cần sửa đổi hành vi này.

+ +

Chú ý: Hầu hết các ứng dụng không nên gián đoạn hành vi +mặc định cho các hoạt động và tác vụ. Nếu bạn xác định rằng hoạt động của bạn cần phải sửa đổi +hành vi mặc định, hãy thận trọng và đảm bảo kiểm tra khả năng sử dụng được của hoạt động đó trong khi +khởi chạy và khi điều hướng trở lại nó từ các hoạt động và tác vụ khác bằng nút Quay lại. +Đảm bảo kiểm tra hành vi điều hướng mà có thể xung đột với hành vi được kỳ vọng của người dùng.

+ + +

Định nghĩa các chế độ khởi chạy

+ +

Các chế độ khởi chạy cho phép bạn định nghĩa một thực thể mới của một hoạt động được liên kết với tác vụ hiện tại +như thế nào. Bạn có thể định nghĩa các chế độ khởi chạy khác nhau theo hai cách:

+
    +
  • Sử dụng tệp bản kê khai +

    Khi bạn khai báo một hoạt động trong tệp bản kê khai của mình, bạn có thể quy định hoạt động +sẽ liên kết với các tác vụ như thế nào khi nó bắt đầu.

  • +
  • Sử dụng cờ Ý định +

    Khi bạn gọi {@link android.app.Activity#startActivity startActivity()}, +bạn có thể thêm một cờ vào {@link android.content.Intent} mà khai báo cách (hoặc +liệu có hay không) hoạt động mới sẽ liên kết với tác vụ hiện tại.

  • +
+ +

Như vậy, nếu Hoạt động A bắt đầu Hoạt động B, Hoạt động B có thể định nghĩa trong bản kê khai của mình cách nó +sẽ liên kết với tác vụ hiện tại (nếu có) và Hoạt động A cũng có thể yêu cầu cách mà Hoạt động +B sẽ liên kết với tác vụ hiện tại. Nếu cả hai hoạt động đều định nghĩa cách Hoạt động B +nên liên kết với một tác vụ thì yêu cầu của Hoạt động A (như định nghĩa trong ý định) được ưu tiên +so với yêu cầu của Hoạt động B (như được định nghĩa trong bản kê khai của nó).

+ +

Lưu ý: Một số chế độ khởi chạy sẵn có cho tệp bản kê khai +không sẵn có dưới dạng cờ cho một ý định và, tương tự, một số chế độ khởi chạy sẵn có dưới dạng cờ +cho một ý định không thể được định nghĩa trong bản kê khai.

+ + +

Sử dụng tệp bản kê khai

+ +

Khi khai báo một hoạt động trong tệp bản kê khai của bạn, bạn có thể quy định cách mà hoạt động +sẽ liên kết với một tác vụ bằng cách sử dụng thuộc tính của phần tử {@code <activity>} +, {@code +launchMode}.

+ +

Thuộc tính {@code +launchMode} quy định một chỉ lệnh về cách hoạt động sẽ được khởi chạy vào một +tác vụ. Có bốn chế độ khởi chạy khác nhau mà bạn có thể gán cho thuộc tính +launchMode +:

+ +
+
{@code "standard"} (chế độ mặc định)
+
Mặc định. Hệ thống tạo một thực thể mới của hoạt động trong tác vụ là nơi +mà nó được bắt đầu và định tuyến ý định tới đó. Hoạt động có thể được khởi tạo nhiều lần, +mỗi thực thể có thể thuộc về các tác vụ khác nhau, và một tác vụ có thể có nhiều thực thể.
+
{@code "singleTop"}
+
Nếu một thực thể của hoạt động đã tồn tại ở trên cùng của tác vụ hiện tại, hệ thống +sẽ định tuyến ý định tới thực thể đó thông qua một lệnh gọi tới phương pháp {@link +android.app.Activity#onNewIntent onNewIntent()} của nó, thay vì tạo một thực thể mới của +hoạt động. Hoạt động có thể được tạo phiên bản nhiều lần, mỗi thực thể có thể +thuộc về các tác vụ khác nhau, và một tác vụ có thể có nhiều thực thể (nhưng chỉ nếu +hoạt động nằm trên cùng của ngăn xếp không phải là một thực thể của hoạt động hiện có). +

Ví dụ, giả sử ngăn xếp của một tác vụ bao gồm hoạt động gốc A với các hoạt động B, C, +và D ở trên cùng (chồng là A-B-C-D; D ở trên cùng). Một ý định đến cho loại hoạt động D. +Nếu D có chế độ khởi chạy {@code "standard"} mặc định, một thực thể mới của lớp sẽ được khởi chạy và +chồng trở thành A-B-C-D-D. Tuy nhiên, nếu chế độ khởi chạy của D là {@code "singleTop"}, thực thể hiện tại +của D sẽ nhận ý định thông qua {@link +android.app.Activity#onNewIntent onNewIntent()}, bởi nó nằm ở vị trí trên cùng của chồng—chồng +vẫn là A-B-C-D. Tuy nhiên, nếu một ý định đến cho hoạt động loại B, khi đó một thực thể +mới của B sẽ được thêm vào chồng ngay cả khi chế độ khởi chạy của nó là {@code "singleTop"}.

+

Lưu ý: Khi một thực thể mới của hoạt động được tạo, +người dùng có thể nhấn nút Quay lại để quay về hoạt động trước đó. Nhưng khi một thực thể +hiện tại của +hoạt động xử lý một ý định mới, người dùng không thể nhấn nút Quay lại để quay về trạng thái +của +hoạt động trước khi ý định mới đến trong {@link android.app.Activity#onNewIntent +onNewIntent()}.

+
+ +
{@code "singleTask"}
+
Hệ thống sẽ tạo ra một tác vụ mới và khởi tạo hoạt động ở gốc của tác vụ mới. +Tuy nhiên, nếu một thực thể của hoạt động đã tồn tại trong một tác vụ riêng, hệ thống sẽ định tuyến +ý định tới thực thể hiện tại thông qua một lệnh gọi tới phương pháp {@link +android.app.Activity#onNewIntent onNewIntent()} của nó thay vì tạo một thực thể mới. Chỉ +một thực thể của hoạt động có thể tồn tại ở một thời điểm. +

Lưu ý: Mặc dù hoạt động bắt đầu một tác vụ mới, nút +Quay lại sẽ vẫn đưa người dùng quay về hoạt động trước đó.

+
{@code "singleInstance"}.
+
Giống như {@code "singleTask"}, trừ khi hệ thống không khởi chạy bất kỳ hoạt động nào khác vào +tác vụ đang nắm giữ thực thể. Hoạt động luôn là thành viên đơn lẻ và duy nhất của tác vụ; +bất kỳ hoạt động nào được bắt đầu bởi hoạt động này sẽ mở ra trong một tác vụ riêng.
+
+ + +

Lấy một ví dụ khác, ứng dụng Trình duyệt của Android khai báo rằng hoạt động trình duyệt web sẽ +luôn mở tác vụ của chính mình—bằng cách quy định chế độ khởi chạy {@code singleTask} trong phần tử {@code <activity>}. +Điều này có nghĩa rằng nếu ứng dụng của bạn phát hành một +ý định để mở Trình duyệt Android, hoạt động của nó không được đặt trong cùng +tác vụ như ứng dụng của bạn. Thay vào đó, hoặc là một tác vụ mới bắt đầu cho Trình duyệt hoặc, nếu Trình duyệt +đã có một tác vụ đang chạy trong nền thì tác vụ đó sẽ được đưa lên trước để xử lý +ý định mới.

+ +

Không phụ thuộc vào việc một hoạt động bắt đầu trong một tác vụ mới hay trong cùng tác vụ mà hoạt động +đã bắt đầu, nút Quay lại sẽ luôn đưa người dùng về hoạt động trước đó. Tuy nhiên, nếu bạn +bắt đầu một hoạt động mà quy định chế độ khởi chạy {@code singleTask}, khi đó nếu một thực thể của +hoạt động đó tồn tại trong một tác vụ nền, toàn bộ tác vụ đó sẽ được đưa ra tiền cảnh. Tại thời điểm này +, lúc này ngăn xếp bao gồm tất cả hoạt động từ tác vụ được mang ra, ở vị trí trên cùng của +chồng. Hình 4 minh họa loại kịch bản này.

+ + +

Hình 4. Biểu diễn cách thức một hoạt động với +chế độ khởi chạy "singleTask" được thêm vào ngăn xếp. Nếu hoạt động đã là một phần của một +tác vụ nền với ngăn xếp của chính nó, khi đó toàn bộ ngăn xếp cũng tiến +về phía trước, ở trên cùng tác vụ hiện tại.

+ +

Để biết thêm thông tin về việc sử dụng các chế độ khởi chạy trong tệp bản kê khai, hãy xem tài liệu phần tử +<activity> +, trong đó có trình bày thêm về thuộc tính {@code launchMode} và các giá trị +được chấp nhận.

+ +

Lưu ý: Hành vi mà bạn quy định cho hoạt động của mình bằng thuộc tính {@code launchMode} có thể +bị khống chế bởi cờ có ý định bắt đầu hoạt động của bạn, như được trình bày trong +phần tiếp theo.

+ + + +

Sử dụng cờ Ý định

+ +

Khi bắt đầu một hoạt động, bạn có thể sửa đổi liên kết mặc định giữa một hoạt động với tác vụ của nó +bằng cách thêm cờ vào trong ý định mà bạn chuyển tới {@link +android.app.Activity#startActivity startActivity()}. Những cờ mà bạn có thể sử dụng để sửa đổi +hành vi mặc định là:

+ +

+

{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
+
Bắt đầu một hoạt động trong một tác vụ mới. Nếu đã có một tác vụ đang chạy cho hoạt động mà bạn đang +bắt đầu, tác vụ đó sẽ được đưa ra tiền cảnh với trạng thái cuối cùng được khôi phục và hoạt động +nhận ý định mới trong {@link android.app.Activity#onNewIntent onNewIntent()}. +

Điều này sẽ tạo ra cùng hành vi như giá trị {@code "singleTask"} {@code launchMode}, +đã được trình bày ở phần trước.

+
{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
+
Nếu hoạt động đang được bắt đầu là hoạt động hiện tại (ở trên cùng của ngăn xếp), khi đó +thực thể hiện có sẽ nhận một lệnh gọi đến {@link android.app.Activity#onNewIntent onNewIntent()}, +thay vì tạo một thực thể của hoạt động mới. +

Điều này sẽ tạo ra cùng hành vi như giá trị {@code "singleTop"} {@code launchMode}, +đã được trình bày ở phần trước.

+
{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
+
Nếu hoạt động đang được bắt đầu đang chạy trong tác vụ hiện tại, khi đó +thay vì khởi chạy một thực thể mới của hoạt động đó, tất cả các hoạt động khác bên trên nó đều +bị hủy và ý định này được chuyển tới thực thể được tiếp tục của hoạt động (lúc này đang ở trên cùng), +thông qua {@link android.app.Activity#onNewIntent onNewIntent()}). +

Không có giá trị cho thuộc tính {@code launchMode} +mà sinh ra hành vi này.

+

{@code FLAG_ACTIVITY_CLEAR_TOP} được sử dụng thường xuyên nhất cùng với + {@code FLAG_ACTIVITY_NEW_TASK}. +Khi được sử dụng cùng nhau, những cờ này là một cách để định vị hoạt động hiện tại +trong một tác vụ khác và đặt nó vào vị trí nơi nó có thể hồi đáp ý định.

+

Lưu ý: Nếu chế độ khởi chạy của hoạt động được chỉ định là + {@code "standard"}, +nó cũng bị loại bỏ khỏi chồng và một thực thể mới sẽ được khởi chạy thay chỗ nó để xử lý +ý định đến. Đó là bởi một thực thể mới luôn được tạo cho ý định mới khi chế độ khởi chạy +là {@code "standard"}.

+
+ + + + + + +

Xử lý quan hệ

+ +

Quan hệ sẽ cho biết một hoạt động ưu tiên thuộc về tác vụ nào hơn. Theo mặc định, tất cả +các hoạt động từ cùng ứng dụng có quan hệ với nhau. Vì thế, theo mặc định, tất cả +hoạt động trong cùng ứng dụng ưu tiên ở trong cùng tác vụ hơn. Tuy nhiên, bạn có thể sửa đổi +quan hệ mặc định cho một hoạt động. Các hoạt động được định nghĩa trong +các ứng dụng khác nhau có thể chia sẻ một quan hệ, hoặc các hoạt động được định nghĩa trong cùng ứng dụng có thể +được gán các quan hệ tác vụ khác nhau.

+ +

Bạn có thể sửa đổi quan hệ cho bất kỳ hoạt động đã cho nào bằng thuộc tính {@code taskAffinity} của +phần tử {@code <activity>} +.

+ +

Thuộc tính {@code taskAffinity} +lấy một giá trị xâu, đó phải là giá trị duy nhất từ tên gói mặc định +được khai báo trong phần tử +{@code <manifest>} +, do hệ thống sử dụng tên đó để nhận biết quan hệ +tác vụ mặc định cho ứng dụng.

+ +

Vấn đề quan hệ được xét trong hai trường hợp:

+
    +
  • Khi ý định khởi chạy hoạt động chứa cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +. + +

    Theo mặc định, một hoạt động mới được khởi chạy vào tác vụ của hoạt động +đã gọi {@link android.app.Activity#startActivity startActivity()}. Nó được đẩy lên cùng +ngăn xếp như trình gọi. Tuy nhiên, nếu ý định được chuyển tới +{@link android.app.Activity#startActivity startActivity()} +chứa cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +, hệ thống sẽ tìm một tác vụ khác để chứa hoạt động mới. Thường thì đó là một tác vụ mới. +Tuy nhiên, không nhất thiết phải như vậy. Nếu đã có một tác vụ hiện tại với cùng quan hệ như +hoạt động mới, hoạt động sẽ được khởi chạy vào tác vụ đó. Nếu không, nó sẽ bắt đầu một tác vụ mới.

    + +

    Nếu cờ này khiến một hoạt động bắt đầu một tác vụ mới và người dùng nhấn nút Home +để rời +nó thì phải có một cách nào đó để người dùng điều hướng quay lại tác vụ. Một số đối tượng (chẳng hạn như +trình quản lý thông báo) luôn bắt đầu các hoạt động trong một tác vụ bên ngoài, không bao giờ bắt đầu trong chính tác vụ của mình, vì thế +chúng luôn đặt {@code FLAG_ACTIVITY_NEW_TASK} vào ý định mà chúng chuyển tới +{@link android.app.Activity#startActivity startActivity()}. +Nếu bạn có một hoạt động có thể được gọi ra bởi +một đối tượng bên ngoài mà có thể sử dụng cờ này, hãy lưu ý là người dùng có một cách độc lập để quay lại +tác vụ đã được bắt đầu, chẳng hạn như bằng một biểu tượng trình khởi chạy (hoạt động gốc của tác vụ +có một bộ lọc ý định {@link android.content.Intent#CATEGORY_LAUNCHER}; xem phần Bắt đầu một tác vụ ở bên dưới).

    +
  • + +
  • Khi một hoạt động có thuộc tính +{@code allowTaskReparenting} của nó được đặt thành {@code "true"}. +

    Trong trường hợp này, hoạt động có thể di chuyển từ tác vụ mà nó bắt đầu tới tác vụ mà nó có quan hệ + khi tác vụ đó đi ra tiền cảnh.

    +

    Ví dụ, giả sử rằng một hoạt động với chức năng báo cáo tình hình thời tiết ở các thành phố được chọn +được định nghĩa là một phần trong một ứng dụng du lịch. Nó có cùng quan hệ như các hoạt động khác trong cùng +ứng dụng (quan hệ ứng dụng mặc định) và nó cho phép tạo lại tập mẹ với thuộc tính này. +Khi một trong các hoạt động của bạn bắt đầu hoạt động trình báo cáo thời tiết, ban đầu nó thuộc về cùng +tác vụ như hoạt động của bạn. Tuy nhiên, khi tác vụ của ứng dụng du lịch đi ra tiền cảnh, hoạt động +của trình báo cáo thời tiết được gán lại cho tác vụ đó và được hiển thị bên trong nó.

    +
  • +
+ +

Mẹo: Nếu một tệp {@code .apk} chứa nhiều hơn một "ứng dụng" +từ quan điểm của người dùng, bạn có thể muốn sử dụng thuộc tính {@code taskAffinity} +để gán các quan hệ khác nhau cho hoạt động được liên kết với từng "ứng dụng".

+ + + +

Xóa ngăn xếp

+ +

Nếu người dùng rời một tác vụ trong khoảng thời gian dài, hệ thống sẽ xóa tác vụ của tất cả hoạt động ngoại trừ +hoạt động gốc. Khi người dùng quay trở lại tác vụ, chỉ hoạt động gốc được khôi phục. +Hệ thống sẽ hoạt động theo cách này, vì, sau một khoảng thời gian dài, người dùng có thể đã từ bỏ +việc mà họ đang làm trước đó và quay lại tác vụ để bắt đầu một việc mới.

+ +

Có một số thuộc tính hoạt động mà bạn có thể sử dụng để sửa đổi hành vi này:

+ +
+
alwaysRetainTaskState +
+
Nếu thuộc tính này được đặt thành {@code "true"} trong hoạt động gốc của một tác vụ, +hành vi mặc định được mô tả sẽ không xảy ra. +Tác vụ giữ lại tất cả hoạt động trong chồng của mình kể cả sau một khoảng thời gian dài.
+ +
clearTaskOnLaunch
+
Nếu thuộc tính này được đặt thành {@code "true"} trong hoạt động gốc của một tác vụ, +chồng sẽ bị xóa tới hoạt động gốc bất cứ khi nào người dùng rời khỏi tác vụ +và quay lại. Nói cách khác, nó ngược với + +{@code alwaysRetainTaskState}. Người dùng luôn quay lại tác vụ ở +trạng thái ban đầu của nó, ngay cả sau khi rời khỏi tác vụ trong chỉ một lúc.
+ +
finishOnTaskLaunch +
+
Thuộc tính này giống như {@code clearTaskOnLaunch}, +nhưng nó hoạt động trên một tác vụ +đơn lẻ chứ không phải một tác vụ toàn bộ. Nó cũng có thể khiến bất kỳ hoạt động nào +thoát mất, bao gồm cả hoạt động gốc. Khi nó được đặt thành {@code "true"}, hoạt động +vẫn là một bộ phận của tác vụ chỉ cho phiên làm việc hiện tại. Nếu người dùng rời đi +rồi quay lại tác vụ, nó không còn xuất hiện nữa.
+
+ + + + +

Bắt đầu một tác vụ

+ +

Bạn có thể thiết lập một hoạt động làm điểm bắt đầu cho một tác vụ bằng cách đưa cho nó một bộ lọc ý định với +{@code "android.intent.action.MAIN"} là hành động được quy định và +{@code "android.intent.category.LAUNCHER"} +là thể loại được quy định. Ví dụ:

+ +
+<activity ... >
+    <intent-filter ... >
+        <action android:name="android.intent.action.MAIN" />
+        <category android:name="android.intent.category.LAUNCHER" />
+    </intent-filter>
+    ...
+</activity>
+
+ +

Một bộ lọc ý định loại này khiến một biểu tượng và nhãn cho +hoạt động được hiển thị trong trình khởi chạy ứng dụng, cho phép người dùng khởi chạy hoạt động và +quay lại tác vụ mà nó tạo ra vào bất cứ lúc nào sau khi nó được khởi chạy. +

+ +

Khả năng thứ hai này là quan trọng: Người dùng chắc chắn có thể rời một tác vụ rồi quay lại +sau bằng cách sử dụng trình khởi chạy hoạt động này. Vì lý do này, hai chế độ +khởi chạy mà đánh dấu các hoạt động là luôn khởi tạo một tác vụ, {@code "singleTask"} và +{@code "singleInstance"}, sẽ chỉ được sử dụng khi hoạt động có một +{@link android.content.Intent#ACTION_MAIN} +và một bộ lọc {@link android.content.Intent#CATEGORY_LAUNCHER}. Ví dụ, hãy tưởng tượng chuyện gì +sẽ xảy ra nếu thiếu bộ lọc: Một ý định khởi chạy một hoạt động {@code "singleTask"}, khởi đầu một +tác vụ mới và người dùng dành một khoảng thời gian làm việc trong tác vụ đó. Khi đó, người dùng nhấn nút Home +. Lúc này, tác vụ được gửi tới nền và không hiển thị. Bây giờ, người dùng không có cách nào để quay lại +tác vụ bởi nó không được biểu diễn trong trình khởi chạy ứng dụng.

+ +

Đối với những trường hợp mà bạn không muốn người dùng có thể quay lại một hoạt động, hãy đặt giá trị của phần tử +<activity> +, +{@code finishOnTaskLaunch} +thành {@code "true"} (xem Xóa chồng).

+ +

Bạn có thể tham khảo thêm thông tin về cách các tác vụ và hoạt động được trình bày và quản lý trong +màn hình tổng quan sẵn có tại phần +Màn hình Tổng quan.

+ + diff --git a/docs/html-intl/intl/vi/guide/index.jd b/docs/html-intl/intl/vi/guide/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..7be2d89a2d1784dfd88cea0e979847244d8b827a --- /dev/null +++ b/docs/html-intl/intl/vi/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Giới thiệu về Android + +@jd:body + + + + +

Android cung cấp một khuôn khổ ứng dụng phong phú cho phép bạn xây dựng các ứng dụng và trò chơi mới +cho các thiết bị di động trong môi trường ngôn ngữ Java. Tài liệu được liệt kê trong vùng điều hướng +bên trái sẽ cung cấp chi tiết về cách xây dựng ứng dụng bằng cách sử dụng các API khác nhau của Android.

+ +

Nếu bạn mới làm quen với việc phát triển Android, quan trọng là bạn phải hiểu +những khái niệm cơ bản sau về khuôn khổ ứng dụng Android:

+ + +
+ +
+ +

Các ứng dụng cung cấp nhiều điểm nhập

+ +

Các ứng dụng Android được tích hợp như một sự kết hợp giữa các thành phần khác nhau có thể được gọi ra +riêng. Ví dụ, một hoạt động riêng lẻ cung cấp một màn hình +duy nhất cho một giao diện người dùng, và một dịch vụ chạy ngầm thực hiện độc lập +công việc.

+ +

Từ một thành phần, bạn có thể khởi động một thành phần khác bằng cách sử dụng một ý định. Thậm chí bạn có thể bắt đầu +một thành phần trong một ứng dụng khác, chẳng hạn như một hoạt động trong một ứng dụng bản đồ để hiển thị một địa chỉ. Mô hình này +cung cấp nhiều điểm nhập cho một ứng dụng duy nhất và cho phép bất kỳ ứng dụng nào xử lý như "mặc định" +của một người dùng đối với một hành động mà các ứng dụng khác có thể gọi ra.

+ + +

Tìm hiểu thêm:

+ + +
+ + +
+ +

Các ứng dụng sẽ thích ứng theo các thiết bị khác nhau

+ +

Android cung cấp một khuôn khổ ứng dụng thích ứng cho phép bạn cung cấp các tài nguyên duy nhất +cho các cấu hình thiết bị khác nhau. Ví dụ, bạn có thể tạo các tệp bố trí +XML khác nhau cho các kích cỡ màn hình khác nhau và hệ thống +sẽ xác định bố trí nào sẽ áp dụng dựa trên kích cỡ màn hình hiện tại của thiết bị.

+ +

Bạn có thể truy vấn về sự sẵn có của các tính năng trên thiết bị vào thời gian chạy nếu bất kỳ tính năng nào của ứng dụng +yêu cầu phần cứng cụ thể, chẳng hạn như máy ảnh. Nếu cần, bạn cũng có thể khai báo các tính năng mà ứng dụng của mình yêu cầu +vì vậy, những chợ ứng dụng như Google Play Store không cho phép cài đặt trên những thiết bị không hỗ trợ +tính năng đó.

+ + +

Tìm hiểu thêm:

+ + +
+ +
+ + + diff --git a/docs/html-intl/intl/vi/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/vi/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 0000000000000000000000000000000000000000..ca2ed26270f2b52d566fefde53d5b66cb6cade30 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=Bản kê khai Ứng dụng +@jd:body + + + +

+ Mọi ứng dụng đều phải có một tệp AndroidManifest.xml (chính xác là + tên gọi này) trong thư mục gốc của mình. Tệp bản kê khai + trình bày những thông tin thiết yếu về ứng dụng của bạn với hệ thống Android, + thông tin mà hệ thống phải có trước khi có thể chạy bất kỳ mã nào + của ứng dụng. Ngoài một số mục đích khác, bản kê khai thực hiện những điều sau: +

+ +
    +
  • Nó đặt tên gói Java cho ứng dụng. +Tên gói đóng vai trò như một mã nhận diện duy nhất cho ứng dụng.
  • + +
  • Nó mô tả các thành phần của ứng dụng — hoạt động, +dịch vụ, hàm nhận quảng bá, và trình cung cấp nội dung mà ứng dụng +được soạn bởi. Nó đặt tên các lớp triển khai từng thành phần và +công bố các khả năng của chúng (ví dụ, những tin nhắn {@link android.content.Intent +Intent} mà chúng có thể xử lý). Những khai báo này cho phép hệ thống Android +biết các thành phần là gì và chúng có thể được khởi chạy trong những điều kiện nào.
  • + +
  • Nó xác định những tiến trình nào sẽ lưu trữ các thành phần ứng dụng.
  • + +
  • Nó khai báo các quyền mà ứng dụng phải có để +truy cập các phần được bảo vệ của API và tương tác với các ứng dụng khác.
  • + +
  • Nó cũng khai báo các quyền mà ứng dụng khác phải có để +tương tác với các thành phần của ứng dụng.
  • + +
  • Nó liệt kê các lớp {@link android.app.Instrumentation} cung cấp +tính năng tạo hồ sơ và các thông tin khác khi ứng dụng đang chạy. Những khai báo này +chỉ xuất hiện trong bản kê khai khi ứng dụng đang được phát triển và +thử nghiệm; chúng bị loại bỏ trước khi ứng dụng được công bố.
  • + +
  • Nó khai báo mức tối thiểu của API Android mà ứng dụng +yêu cầu.
  • + +
  • Nó liệt kê các thư viện mà ứng dụng phải được liên kết với.
  • +
+ + +

Cấu trúc của Tệp Bản kê khai

+ +

+Sơ đồ bên dưới minh họa cấu trúc chung của tệp bản kê khai và mọi +phần tử mà nó có thể chứa. Từng phần tử, cùng với tất cả thuộc tính +của mình, sẽ được lập tài liệu theo dõi đầy đủ vào một tệp riêng. Để xem thông tin +chi tiết về mọi phần tử, hãy nhấp vào tên phần tử trong sơ đồ, +trong danh sách các phần tử theo thứ tự chữ cái mà tuân theo sơ đồ, hoặc trên bất kỳ +nội dung nào khác đề cập tới tên phần tử. +

+ +
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest>
+
+    <uses-permission />
+    <permission />
+    <permission-tree />
+    <permission-group />
+    <instrumentation />
+    <uses-sdk />
+    <uses-configuration />  
+    <uses-feature />  
+    <supports-screens />  
+    <compatible-screens />  
+    <supports-gl-texture />  
+
+    <application>
+
+        <activity>
+            <intent-filter>
+                <action />
+                <category />
+                <data />
+            </intent-filter>
+            <meta-data />
+        </activity>
+
+        <activity-alias>
+            <intent-filter> . . . </intent-filter>
+            <meta-data />
+        </activity-alias>
+
+        <service>
+            <intent-filter> . . . </intent-filter>
+            <meta-data/>
+        </service>
+
+        <receiver>
+            <intent-filter> . . . </intent-filter>
+            <meta-data />
+        </receiver>
+
+        <provider>
+            <grant-uri-permission />
+            <meta-data />
+            <path-permission />
+        </provider>
+
+        <uses-library />
+
+    </application>
+
+</manifest>
+
+ +

+Tất cả phần tử có thể xuất hiện trong tệp bản kê khai được liệt kê ở bên dưới +theo thứ tự chữ cái. Đây là những phần tử hợp pháp duy nhất; bạn không thể +thêm các phần tử hay thuộc tính của chính mình. +

+ +

+<action> +
<activity> +
<activity-alias> +
<application> +
<category> +
<data> +
<grant-uri-permission> +
<instrumentation> +
<intent-filter> +
<manifest> +
<meta-data> +
<permission> +
<permission-group> +
<permission-tree> +
<provider> +
<receiver> +
<service> +
<supports-screens> +
<uses-configuration> +
<uses-feature> +
<uses-library> +
<uses-permission> +
<uses-sdk> +

+ + + + +

Các Quy ước Tệp

+ +

+Một số quy ước và quy tắc áp dụng chung cho tất cả các phần tử và thuộc tính +trong bản kê khai: +

+ +
+
Phần tử
+
Chỉ các phần tử +<manifest> và +<application> +là bắt buộc phải có, chúng đều phải có mặt và chỉ có thể xảy ra một lần. +Hầu hết các phần tử khác có thể xảy ra nhiều lần hoặc không xảy ra — mặc dù ít +nhất một vài trong số chúng phải có mặt để bản kê khai thực sự có +ý nghĩa nào đó. + +

+Nếu một phần tử chứa bất kỳ nội dung nào, nó có thể chứa các phần tử khác. +Tất cả giá trị sẽ được đặt thông qua thuộc tính, chứ không phải là dữ liệu ký tự trong một phần tử. +

+ +

+Các phần tử cùng cấp thường không theo thứ tự. Ví dụ, các phần tử +<activity>, +<provider>, và +<service> +có thể được trộn lẫn với nhau theo bất kỳ trình tự nào. (Phần tử +<activity-alias> +là trường hợp ngoại lệ đối với quy tắc này: Nó phải tuân theo +<activity> +, đối tượng mà nó là bí danh cho.) +

+ +
Thuộc tính
+
Theo cách hiểu thông thường, tất cả thuộc tính đều mang tính tùy chọn. Tuy nhiên, có một số thuộc tính +phải được quy định cho một phần tử để hoàn thành mục đích của nó. Sử dụng +tài liệu làm hướng dẫn. Đối với những thuộc tính thực sự tùy chọn, nó đề cập tới một giá trị +mặc định hoặc thông báo điều gì sẽ xảy ra nếu không có một đặc tả. + +

Ngoài một số thuộc tính của phần tử +<manifest> +gốc, tất cả tên thuộc tính đều bắt đầu bằng một tiền tố {@code android:}— +ví dụ, {@code android:alwaysRetainTaskState}. Do tiền tố này +phổ dụng, tài liệu thường bỏ sót nó khi tham chiếu tới các thuộc tính +theo tên.

+ +
Khai báo tên lớp
+
Nhiều thuộc tính tương ứng với các đối tượng Java, bao gồm các phần tử cho +chính ứng dụng (phần tử +<application> +) và các thành phần chính của nó — hoạt động +(<activity>), +dịch vụ +(<service>), +hàm nhận quảng bá +(<receiver>), +và trình cung cấp nội dung +(<provider>). + +

+Nếu bạn định nghĩa một lớp con như vẫn luôn làm đối với lớp thành phần +({@link android.app.Activity}, {@link android.app.Service}, +{@link android.content.BroadcastReceiver}, và {@link android.content.ContentProvider}), +lớp con sẽ được khai báo thông qua một thuộc tính {@code name}. Tên phải bao gồm +chỉ định gói đầy đủ. +Ví dụ, một lớp con {@link android.app.Service} có thể được khai báo như sau: +

+ +
<manifest . . . >
+    <application . . . >
+        <service android:name="com.example.project.SecretService" . . . >
+            . . .
+        </service>
+        . . .
+    </application>
+</manifest>
+ +

+Tuy nhiên, do cách viết tốc ký, nếu ký tự đầu tiên của xâu là một dấu chấm, +xâu sẽ được nối với tên gói của ứng dụng (như được quy định bởi +thuộc tính của phần tử <manifest> + +, package +). Cách gán sau cũng giống như trên: +

+ +
<manifest package="com.example.project" . . . >
+    <application . . . >
+        <service android:name=".SecretService" . . . >
+            . . .
+        </service>
+        . . .
+    </application>
+</manifest>
+ +

+Khi khởi động một thành phần, Android sẽ tạo một thực thể của lớp con được nêu tên. +Nếu lớp con không được quy định, nó sẽ tạo một thực thể của lớp cơ sở. +

+ +
Nhiều giá trị
+
Nếu có thể quy định nhiều hơn một giá trị, phần tử gần như luôn +được lặp lại, thay vì liệt kê nhiều giá trị trong một phần tử duy nhất. +Ví dụ, một bộ lọc ý định có thể liệt kê vài hành động: + +
<intent-filter . . . >
+    <action android:name="android.intent.action.EDIT" />
+    <action android:name="android.intent.action.INSERT" />
+    <action android:name="android.intent.action.DELETE" />
+    . . .
+</intent-filter>
+ +
Giá trị tài nguyên
+
Một số thuộc tính có các giá trị có thể được hiển thị với người dùng — ví +dụ, một nhãn và một biểu tượng cho một hoạt động. Giá trị của những thuộc tính này +cần được cục bộ hóa và vì thế phải được thiết đặt từ một tài nguyên hoặc chủ đề. Giá trị +tài nguyên được biểu diễn theo định dạng sau,

+ +

{@code @[gói:]kiểu:tên}

+ +

+trong đó gói có thể được bỏ qua nếu tài nguyên nằm trong cùng gói +với ứng dụng, kiểu là kiểu của tài nguyên — chẳng hạn như "xâu" hoặc +— "vẽ được" và tên là tên nhận biết tài nguyên cụ thể. +Ví dụ: +

+ +
<activity android:icon="@drawable/smallPic" . . . >
+ +

+Các giá trị từ một chủ đề được biểu diễn theo cách tương tự, nhưng với một '{@code ?}' +thay vì '{@code @}' ở đầu: +

+ +

{@code ?[gói:]kiểu:tên} +

+ +
Giá trị xâu
+
Trường hợp giá trị của một thuộc tính là một xâu, phải sử dụng hai dấu xuyệc ngược ('{@code \\}') +để thoát các ký tự — ví dụ, '{@code \\n}' đối với +một dòng tin tức hoặc '{@code \\uxxxx}' đối với một ký tự Unicode.
+
+ + +

Các Tính năng Tệp

+ +

+Phần sau đây mô tả cách phản ánh một số tính năng của Android +trong tệp bản kê khai. +

+ + +

Bộ lọc Ý định

+ +

+Các thành phần cốt lõi của một ứng dụng (hoạt động, dịch vụ và hàm nhận +quảng bá) được kích hoạt bởi ý định. Ý định là một +gói thông tin (một đối tượng {@link android.content.Intent}) mô tả một +hành động mong muốn — bao gồm dữ liệu sẽ được dựa trên, thể loại của +thành phần mà sẽ thực hiện hành động, và các chỉ dẫn thích hợp khác. +Android định vị một thành phần phù hợp để hồi đáp ý định, khởi chạy +một thực thể mới của thành phần nếu cần, và chuyển cho nó đối tượng đó +Ý định. +

+ +

+Các thành phần sẽ quảng cáo khả năng của mình — các kiểu ý định mà chúng có thể +hồi đáp — thông qua các bộ lọc ý định. Do hệ thống Android phải +tìm hiểu một thành phần có thể xử lý những ý định nào trước khi khởi chạy thành phần đó, +bộ lọc ý định được quy định trong bản kê khai như là các phần tử +<intent-filter> +. Một thành phần có thể có nhiều bộ lọc, mỗi bộ lọc lại mô tả +một khả năng khác nhau. +

+ +

+Một ý định mà công khai nêu tên một thành phần mục tiêu sẽ kích hoạt thành phần đó; +bộ lọc không có vai trò gì ở đây. Nhưng một ý định mà không quy định một mục tiêu +theo tên sẽ chỉ có thể kích hoạt thành phần nếu nó có thể chuyển qua một trong các bộ lọc của +thành phần. +

+ +

+Để biết thông tin về cách các đối tượng Ý định được kiểm tra thông qua bộ lọc ý định, +hãy xem tài liệu riêng có tiêu đề +Ý định +và Bộ lọc Ý định. +

+ + +

Biểu tượng và Nhãn

+ +

+Nhiều phần tử có thuộc tính {@code icon} và {@code label} cho một +biểu tượng nhỏ và nhãn văn bản mà có thể được hiển thị với người dùng. Một số cũng có thuộc tính +{@code description} cho văn bản giải trình dài hơn mà cũng có thể +được hiển thị trên màn hình. Ví dụ, phần tử +<permission> +có cả ba thuộc tính này, vì thế khi người dùng được hỏi xem có +cấp quyền cho một ứng dụng yêu cầu hay không, biểu tượng thể hiện +quyền, tên của quyền, và mô tả nội dung +của quyền đó đều có thể được trình bày cho người dùng xem. +

+ +

+Trong mọi trường hợp, biểu tượng và nhãn được đặt trong một phần tử chứa sẽ trở thành các thiết đặt +{@code icon} và {@code label} mặc định cho tất cả phần tử con của bộ chứa đó. +Vì thế, biểu tượng và nhãn được đặt trong phần tử +<application> +là biểu tượng và nhãn mặc định cho từng thành phần của ứng dụng. +Tương tự, biểu tượng và nhãn được đặt cho một thành phần — ví dụ, một phần tử +<activity> +— sẽ là các cài đặt mặc định cho từng phần tử +<intent-filter> +của thành phần đó. Nếu một phần tử +<application> +thiết đặt một nhãn, nhưng hoạt động và bộ lọc ý định của nó thì không, +nhãn ứng dụng sẽ được coi là nhãn của cả hoạt động và +bộ lọc ý định. +

+ +

+Biểu tượng và nhãn được đặt cho một bộ lọc ý định sẽ được sử dụng để biểu diễn một thành phần +bất cứ khi nào thành phần đó được trình bày với người dùng để thực hiện chức năng +mà bộ lọc đã quảng cáo. Ví dụ, một bộ lọc với các thiết đặt +"{@code android.intent.action.MAIN}" và +"{@code android.intent.category.LAUNCHER}" quảng cáo một hoạt động +là hoạt động khởi đầu một ứng dụng — cụ thể, là +hoạt động sẽ được hiển thị trong trình khởi chạy ứng dụng. Vì thế, biểu tượng và nhãn +được đặt trong bộ lọc là những nội dung được hiển thị trong trình khởi chạy. +

+ + +

Quyền

+ +

+Một quyền là sự hạn chế giới hạn truy cập vào một phần của mã +hoặc vào dữ liệu trên thiết bị. Giới hạn này được áp đặt nhằm bảo vệ dữ liệu +và mã trọng yếu, có thể bị lạm dụng để bóp méo hoặc làm hỏng trải nghiệm người dùng. +

+ +

+Mỗi quyền được nhận biết bằng một nhãn duy nhất. Thông thường, nhãn cho biết +hành động bị hạn chế. Ví dụ, sau đây là một số quyền được định nghĩa +bởi Android: +

+ +

{@code android.permission.CALL_EMERGENCY_NUMBERS} +
{@code android.permission.READ_OWNER_DATA} +
{@code android.permission.SET_WALLPAPER} +
{@code android.permission.DEVICE_POWER}

+ +

+Một tính năng có thể được bảo vệ bởi nhiều nhất một quyền. +

+ +

+Nếu một ứng dụng cần truy cập vào một tính năng được bảo vệ bởi một quyền, +nó phải khai báo rằng nó yêu cầu quyền đó cùng với một phần tử +<uses-permission> +trong bản kê khai. Lúc đó, khi ứng dụng được cài đặt trên +thiết bị, trình cài đặt sẽ xác định xem có cấp quyền +được yêu cầu hay không bằng cách kiểm tra các thẩm quyền đã ký chứng chỉ +của ứng dụng và trong một số trường hợp, bằng cách hỏi người dùng. +Nếu quyền được cấp, ứng dụng có thể sử dụng các tính năng +được bảo vệ. Nếu không, việc thử truy cập những tính năng đó sẽ thất bại +mà không có bất kỳ thông báo nào cho người dùng. +

+ +

+Một ứng dụng cũng có thể bảo vệ các thành phần của chính nó (hoạt động, dịch vụ, +hàm nhận quảng bá và trình cung cấp nội dung) bằng các quyền. Nó có thể sử dụng +bất kỳ quyền nào được định nghĩa bởi Android (được liệt kê trong +{@link android.Manifest.permission android.Manifest.permission}) hoặc được khai báo +bởi các ứng dụng khác. Hoặc nó có thể tự định nghĩa quyền của mình. Một quyền mới được khai báo +bằng phần tử +<permission> +. Ví dụ, một hoạt động có thể được bảo vệ như sau: +

+ +
+<manifest . . . >
+    <permission android:name="com.example.project.DEBIT_ACCT" . . . />
+    <uses-permission android:name="com.example.project.DEBIT_ACCT" />
+    . . .
+    <application . . .>
+        <activity android:name="com.example.project.FreneticActivity"
+                  android:permission="com.example.project.DEBIT_ACCT"
+                  . . . >
+            . . .
+        </activity>
+    </application>
+</manifest>
+
+ +

+Lưu ý rằng trong ví dụ này, quyền {@code DEBIT_ACCT} không chỉ +được khai báo bằng phần tử +<permission> +, việc sử dụng quyền cũng được yêu cầu bằng phần tử +<uses-permission> +. Phải yêu cầu sử dụng quyền để các thành phần khác của +ứng dụng nhằm khởi chạy hoạt động được bảo vệ, mặc dù việc bảo vệ +do chính ứng dụng áp đặt. +

+ +

+Trong cùng ví dụ này, nếu thuộc tính {@code permission} được đặt thành một quyền +được khai báo ở nơi khác +(chẳng hạn như {@code android.permission.CALL_EMERGENCY_NUMBERS}, sẽ không +cần phải khai báo lại nó bằng một phần tử +<permission> +. Tuy nhiên, sẽ vẫn cần phải yêu cầu sử dụng nó bằng +<uses-permission>. +

+ +

+Phần tử +<permission-tree> +sẽ khai báo một vùng tên cho nhóm quyền mà sẽ được định nghĩa trong +mã. Và +<permission-group> +sẽ định nghĩa một nhãn cho một tập hợp quyền (cả được khai báo trong bản kê khai bằng phần tử +<permission> +và được khai báo ở chỗ khác). Nó chỉ ảnh hưởng tới cách các quyền được +nhóm lại khi được trình bày với người dùng. Phần tử +<permission-group> +không quy định những quyền nào thuộc về nhóm; +nó chỉ đặt cho nhóm một cái tên. Một quyền được đặt vào nhóm +bằng cách gán tên nhóm với thuộc tính của phần tử +<permission> +, +permissionGroup +. +

+ + +

Thư viện

+ +

+Mọi ứng dụng đều được liên kết với thư viện Android mặc định, nó +bao gồm các gói cơ bản để xây dựng ứng dụng (bằng các lớp thông dụng +chẳng hạn như Hoạt động, Dịch vụ, Ý định, Dạng xem, Nút, Ứng dụng, Trình cung cấp Nội dung, +v.v.). +

+ +

+Tuy nhiên, một số gói nằm trong thư viện của chính mình. Nếu ứng dụng của bạn +sử dụng mã từ bất kỳ gói nào trong những gói này, nó phải công khai yêu cầu được liên kết +với chúng. Bản kê khai phải chứa một phần tử +<uses-library> +riêng để đặt tên cho từng thư viện. (Tên thư viện có thể được tìm thấy trong tài liệu +của gói.) +

diff --git a/docs/html-intl/intl/vi/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/vi/guide/topics/providers/calendar-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..e2ecdb32b2e8116b906e2f0538b3d54d5059137c --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=Trình cung cấp Lịch +@jd:body + +
+
+

Trong tài liệu này

+
    +
  1. Nội dung Cơ bản
  2. +
  3. Quyền của Người dùng
  4. +
  5. Bảng Lịch +
      +
    1. Truy vấn một lịch
    2. +
    3. Sửa đổi một lịch
    4. +
    5. Chèn một lịch
    6. +
    +
  6. +
  7. Bảng Sự kiện +
      +
    1. Thêm Sự kiện
    2. +
    3. Cập nhật Sự kiện
    4. +
    5. Xóa Sự kiện
    6. +
    +
  8. +
  9. Bảng Người dự +
      +
    1. Thêm Người dự
    2. +
    +
  10. +
  11. Bảng Nhắc nhở +
      +
    1. Thêm Nhắc nhở
    2. +
    +
  12. +
  13. Bảng Thực thể +
      +
    1. Truy vấn bảng Thực thể
    2. +
  14. +
  15. Ý định Lịch +
      +
    1. Sử dụng ý định để chèn một sự kiện
    2. +
    3. Sử dụng ý định để chỉnh sửa một sự kiện
    4. +
    5. Sử dụng ý định để xem dữ liệu lịch
    6. +
    +
  16. + +
  17. Trình điều hợp Đồng bộ
  18. +
+ +

Lớp khóa

+
    +
  1. {@link android.provider.CalendarContract.Calendars}
  2. +
  3. {@link android.provider.CalendarContract.Events}
  4. +
  5. {@link android.provider.CalendarContract.Attendees}
  6. +
  7. {@link android.provider.CalendarContract.Reminders}
  8. +
+
+
+ +

Trình cung cấp Lịch là một kho lưu trữ các sự kiện lịch của người dùng. API +Trình cung cấp Lịch cho phép bạn thực hiện truy vấn, chèn, cập nhật và xóa +các thao tác trên lịch, sự kiện, người dự, nhắc nhở, v.v.

+ + +

API Trình cung cấp Lịch có thể được sử dụng bởi các ứng dụng và trình điều hợp đồng bộ. Các quy tắc +thay đổi tùy vào loại chương trình đang thực hiện lệnh gọi. Tài liệu này +tập trung chủ yếu vào việc sử dụng API Trình cung cấp Lịch như một ứng dụng. Để bàn +về việc các trình điều hợp đồng bộ khác nhau như thế nào, hãy xem phần +Trình điều hợp Đồng bộ.

+ + +

Thông thường, để đọc hoặc ghi dữ liệu lịch, bản kê khai của ứng dụng phải +bao gồm các quyền thích hợp của người dùng, được nêu trong phần Quyền +của Người dùng. Để thực hiện các thao tác chung dễ hơn, Trình cung cấp +Lịch đưa ra một tập hợp ý định, như được mô tả trong phần Ý định +Lịch. Những ý định này đưa người dùng tới ứng dụng Lịch để chèn, xem, +và chỉnh sửa sự kiện. Người dùng tương tác với ứng dụng Lịch rồi +quay lại ứng dụng ban đầu. Vì thế, ứng dụng của bạn không cần yêu cầu quyền, +và cũng không cần cung cấp một giao diện người dùng để xem hoặc tạo sự kiện.

+ +

Nội dung Cơ bản

+ +

Các trình cung cấp nội dung sẽ lưu trữ dữ liệu và cho phép truy cập +ứng dụng. Trình cung cấp nội dung được nền tảng Android giới thiệu (bao gồm Trình cung cấp Lịch) thường trình bày dữ liệu như một tập hợp gồm nhiều bảng dựa trên một +mô hình cơ sở dữ liệu quan hệ, trong đó mỗi hàng là một bản ghi và mỗi cột là dữ liệu thuộc +một loại và có ý nghĩa cụ thể. Thông qua API Trình cung cấp Lịch, các ứng dụng +và trình điều hợp đồng bộ có thể nhận được quyền truy cập đọc/ghi vào các bảng trong cơ sở dữ liệu là nơi chứa +dữ liệu lịch của người dùng.

+ +

Mọi trình cung cấp nội dung đều đưa ra một URI công khai (được bẻ dòng như một đối tượng +{@link android.net.Uri} +) để xác định tập dữ liệu của nó một cách duy nhất. Trình cung cấp nội dung mà kiểm soát nhiều + tập dữ liệu (nhiều bảng) sẽ đưa ra một URI riêng cho từng bảng. Tất cả +URI cho trình cung cấp đều bắt đầu bằng xâu "content://". Điều này +sẽ xác định dữ liệu là đang được kiểm soát bởi một trình cung cấp nội dung. Trình cung cấp +Lịch định nghĩa các hằng số cho URI đối với từng lớp (bảng) của nó. Những URI +này có định dạng <class>.CONTENT_URI. Ví +dụ, {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}.

+ +

Hình 1 cho biết biểu diễn đồ họa của mô hình dữ liệu Trình cung cấp Lịch. Nó cho biết +các bảng chính và các trường liên kết chúng với nhau.

+ +Calendar Provider Data Model +

Hình 1. Mô hình dữ liệu Trình cung cấp Lịch.

+ +

Một người dùng có thể có nhiều lịch, và các lịch khác nhau có thể được liên kết với các loại tài khoản khác nhau (Google Calendar, Exchange, v.v.).

+ +

{@link android.provider.CalendarContract} sẽ định nghĩa mô hình dữ liệu của thông tin liên quan tới lịch và sự kiện. Dữ liệu này được lưu trữ trong nhiều bảng như liệt kê bên dưới.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bảng (Lớp)Mô tả

{@link android.provider.CalendarContract.Calendars}

Bảng này chứa +thông tin riêng của lịch. Mỗi hàng trong bảng này chứa chi tiết của +một lịch duy nhất, chẳng hạn như tên, màu, thông tin đồng bộ, v.v.
{@link android.provider.CalendarContract.Events}Bảng này chứa +thông tin riêng theo sự kiện. Mỗi hàng trong bảng có thông tin cho một +sự kiện duy nhất—ví dụ: tiêu đề sự kiện, địa điểm, thời gian bắt đầu +, thời gian kết thúc, v.v. Sự kiện có thể xảy ra một lần hoặc lặp lại nhiều lần. Người dự, +nhắc nhở, và các tính chất mở rộng được lưu trữ trong các bảng riêng. +Mỗi mục đều có một {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +tham chiếu tới {@link android.provider.BaseColumns#_ID} trong bảng Sự kiện.
{@link android.provider.CalendarContract.Instances}Bảng này chứa +thời gian bắt đầu và thời gian kết thúc của mỗi lần xảy ra một sự kiện. Mỗi hàng trong bảng này +đại diện cho một lần xảy ra sự kiện. Với các sự kiện xảy ra một lần thì có một ánh xạ 1:1 +của thực thể tới sự kiện. Đối với các sự kiện định kỳ, nhiều hàng sẽ tự động + được khởi tạo tương ứng với nhiều lần xảy ra sự kiện đó.
{@link android.provider.CalendarContract.Attendees}Bảng này chứa +thông tin về người dự (khách) của sự kiện. Mỗi hàng đại diện một khách duy nhất của +một sự kiện. Nó quy định loại khách và phản hồi tham dự của khách +cho một sự kiện.
{@link android.provider.CalendarContract.Reminders}Bảng này chứa +dữ liệu về cảnh báo/thông báo. Mỗi hàng đại diện một cảnh báo duy nhất cho một sự kiện. Một +sự kiện có thể có nhiều nhắc nhở. Số nhắc nhở tối đa của một sự kiện +được quy định trong +{@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS}, +được đặt bởi trình điều hợp đồng bộ đang +sở hữu lịch đã cho. Nhắc nhở được quy định bằng số phút trước khi diễn ra sự kiện +và có một phương pháp để xác định cách người dùng sẽ được cảnh báo.
+ +

API Trình cung cấp Lịch được thiết kế để linh hoạt và mạnh mẽ. Đồng +thời, điều quan trọng là phải cung cấp một trải nghiệm người dùng cuối tốt và +bảo vệ sự toàn vẹn của lịch và dữ liệu lịch. Để làm điều này, sau đây là một số +điều cần ghi nhớ khi sử dụng API này:

+ +
    + +
  • Chèn, cập nhật, và xem sự kiện lịch. Để trực tiếp chèn, sửa đổi, và đọc sự kiện từ Trình cung cấp Lịch, bạn cần các quyền phù hợp. Tuy nhiên, nếu bạn không đang xây dựng một ứng dụng lịch hoặc trình điều hợp đồng bộ chính thức, việc yêu cầu những quyền này là không cần thiết. Thay vào đó, bạn có thể sử dụng những ý định được cung cấp bởi ứng dụng Lịch của Android để chuyển giao các thao tác đọc và ghi cho ứng dụng đó. Khi bạn sử dụng ý định, ứng dụng của bạn sẽ gửi người dùng tới ứng dụng Lịch để thực hiện thao tác mong muốn +trong một mẫu được điền trước. Sau khi xong, họ sẽ được trả về ứng dụng của bạn. +Bằng việc thiết kế ứng dụng của bạn để thực hiện các thao tác thường gặp thông qua Lịch, +bạn cung cấp cho người dùng một giao diện người dùng nhất quán, thiết thực. Đây là phương pháp +được khuyến cáo nên dùng. Để biết thêm thông tin, hãy xem phần Ý định +Lịch.

    + + +
  • Trình điều hợp đồng bộ. Trình điều hợp đồng bộ có chức năng đồng bộ dữ liệu lịch +lên thiết bị của một người dùng bằng một máy chủ hoặc nguồn dữ liệu khác. Trong bảng +{@link android.provider.CalendarContract.Calendars} và +{@link android.provider.CalendarContract.Events}, +có các cột để cho trình điều hợp đồng bộ sử dụng. +Trình cung cấp và ứng dụng không nên sửa đổi chúng. Trên thực tế, chúng không +hiển thị trừ khi được truy cập như một trình điều hợp đồng bộ. Để biết thêm thông tin về +trình điều hợp đồng bộ, hãy xem phần Trình điều hợp Đồng bộ.
  • + +
+ + +

Quyền của Người dùng

+ +

Để đọc dữ liệu lịch, một ứng dụng phải bao gồm quyền {@link +android.Manifest.permission#READ_CALENDAR} trong tệp bản kê khai của mình. Nó +phải bao gồm quyền {@link android.Manifest.permission#WRITE_CALENDAR} +để xóa, chèn hoặc cập nhật dữ liệu lịch:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
+    <uses-sdk android:minSdkVersion="14" />
+    <uses-permission android:name="android.permission.READ_CALENDAR" />
+    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
+    ...
+</manifest>
+
+ + +

Bảng Lịch

+ +

Bảng {@link android.provider.CalendarContract.Calendars} chứa thông tin chi tiết +cho từng lịch. Các cột +Lịch sau có thể ghi được bởi cả ứng dụng và trình điều hợp đồng bộ. +Để xem danh sách đầy đủ về các trường được hỗ trợ, hãy xem tài liệu tham khảo +{@link android.provider.CalendarContract.Calendars}.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Hằng sốMô tả
{@link android.provider.CalendarContract.Calendars#NAME}Tên của lịch.
{@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}Tên của lịch này mà được hiển thị cho người dùng.
{@link android.provider.CalendarContract.Calendars#VISIBLE}Một boolean cho biết lịch có được chọn để hiển thị hay không. Giá trị +bằng 0 cho biết các sự kiện liên kết với lịch này sẽ không được +hiển thị. Giá trị bằng 1 cho biết các sự kiện liên kết với lịch này sẽ được +hiển thị. Giá trị này ảnh hưởng tới việc khởi tạo hàng trong bảng {@link +android.provider.CalendarContract.Instances}.
{@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}Một boolean cho biết lịch sẽ được đồng bộ và có các sự kiện +của mình được lưu trữ trên thiết bị hay không. Giá trị bằng 0 tức là không đồng bộ lịch này hay +lưu giữ các sự kiện của nó lên thiết bị. Giá trị bằng 1 tức là đồng bộ các sự kiện cho lịch này +và lưu trữ các sự kiện của nó lên thiết bị.
+ +

Truy vấn một lịch

+ +

Sau đây là một ví dụ về cách nhận được lịch do một người dùng +cụ thể sở hữu. Để đơn giản, trong ví dụ này, thao tác truy vấn được thể hiện trong + luồng giao diện người dùng ("luồng chính"). Trong thực hành, nên làm điều này trong một luồng +không đồng bộ thay vì trên luồng chính. Để bàn thêm, hãy xem phần +Trình tải. Nếu bạn đang không chỉ +đọc dữ liệu mà còn sửa đổi nó, hãy xem {@link android.content.AsyncQueryHandler}. +

+ + +
+// Projection array. Creating indices for this array instead of doing
+// dynamic lookups improves performance.
+public static final String[] EVENT_PROJECTION = new String[] {
+    Calendars._ID,                           // 0
+    Calendars.ACCOUNT_NAME,                  // 1
+    Calendars.CALENDAR_DISPLAY_NAME,         // 2
+    Calendars.OWNER_ACCOUNT                  // 3
+};
+  
+// The indices for the projection array above.
+private static final int PROJECTION_ID_INDEX = 0;
+private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
+private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
+private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
+ + + + + +

Trong phần tiếp theo của ví dụ, bạn sẽ xây dựng truy vấn của mình. Lựa chọn +này sẽ quy định các tiêu chí cho truy vấn. Trong ví dụ này, truy vấn đang tìm +các lịch có ACCOUNT_NAME +"sampleuser@google.com", ACCOUNT_TYPE +"com.google", và OWNER_ACCOUNT +"sampleuser@google.com". Nếu bạn muốn xem tất cả lịch mà một người dùng +đã xem, không chỉ các lịch mà người dùng sở hữu, hãy bỏ qua OWNER_ACCOUNT. +Truy vấn sẽ trả về đối tượng {@link android.database.Cursor} +mà bạn có thể sử dụng để xem xét tập kết quả được trả về bởi truy vấn +cơ sở dữ liệu. Để bàn thêm về việc sử dụng các truy vấn trong trình cung cấp nội dung, +hãy xem phần Trình cung cấp Nội dung.

+ + +
// Run query
+Cursor cur = null;
+ContentResolver cr = getContentResolver();
+Uri uri = Calendars.CONTENT_URI;   
+String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" 
+                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
+                        + Calendars.OWNER_ACCOUNT + " = ?))";
+String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
+        "sampleuser@gmail.com"}; 
+// Submit the query and get a Cursor object back. 
+cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
+ +

Phần tiếp theo sử dụng con chạy để duyệt qua tập kết quả. Nó sử dụng +các hằng số được thiết lập ngay từ đầu ví dụ để trả về các giá trị +cho mỗi trường.

+ +
// Use the cursor to step through the returned records
+while (cur.moveToNext()) {
+    long calID = 0;
+    String displayName = null;
+    String accountName = null;
+    String ownerName = null;
+      
+    // Get the field values
+    calID = cur.getLong(PROJECTION_ID_INDEX);
+    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
+    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
+    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
+              
+    // Do something with the values...
+
+   ...
+}
+
+ +

Sửa đổi một lịch

+ +

Để thực hiện cập nhật một lịch, bạn có thể cung cấp {@link +android.provider.BaseColumns#_ID} của lịch hoặc dưới dạng ID được nối vào cho +Uri + +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) +hoặc dưới dạng mục chọn đầu tiên. Lựa chọn +nên bắt đầu bằng "_id=?", và +selectionArg đầu tiên sẽ là {@link +android.provider.BaseColumns#_ID} của lịch. +Bạn cũng có thể thực hiện cập nhật bằng cách mã hóa ID trong URI. Ví dụ này thay đổi tên hiển thị +của một lịch bằng cách sử dụng phương pháp +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) +:

+ +
private static final String DEBUG_TAG = "MyActivity";
+...
+long calID = 2;
+ContentValues values = new ContentValues();
+// The new display name for the calendar
+values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
+Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
+int rows = getContentResolver().update(updateUri, values, null, null);
+Log.i(DEBUG_TAG, "Rows updated: " + rows);
+ +

Chèn một lịch

+ +

Lịch được thiết kế để được quản lý chủ yếu bởi một trình điều hợp đồng bộ, vì vậy bạn chỉ nên +chèn các lịch mới như một trình điều hợp đồng bộ. Phần lớn thì các ứng dụng +chỉ có thể thực hiện những thay đổi bề mặt về lịch chẳng hạn như thay đổi tên hiển thị. Nếu +một ứng dụng cần tạo một lịch cục bộ, nó có thể làm điều này bằng cách thực hiện +chèn lịch dưới dạng một trình điều hợp đồng bộ, sử dụng {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} của {@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL}. +{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} +là một loại tài khoản đặc biệt dành cho các lịch không +liên kết với một tài khoản thiết bị. Các lịch loại này không được đồng bộ với một máy chủ. Để +bàn về trình điều hợp đồng bộ, hãy xem phần Trình điều hợp Đồng bộ.

+ +

Bảng Sự kiện

+ +

Bảng {@link android.provider.CalendarContract.Events} chứa thông tin chi tiết +cho các sự kiện riêng lẻ. Để thêm, cập nhật hoặc xóa sự kiện, ứng dụng phải +bao gồm quyền {@link android.Manifest.permission#WRITE_CALENDAR} trong +tệp bản kê khai của mình.

+ +

Các cột Sự kiện sau có thể ghi được bởi cả ứng dụng và trình điều hợp +đồng bộ. Để xem danh sách đầy đủ về các trường được hỗ trợ, hãy xem tài liệu tham khảo {@link +android.provider.CalendarContract.Events}.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hằng sốMô tả
{@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}{@link android.provider.BaseColumns#_ID} của lịch mà chứa sự kiện.
{@link android.provider.CalendarContract.EventsColumns#ORGANIZER}E-mail của người tổ chức (người chủ) của sự kiện.
{@link android.provider.CalendarContract.EventsColumns#TITLE}Tiêu đề của sự kiện.
{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}Nơi sự kiện diễn ra.
{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}Mô tả sự kiện.
{@link android.provider.CalendarContract.EventsColumns#DTSTART}Thời gian sự kiện bắt đầu tính bằng mili giây UTC trôi qua kể từ giờ epoch.
{@link android.provider.CalendarContract.EventsColumns#DTEND}Thời gian sự kiện kết thúc tính bằng mili giây UTC trôi qua kể từ giờ epoch.
{@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}Múi giờ của sự kiện.
{@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}Múi giờ của thời điểm kết thúc sự kiện.
{@link android.provider.CalendarContract.EventsColumns#DURATION}Thời lượng của sự kiện theo định dạng RFC5545. +Ví dụ, giá trị bằng "PT1H" cho biết sự kiện sẽ kéo dài +một giờ và giá trị bằng "P2W" cho biết +thời lượng là 2 tuần.
{@link android.provider.CalendarContract.EventsColumns#ALL_DAY}Giá trị bằng 1 cho biết sự kiện này chiếm cả ngày, được xác định bởi +múi giờ tại địa phương. Giá trị bằng 0 cho biết đó là một sự kiện thường xuyên mà có thể bắt đầu +và kết thúc vào bất cứ lúc nào trong một ngày.
{@link android.provider.CalendarContract.EventsColumns#RRULE}Quy tắc lặp lại đối với định dạng sự kiện. Ví +dụ, "FREQ=WEEKLY;COUNT=10;WKST=SU". Bạn có thể tìm thêm +nhiều ví dụ hơn ở đây.
{@link android.provider.CalendarContract.EventsColumns#RDATE}Ngày lặp lại đối với sự kiện. + Bạn thường sử dụng {@link android.provider.CalendarContract.EventsColumns#RDATE} + cùng với {@link android.provider.CalendarContract.EventsColumns#RRULE} + để định nghĩa một tập tổng hợp +các trường hợp xảy ra lặp lại. Để bàn thêm, hãy xem phần RFC5545 spec.
{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}Xem sự kiện này được tính là thời gian bận hay là thời gian rảnh có thể được +xếp lại lịch.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}Khách có thể sửa đổi sự kiện được hay không.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}Khách có thể mời khách khác được hay không.
{@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}Khách có thể xem danh sách người dự được hay không.
+ +

Thêm Sự kiện

+ +

Khi ứng dụng của bạn chèn một sự kiện mới, chúng tôi khuyến cáo bạn nên sử dụng một Ý định +{@link android.content.Intent#ACTION_INSERT INSERT}, như được mô tả trong Sử dụng ý định để chèn một sự kiện. Tuy nhiên, nếu +cần, bạn có thể chèn sự kiện trực tiếp. Phần này mô tả cách làm điều +này.

+ + +

Sau đây là các quy tắc để chèn một sự kiện mới:

+
    + +
  • Bạn phải đưa vào {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} và {@link +android.provider.CalendarContract.EventsColumns#DTSTART}.
  • + +
  • Bạn phải đưa vào một {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}. Để nhận một danh sách +các ID múi giờ được cài đặt của hệ thống, hãy sử dụng {@link +java.util.TimeZone#getAvailableIDs()}. Lưu ý rằng quy tắc này không áp dụng nếu +bạn đang chèn một sự kiện thông qua Ý định {@link +android.content.Intent#ACTION_INSERT INSERT} như được mô tả trong Sử dụng ý định để chèn một sự kiện—trong kịch bản +đó, một múi giờ mặc định sẽ được cung cấp.
  • + +
  • Đối với các sự kiện không định kỳ, bạn phải đưa vào {@link +android.provider.CalendarContract.EventsColumns#DTEND}.
  • + + +
  • Đối với các sự kiện định kỳ, bạn phải đưa vào một {@link +android.provider.CalendarContract.EventsColumns#DURATION} bên cạnh {@link +android.provider.CalendarContract.EventsColumns#RRULE} hay {@link +android.provider.CalendarContract.EventsColumns#RDATE}. Lưu ý rằng quy tắc này không áp dụng nếu +bạn đang chèn một sự kiện thông qua Ý định {@link +android.content.Intent#ACTION_INSERT INSERT} như được mô tả trong Sử dụng ý định để chèn một sự kiện—trong kịch bản +đó, bạn có thể sử dụng một {@link +android.provider.CalendarContract.EventsColumns#RRULE} cùng với {@link android.provider.CalendarContract.EventsColumns#DTSTART} và {@link android.provider.CalendarContract.EventsColumns#DTEND}, và ứng dụng Lịch +sẽ tự động chuyển nó thành một thời lượng.
  • + +
+ +

Sau đây là một ví dụ về cách chèn một sự kiện. Ví dụ này đang được thực hiện trong luồng +UI để cho đơn giản. Trong thực hành, chèn và cập nhật nên được thực hiện trong một +luồng không đồng bộ để di chuyển hành động vào một luồng chạy ngầm. Để biết thêm +thông tin, hãy xem phần {@link android.content.AsyncQueryHandler}.

+ + +
+long calID = 3;
+long startMillis = 0; 
+long endMillis = 0;     
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2012, 9, 14, 7, 30);
+startMillis = beginTime.getTimeInMillis();
+Calendar endTime = Calendar.getInstance();
+endTime.set(2012, 9, 14, 8, 45);
+endMillis = endTime.getTimeInMillis();
+...
+
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Events.DTSTART, startMillis);
+values.put(Events.DTEND, endMillis);
+values.put(Events.TITLE, "Jazzercise");
+values.put(Events.DESCRIPTION, "Group workout");
+values.put(Events.CALENDAR_ID, calID);
+values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
+Uri uri = cr.insert(Events.CONTENT_URI, values);
+
+// get the event ID that is the last element in the Uri
+long eventID = Long.parseLong(uri.getLastPathSegment());
+// 
+// ... do something with event ID
+//
+//
+ +

Lưu ý: Xem cách mà ví dụ này bắt được ID +sự kiện sau khi sự kiện được tạo. Đây là cách dễ nhất để nhận được một ID sự kiện. Bạn thường +cần ID sự kiện để thực hiện các thao tác lịch khác—ví dụ: để thêm +người dự hoặc nhắc nhở vào một sự kiện.

+ + +

Cập nhật Sự kiện

+ +

Khi ứng dụng của bạn muốn cho phép người dùng chỉnh sửa một sự kiện, chúng tôi khuyến cáo +bạn nên sử dụng một Ý định {@link android.content.Intent#ACTION_EDIT EDIT} như được mô tả +trong Sử dụng ý định để chỉnh sửa một sự kiện. +Tuy nhiên, nếu cần, bạn có thể chỉnh sửa sự kiện trực tiếp. Để thực hiện cập nhật +một Sự kiện, bạn có thể cung cấp _ID của sự kiện +hoặc dưới dạng ID được nối vào cho Uri ({@link +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) +hoặc dưới dạng mục chọn đầu tiên. +Lựa chọn nên bắt đầu bằng "_id=?", và +selectionArg đầu tiên nên là _ID của sự kiện. Bạn cũng có thể +thực hiện cập nhật bằng cách sử dụng một lựa chọn không có ID. Sau đây là một ví dụ về cách cập nhật một +sự kiện. Nó thay đổi tiêu đề của sự kiện bằng cách sử dụng phương pháp +{@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()} +:

+ + +
private static final String DEBUG_TAG = "MyActivity";
+...
+long eventID = 188;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+Uri updateUri = null;
+// The new title for the event
+values.put(Events.TITLE, "Kickboxing"); 
+updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+int rows = getContentResolver().update(updateUri, values, null, null);
+Log.i(DEBUG_TAG, "Rows updated: " + rows);  
+ +

Xóa Sự kiện

+ +

Bạn có thể xóa một sự kiện hoặc theo {@link +android.provider.BaseColumns#_ID} của nó như một ID được nối trên URI, hoặc bằng cách sử dụng +lựa chọn tiêu chuẩn. Nếu sử dụng một ID được nối, bạn không thể lựa chọn đồng thời. +Có hai kiểu xóa: như một ứng dụng và như một trình điều hợp đồng bộ. Xóa +như một ứng dụng sẽ đặt cột đã xóa thành 1. Cờ này sẽ báo cho +trình điều hợp đồng bộ rằng hàng đó đã được xóa và rằng việc xóa này nên +được truyền tới máy chủ. Xóa trình điều hợp đồng bộ sẽ gỡ bỏ sự kiện khỏi +cơ sở dữ liệu cùng với tất cả dữ liệu được liên kết của nó. Sau đây là một ví dụ về ứng dụng +xóa một sự kiện thông qua {@link android.provider.BaseColumns#_ID} của nó:

+ + +
private static final String DEBUG_TAG = "MyActivity";
+...
+long eventID = 201;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+Uri deleteUri = null;
+deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+int rows = getContentResolver().delete(deleteUri, null, null);
+Log.i(DEBUG_TAG, "Rows deleted: " + rows);  
+
+ +

Bảng Người dự

+ +

Mỗi hàng của bảng {@link android.provider.CalendarContract.Attendees} đại diện +cho một người dự hoặc khách duy nhất của một sự kiện. Gọi +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} +sẽ trả về một danh sách người dự cho sự kiện +với {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} đã cho. +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} này +phải khớp với {@link +android.provider.BaseColumns#_ID} của một sự kiện cụ thể.

+ +

Bảng sau liệt kê các trường +có thể ghi được. Khi chèn một người dự mới, bạn phải điền tất cả +ngoại trừ ATTENDEE_NAME. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hằng sốMô tả
{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}ID của sự kiện.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}Tên của người dự.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}Địa chỉ e-mail của người dự.
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}

Mối quan hệ của người dự với sự kiện. Một trong:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}
  • +
+
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}

Loại người dự. Một trong:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}
  • +
{@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}

Trạng thái tham dự của người dự. Một trong:

+
    +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}
  • +
  • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}
  • +
+ +

Thêm Người dự

+ +

Sau đây là một ví dụ về cách thêm một người dự vào một sự kiện. Lưu ý rằng +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +là bắt buộc:

+ +
+long eventID = 202;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Attendees.ATTENDEE_NAME, "Trevor");
+values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
+values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
+values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
+values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
+values.put(Attendees.EVENT_ID, eventID);
+Uri uri = cr.insert(Attendees.CONTENT_URI, values);
+
+ +

Bảng Nhắc nhở

+ +

Mỗi hàng của bảng {@link android.provider.CalendarContract.Reminders} đại diện +cho một nhắc nhở của một sự kiện. Gọi +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} sẽ trả về một danh sách nhắc nhở cho +sự kiện với +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} đã cho.

+ + +

Bảng sau liệt kê các trường ghi được đối với nhắc nhở. Tất cả đều phải được +đưa vào khi chèn một nhắc nhở mới. Lưu ý rằng các trình điều hợp đồng bộ quy định +các loại nhắc nhở chúng hỗ trợ trong bảng {@link +android.provider.CalendarContract.Calendars}. Xem +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS} +để biết chi tiết.

+ + + + + + + + + + + + + + + + + + + +
Hằng sốMô tả
{@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}ID của sự kiện.
{@link android.provider.CalendarContract.RemindersColumns#MINUTES}Số phút trước khi diễn ra sự kiện mà nhắc nhở cần báo.
{@link android.provider.CalendarContract.RemindersColumns#METHOD}

Phương pháp báo thức, như được đặt trên máy chủ. Một trong:

+
    +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}
  • +
  • {@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}
  • +
+ +

Thêm Nhắc nhở

+ +

Ví dụ này thêm nhắc nhở vào một sự kiện. Nhắc nhở sẽ báo 15 +phút trước khi xảy ra sự kiện.

+
+long eventID = 221;
+...
+ContentResolver cr = getContentResolver();
+ContentValues values = new ContentValues();
+values.put(Reminders.MINUTES, 15);
+values.put(Reminders.EVENT_ID, eventID);
+values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
+Uri uri = cr.insert(Reminders.CONTENT_URI, values);
+ +

Bảng Thực thể

+ +

Bảng +{@link android.provider.CalendarContract.Instances} chứa +thời gian bắt đầu và thời gian kết thúc của các lần xảy ra một sự kiện. Mỗi hàng trong bảng này +đại diện cho một lần xảy ra sự kiện. Bảng thực thể không ghi được và chỉ +đưa ra một cách để truy vấn các lần xảy ra sự kiện.

+ +

Bảng sau liệt kê một số trường mà bạn có thể truy vấn đối với một thực thể. Lưu ý +rằng múi giờ được định nghĩa bởi +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} +và +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES}.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hằng sốMô tả
{@link android.provider.CalendarContract.Instances#BEGIN}Thời gian bắt đầu của thực thể, tính bằng mili giây UTC.
{@link android.provider.CalendarContract.Instances#END}Thời gian kết thúc của thực thể, tính bằng mili giây UTC.
{@link android.provider.CalendarContract.Instances#END_DAY}Ngày kết thúc theo lịch Julian của thực thể theo múi giờ +của Lịch. + +
{@link android.provider.CalendarContract.Instances#END_MINUTE}Phút kết thúc của thực thể được xác định từ nửa đêm theo múi giờ +của Lịch.
{@link android.provider.CalendarContract.Instances#EVENT_ID}_ID của sự kiện đối với thực thể này.
{@link android.provider.CalendarContract.Instances#START_DAY}Ngày bắt đầu theo lịch Julian của thực thể theo múi giờ của Lịch. +
{@link android.provider.CalendarContract.Instances#START_MINUTE}Phút bắt đầu của thực thể được xác định từ nửa đêm theo múi giờ +của Lịch. +
+ +

Truy vấn bảng Thực thể

+ +

Để truy vấn bảng Thực thể, bạn cần chỉ định một khoảng thời gian cho truy vấn +trong URI. Trong ví dụ này, {@link android.provider.CalendarContract.Instances} +có quyền truy cập trường {@link +android.provider.CalendarContract.EventsColumns#TITLE} thông qua việc +triển khai giao diện {@link android.provider.CalendarContract.EventsColumns} của nó. +Nói cách khác, {@link +android.provider.CalendarContract.EventsColumns#TITLE} được trả về qua một +chế độ xem cơ sở dữ liệu, chứ không qua việc truy vấn bảng {@link +android.provider.CalendarContract.Instances} thô.

+ +
+private static final String DEBUG_TAG = "MyActivity";
+public static final String[] INSTANCE_PROJECTION = new String[] {
+    Instances.EVENT_ID,      // 0
+    Instances.BEGIN,         // 1
+    Instances.TITLE          // 2
+  };
+  
+// The indices for the projection array above.
+private static final int PROJECTION_ID_INDEX = 0;
+private static final int PROJECTION_BEGIN_INDEX = 1;
+private static final int PROJECTION_TITLE_INDEX = 2;
+...
+
+// Specify the date range you want to search for recurring
+// event instances
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2011, 9, 23, 8, 0);
+long startMillis = beginTime.getTimeInMillis();
+Calendar endTime = Calendar.getInstance();
+endTime.set(2011, 10, 24, 8, 0);
+long endMillis = endTime.getTimeInMillis();
+  
+Cursor cur = null;
+ContentResolver cr = getContentResolver();
+
+// The ID of the recurring event whose instances you are searching
+// for in the Instances table
+String selection = Instances.EVENT_ID + " = ?";
+String[] selectionArgs = new String[] {"207"};
+
+// Construct the query with the desired date range.
+Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
+ContentUris.appendId(builder, startMillis);
+ContentUris.appendId(builder, endMillis);
+
+// Submit the query
+cur =  cr.query(builder.build(), 
+    INSTANCE_PROJECTION, 
+    selection, 
+    selectionArgs, 
+    null);
+   
+while (cur.moveToNext()) {
+    String title = null;
+    long eventID = 0;
+    long beginVal = 0;    
+    
+    // Get the field values
+    eventID = cur.getLong(PROJECTION_ID_INDEX);
+    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
+    title = cur.getString(PROJECTION_TITLE_INDEX);
+              
+    // Do something with the values. 
+    Log.i(DEBUG_TAG, "Event:  " + title); 
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTimeInMillis(beginVal);  
+    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
+    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));    
+    }
+ }
+ +

Ý định Lịch

+

Ứng dụng của bạn không cần quyền để ghi và đọc dữ liệu lịch. Thay vào đó, nó có thể sử dụng những ý định được cung cấp bởi ứng dụng Lịch của Android để chuyển giao các thao tác đọc và ghi cho ứng dụng đó. Bảng sau đây liệt kê các ý định được Trình cung cấp Lịch hỗ trợ:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hành độngURIMô tảPhụ thêm

+ {@link android.content.Intent#ACTION_VIEW VIEW}

content://com.android.calendar/time/<ms_since_epoch>

+ Bạn cũng có thể tham khảo tới URI bằng +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI}. +Để xem một ví dụ về cách sử dụng ý định này, hãy xem Sử dụng ý định để xem dữ liệu lịch. + +
Mở lịch đến thời gian được chỉ định bởi <ms_since_epoch>.Không có.

{@link android.content.Intent#ACTION_VIEW VIEW}

+ +

content://com.android.calendar/events/<event_id>

+ + Bạn cũng có thể tham khảo tới URI bằng +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Để xem một ví dụ về cách sử dụng ý định này, hãy xem Sử dụng ý định để xem dữ liệu lịch. + +
Xem sự kiện được chỉ định bởi <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
+
+
+ {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
{@link android.content.Intent#ACTION_EDIT EDIT}

content://com.android.calendar/events/<event_id>

+ + Bạn cũng có thể tham khảo tới URI bằng +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Để xem một ví dụ về cách sử dụng ý định này, hãy xem Sử dụng ý định để chỉnh sửa một sự kiện. + + +
Chỉnh sửa sự kiện được chỉ định bởi <event_id>.{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
+
+
+ {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
{@link android.content.Intent#ACTION_EDIT EDIT}
+
+ {@link android.content.Intent#ACTION_INSERT INSERT}

content://com.android.calendar/events

+ + Bạn cũng có thể tham khảo tới URI bằng +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}. +Để xem một ví dụ về cách sử dụng ý định này, hãy xem Sử dụng ý định để chèn một sự kiện. + +
Tạo một sự kiện.Bất kỳ phụ thêm nào được liệt kê trong bảng bên dưới.
+ +

Bảng sau đây liệt kê các phụ thêm ý định được Trình cung cấp Lịch hỗ trợ: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Phụ thêm Ý địnhMô tả
{@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}Tên cho sự kiện.
{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}Thời gian bắt đầu sự kiện tính bằng mili giây trôi qua kể từ giờ epoch.
{@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}Thời gian kết thúc sự kiện tính bằng mili giây trôi qua kể từ giờ epoch.
{@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}Một boolean cho biết đó là một sự kiện cả ngày. Giá trị có thể bằng +true hoặc false.
{@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}Địa điểm của sự kiện.
{@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}Mô tả sự kiện.
+ {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}Địa chỉ e-mail của những người cần mời dưới dạng một danh sách phân cách bởi dấu phẩy.
+ {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}Quy tắc lặp lại đối với sự kiện.
+ {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}Sự kiện là riêng tư hay công khai.
{@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}Xem sự kiện này tính là thời gian bận hay là thời gian rảnh có thể được xếp lại lịch.
+

Các phần sau mô tả cách sử dụng những ý định này.

+ + +

Sử dụng ý định để chèn một sự kiện

+ +

Sử dụng Ý định {@link android.content.Intent#ACTION_INSERT INSERT} cho phép +ứng dụng của bạn chuyển giao tác vụ chèn sự kiện cho bản thân Lịch. +Bằng cách này, ứng dụng của bạn thậm chí không cần phải có quyền {@link +android.Manifest.permission#WRITE_CALENDAR} được bao gồm trong tệp bản kê khai của mình.

+ + +

Khi người dùng chạy một ứng dụng mà sử dụng cách này, ứng dụng sẽ gửi +chúng tới Lịch để hoàn thành việc thêm một sự kiện. Ý định {@link +android.content.Intent#ACTION_INSERT INSERT} sử dụng các trường phụ thêm để +điền trước vào một mẫu bằng các chi tiết của sự kiện trong Lịch. Khi đó, người dùng có thể +hủy bỏ sự kiện, chỉnh sửa mẫu nếu cần, hoặc lưu sự kiện vào lịch +của mình.

+ + + +

Sau đây là một đoạn mã HTML lập biểu một sự kiện vào ngày 19/1/2012, diễn ra +từ 7:30 sáng đến 8:30 sáng. Lưu ý điều sau đây về đoạn mã HTML này:

+ +
    +
  • Nó quy định {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} + là Uri.
  • + +
  • Nó sử dụng các trường phụ {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} và {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} để điền trước thời gian của sự kiện +vào mẫu. Các giá trị đối với những thời gian này phải tính bằng mili giây UTC +trôi qua kể từ giờ epoch.
  • + +
  • Nó sử dụng trường phụ {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} +để cung cấp một danh sách người được mời phân cách bằng dấu phẩy, được chỉ định theo địa chỉ e-mail.
  • + +
+
+Calendar beginTime = Calendar.getInstance();
+beginTime.set(2012, 0, 19, 7, 30);
+Calendar endTime = Calendar.getInstance();
+endTime.set(2012, 0, 19, 8, 30);
+Intent intent = new Intent(Intent.ACTION_INSERT)
+        .setData(Events.CONTENT_URI)
+        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
+        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
+        .putExtra(Events.TITLE, "Yoga")
+        .putExtra(Events.DESCRIPTION, "Group class")
+        .putExtra(Events.EVENT_LOCATION, "The gym")
+        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
+        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
+startActivity(intent);
+
+ +

Sử dụng ý định để chỉnh sửa một sự kiện

+ +

Bạn có thể cập nhật một sự kiện trực tiếp như được mô tả trong phần Cập nhật sự kiện. Nhưng việc sử dụng Ý định {@link +android.content.Intent#ACTION_EDIT EDIT} cho phép một ứng dụng +không có quyền được chuyển giao chỉnh sửa sự kiện cho ứng dụng Lịch. +Khi người dùng hoàn thành chỉnh sửa sự kiện của mình trong Lịch, họ được trả về ứng dụng +ban đầu.

Sau đây là một ví dụ về ý định đặt một tiêu đề mới +cho một sự kiện được quy định và cho phép người dùng chỉnh sửa sự kiện trong Lịch.

+ + +
long eventID = 208;
+Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+Intent intent = new Intent(Intent.ACTION_EDIT)
+    .setData(uri)
+    .putExtra(Events.TITLE, "My New Title");
+startActivity(intent);
+ +

Sử dụng ý định để xem dữ liệu lịch

+

Trình cung cấp Lịch giới thiệu hai cách khác nhau để sử dụng Ý định {@link android.content.Intent#ACTION_VIEW VIEW}:

+
    +
  • Để mở Lịch tới một ngày cụ thể.
  • +
  • Để xem một sự kiện.
  • + +
+

Sau đây là một ví dụ cho biết cách mở Lịch tới một ngày cụ thể:

+
// A date-time specified in milliseconds since the epoch.
+long startMillis;
+...
+Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
+builder.appendPath("time");
+ContentUris.appendId(builder, startMillis);
+Intent intent = new Intent(Intent.ACTION_VIEW)
+    .setData(builder.build());
+startActivity(intent);
+ +

Sau đây là một ví dụ cho biết cách mở một sự kiện để xem:

+
long eventID = 208;
+...
+Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+Intent intent = new Intent(Intent.ACTION_VIEW)
+   .setData(uri);
+startActivity(intent);
+
+ + +

Trình điều hợp Đồng bộ

+ + +

Chỉ có vài điểm khác nhau nhỏ giữa cách một ứng dụng và cách một trình điều hợp đồng bộ +truy cập Trình cung cấp Lịch:

+ +
    +
  • Trình điều hợp đồng bộ cần chỉ định rằng nó là một trình điều hợp đồng bộ bằng cách đặt {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} thành true.
  • + + +
  • Trình điều hợp đồng bộ cần cung cấp một {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} và một {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} làm tham số truy vấn trong URI.
  • + +
  • Trình điều hợp đồng bộ có quyền truy nhập ghi vào nhiều cột hơn ứng dụng hay widget. + Ví dụ, một ứng dụng chỉ có thể sửa đổi một vài đặc điểm của một lịch, + chẳng hạn như tên lịch, tên hiển thị, thiết đặt hiển thị, và lịch có được + đồng bộ hay không. Nếu so sánh, một trình điều hợp đồng bộ có thể truy cập không chỉ những cột đó, mà còn nhiều cột khác, + chẳng hạn như màu lịch, múi giờ, mức truy nhập, địa điểm, v.v. +Tuy nhiên, trình điều hợp đồng bộ bị hạn chế đối với ACCOUNT_NAME và +ACCOUNT_TYPE mà nó quy định.
+ +

Sau đây là một phương pháp hữu ích hơn mà bạn có thể sử dụng để trả về một URI để dùng với một trình điều hợp đồng bộ:

+
 static Uri asSyncAdapter(Uri uri, String account, String accountType) {
+    return uri.buildUpon()
+        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
+        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
+        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
+ }
+
+

Để biết việc triển khai mẫu trình điều hợp đồng bộ (không liên quan cụ thể tới Lịch), hãy xem phần +SampleSyncAdapter. diff --git a/docs/html-intl/intl/vi/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/vi/guide/topics/providers/contacts-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..2fa2ed3c9bdf9ecc1e500828d93d15b032d7b45b --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=Trình cung cấp Danh bạ +@jd:body +

+
+

Xem nhanh

+
    +
  • Kho lưu giữ thông tin về con người của Android.
  • +
  • + Đồng bộ với web. +
  • +
  • + Tích hợp dữ liệu theo luồng xã hội. +
  • +
+

Trong tài liệu này

+
    +
  1. + Tổ chức Trình cung cấp Danh bạ +
  2. +
  3. + Liên lạc thô +
  4. +
  5. + Dữ liệu +
  6. +
  7. + Danh bạ +
  8. +
  9. + Dữ liệu từ Trình điều hợp Đồng bộ +
  10. +
  11. + Quyền được Yêu cầu +
  12. +
  13. + Hồ sơ Người dùng +
  14. +
  15. + Siêu dữ liệu Trình cung cấp Danh bạ +
  16. +
  17. + Truy cập Trình cung cấp Danh bạ +
  18. +
  19. +
  20. + Trình điều hợp Đồng bộ Trình cung cấp Danh bạ +
  21. +
  22. + Dữ liệu từ Luồng Xã hội +
  23. +
  24. + Các Tính năng Bổ sung của Trình cung cấp Danh bạ +
  25. +
+

Lớp khóa

+
    +
  1. {@link android.provider.ContactsContract.Contacts}
  2. +
  3. {@link android.provider.ContactsContract.RawContacts}
  4. +
  5. {@link android.provider.ContactsContract.Data}
  6. +
  7. {@code android.provider.ContactsContract.StreamItems}
  8. +
+

Các Mẫu Liên quan

+
    +
  1. + + Trình quản lý Danh bạ + +
  2. +
  3. + + Trình điều hợp Đồng bộ Mẫu +
  4. +
+

Xem thêm

+
    +
  1. + + Nội dung Cơ bản về Trình cung cấp Nội dung + +
  2. +
+
+
+

+ Trình cung cấp Danh bạ là một thành phần Android mạnh mẽ và linh hoạt có chức năng quản lý + kho dữ liệu trung tâm về con người của thiết bị. Trình cung cấp Danh bạ là nguồn của những dữ liệu + mà bạn thấy trong ứng dụng danh bạ của thiết bị, và bạn cũng có thể truy cập dữ liệu của nó trong + ứng dụng của chính mình và chuyển dữ liệu giữa thiết bị và các dịch vụ trực tuyến. Trình cung cấp chứa + đủ loại nguồn dữ liệu và cố gắng quản lý nhiều dữ liệu nhất có thể cho mỗi người, kết quả + là tổ chức của nó trở nên phức tạp. Vì điều này, API của trình cung cấp bao gồm một + tập mở rộng gồm các lớp hợp đồng và giao diện tạo điều kiện cho việc truy xuất và sửa đổi + dữ liệu. +

+

+ Hướng dẫn này trình bày những nội dung sau: +

+
    +
  • + Cấu trúc cơ bản của trình cung cấp. +
  • +
  • + Cách truy xuất dữ liệu từ trình cung cấp. +
  • +
  • + Cách sửa đổi dữ liệu trong trình cung cấp. +
  • +
  • + Cách ghi một trình điều hợp đồng bộ để đồng bộ hóa dữ liệu từ máy chủ của bạn với + Trình cung cấp Danh bạ. +
  • +
+

+ Hướng dẫn này giả sử rằng bạn biết những nội dung cơ bản về trình cung cấp nội dung Android. Để tìm hiểu thêm + về trình cung cấp nội dung Android, hãy đọc hướng dẫn + + Nội dung Cơ bản về Trình cung cấp Nội dung. Ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu + là một ví dụ về cách sử dụng một trình điều hợp đồng bộ để chuyển dữ liệu giữa Trình cung cấp + Danh bạ và ứng dụng mẫu được lưu trữ bởi Dịch vụ Web Google. +

+

Tổ chức Trình cung cấp Danh bạ

+

+ Trình cung cấp Danh bạ là một thành phần của trình cung cấp nội dung Android. Nó chứa ba loại + dữ liệu về một người, từng loại tương ứng với một bảng do trình cung cấp đưa ra, như + được minh họa trong hình 1: +

+ +

+ Hình 1. Cấu trúc bảng của Trình cung cấp Danh bạ. +

+

+ Ba bảng này thường được đề cập theo tên các lớp hợp đồng của chúng. Các lớp này + sẽ định nghĩa các hằng số cho URI nội dung, tên cột và giá trị cột được sử dụng bởi các bảng: +

+
+
+ Bảng {@link android.provider.ContactsContract.Contacts} +
+
+ Hàng thể hiện những người khác nhau dựa trên tổng hợp của các hàng liên lạc thô. +
+
+ Bảng {@link android.provider.ContactsContract.RawContacts} +
+
+ Hàng chứa một bản tổng hợp dữ liệu về một người, liên quan tới tài khoản và loại người dùng. +
+
+ Bảng {@link android.provider.ContactsContract.Data} +
+
+ Hàng chứa các thông tin chi tiết về liên lạc thô, chẳng hạn như địa chỉ e-mail hoặc số điện thoại. +
+
+

+ Các bảng khác được đại diện bởi các lớp hợp đồng trong {@link android.provider.ContactsContract} + là bảng phụ mà Trình cung cấp Danh bạ sử dụng để quản lý thao tác của nó hoặc hỗ trợ + các chức năng cụ thể trong ứng dụng danh bạ hoặc điện thoại của thiết bị. +

+

Liên lạc thô

+

+ Một liên lạc thô thể hiện dữ liệu của một người xuất phát từ một loại tài khoản và tên tài khoản + riêng. Vì Trình cung cấp Danh bạ cho phép nhiều hơn một dịch vụ trực tuyến làm nguồn + dữ liệu cho một người, Trình cung cấp Danh bạ cho phép nhiều liên lạc thô cho cùng một người. + Nhiều liên lạc thô cũng cho phép người dùng kết hợp dữ liệu của một người từ nhiều hơn một tài khoản + từ cùng loại tài khoản. +

+

+ Hầu hết dữ liệu của một liên lạc thô không được lưu giữ trong bảng + {@link android.provider.ContactsContract.RawContacts}. Thay vào đó, nó được lưu giữ trong một hoặc nhiều + hàng trong bảng {@link android.provider.ContactsContract.Data}. Mỗi hàng dữ liệu có một cột + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID} chứa + giá trị {@code android.provider.BaseColumns#_ID RawContacts._ID} của + hàng {@link android.provider.ContactsContract.RawContacts} mẹ của nó. +

+

Các cột liên lạc thô quan trọng

+

+ Các cột quan trọng trong bảng {@link android.provider.ContactsContract.RawContacts} được + liệt kê trong bảng 1. Hãy đọc các lưu ý theo sau bảng dưới đây: +

+

+ Bảng 1. Các cột liên lạc thô quan trọng. +

+ + + + + + + + + + + + + + + + + + + + +
Tên cộtSử dụngLưu ý
+ {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + + Tên tài khoản cho loại tài khoản là nguồn của liên lạc thô này. + Ví dụ, tên tài khoản của một tài khoản Google là một trong các địa chỉ Gmail + của chủ sở hữu thiết bị. Xem mục nhập tiếp theo cho + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} để biết thêm + thông tin. + + Định dạng của tên này áp dụng theo loại tài khoản của nó. Đó không nhất thiết + là một địa chỉ e-mail. +
+ {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + + Loại tài khoản là nguồn của liên lạc thô này. Ví dụ, loại tài khoản + của một tài khoản Google là com.google. Luôn xác định loại tài khoản của bạn + bằng một mã định danh miền cho một miền mà bạn sở hữu hoặc kiểm soát. Điều này đảm bảo rằng loại tài khoản + của bạn là duy nhất. + + Một loại tài khoản cung cấp dữ liệu danh bạ thường có một trình điều hợp đồng bộ liên kết để + đồng bộ hoá với Trình điều hợp Đồng bộ. +
+ {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + + Cờ "đã xóa" cho một liên lạc thô. + + Cờ này cho phép Trình cung cấp Danh bạ duy trì hàng bên trong tới khi trình điều hợp đồng bộ + có thể xóa hàng đó khỏi máy chủ của chúng rồi cuối cùng là xóa hàng + khỏi kho lưu giữ. +
+

Lưu ý

+

+ Sau đây là các ghi chú quan trọng về bảng + {@link android.provider.ContactsContract.RawContacts}: +

+
    +
  • + Tên của liên lạc thô không được lưu giữ trong hàng của nó trong + {@link android.provider.ContactsContract.RawContacts}. Thay vào đó, nó được lưu giữ trong + bảng {@link android.provider.ContactsContract.Data}, trong một hàng + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}. Liên lạc thô + chỉ có một hàng thuộc loại này trong bảng {@link android.provider.ContactsContract.Data}. +
  • +
  • + Chú ý: Để sử dụng dữ liệu tài khoản của chính bạn trong một hàng liên lạc thô, trước tiên dữ liệu + phải được đăng ký với {@link android.accounts.AccountManager}. Để làm điều này, hãy nhắc + người dùng thêm loại tài khoản và tên tài khoản của chúng vào danh sách tài khoản. Nếu bạn không + làm vậy, Trình cung cấp Danh bạ sẽ tự động xóa hàng liên lạc thô của bạn. +

    + Ví dụ, nếu bạn muốn ứng dụng của mình duy trì dữ liệu danh bạ cho dịch vụ dựa trên nền web của mình + với miền {@code com.example.dataservice}, và tài khoản của người dùng cho dịch vụ của bạn + là {@code becky.sharp@dataservice.example.com}, trước tiên, người dùng phải thêm + "loại" tài khoản ({@code com.example.dataservice}) và "tên" tài khoản + ({@code becky.smart@dataservice.example.com}) trước khi ứng dụng của bạn có thể thêm hàng liên lạc thô. + Bạn có thể giải thích yêu cầu này với người dùng bằng tài liệu, hoặc bạn có thể nhắc + người dùng thêm loại và tên này, hoặc cả hai. Loại tài khoản và tên tài khoản + được trình bày chi tiết hơn trong phần sau. +

  • +
+

Các nguồn dữ liệu liên lạc thô

+

+ Để hiểu cách hoạt động của liên lạc thô, hãy xét người dùng "Emily Dickinson", cô ta có ba tài khoản + người dùng sau được xác định trên thiết bị của mình: +

+
    +
  • emily.dickinson@gmail.com
  • +
  • emilyd@gmail.com
  • +
  • Tài khoản Twitter "belle_of_amherst"
  • +
+

+ Người dùng này đã kích hoạt Đồng bộ Danh bạ cho cả ba tài khoản này trong cài đặt + Tài khoản. +

+

+ Giả sử Emily Dickinson mở một cửa sổ trình duyệt, đăng nhập vào Gmail bằng tài khoản + emily.dickinson@gmail.com, mở + Danh bạ, và thêm "Thomas Higginson". Sau đó, cô đăng nhập vào Gmail bằng tài khoản + emilyd@gmail.com và gửi một e-mail tới "Thomas Higginson", làm vậy sẽ tự động + thêm người này làm một liên lạc. Cô ấy cũng theo dõi "colonel_tom" (ID Twitter của Thomas Higginson) trên + Twitter. +

+

+ Trình cung cấp Danh bạ sẽ tạo ba liên lạc thô do kết quả của việc làm này: +

+
    +
  1. + Một liên lạc thô cho "Thomas Higginson" liên kết với emily.dickinson@gmail.com. + Loại tài khoản người dùng là Google. +
  2. +
  3. + Một liên lạc thô thứ hai cho "Thomas Higginson" liên kết với emilyd@gmail.com. + Loại tài khoản người dùng cũng là Google. Có một liên lạc thô thứ hai ngay cả khi + tên giống với một tên trước đó, vì người này đã được thêm cho một + tài khoản người dùng khác. +
  4. +
  5. + Một liên lạc thô thứ ba cho "Thomas Higginson" liên kết với "belle_of_amherst". Loại tài khoản người dùng + là Twitter. +
  6. +
+

Dữ liệu

+

+ Như đề cập trước đó, dữ liệu của một liên lạc thô được lưu giữ trong một hàng + {@link android.provider.ContactsContract.Data} được liên kết với giá trị + _ID của liên lạc thô. Điều này cho phép một liên lạc thô có nhiều thực thể cùng loại + dữ liệu chẳng hạn như địa chỉ e-mail hay số điện thoại. Ví dụ, nếu + "Thomas Higginson" của {@code emilyd@gmail.com} (hàng liên lạc thô cho Thomas Higginson + liên kết với tài khoản Google emilyd@gmail.com) có một địa chỉ e-mail nhà là + thigg@gmail.com và một địa chỉ e-mail cơ quan là + thomas.higginson@gmail.com, Trình cung cấp Danh bạ sẽ lưu trữ hai hàng địa chỉ e-mail đó + và liên kết cả hai với liên lạc thô. +

+

+ Để ý rằng các loại dữ liệu khác nhau được lưu giữ trong một bảng này. Các hàng tên hiển thị, + số điện thoại, e-mail, địa chỉ gửi thư, ảnh và chi tiết trang web đều được tìm thấy trong bảng + {@link android.provider.ContactsContract.Data}. Để giúp quản lý điều này, bảng + {@link android.provider.ContactsContract.Data} có một số cột có tên mô tả, + và các cột còn lại có tên chung. Các nội dung của cột tên mô tả có cùng ý nghĩa + không phụ thuộc vào loại dữ liệu trong hàng, trong khi nội dung của cột tên chung có + ý nghĩa khác nhau tùy vào loại dữ liệu. +

+

Tên cột mô tả

+

+ Một số ví dụ về tên cột mô tả là: +

+
+
+ {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} +
+
+ Giá trị của cột _ID của liên lạc thô đối với dữ liệu này. +
+
+ {@link android.provider.ContactsContract.Data#MIMETYPE} +
+
+ Loại dữ liệu được lưu giữ trong hàng này, được thể hiện dưới dạng một kiểu MIME tùy chỉnh. Trình cung cấp Danh bạ + sử dụng các kiểu MIME được định nghĩa trong lớp con của + {@link android.provider.ContactsContract.CommonDataKinds}. Các kiểu MIME là nguồn mở, + và có thể được sử dụng bởi bất kỳ ứng dụng hay trình điều hợp đồng bộ nào hoạt động với Trình cung cấp Danh bạ. +
+
+ {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} +
+
+ Nếu kiểu hàng dữ liệu này có thể xảy ra nhiều hơn một lần đối với một liên lạc thô, cột + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} sẽ gắn cờ + hàng dữ liệu chứa dữ liệu sơ cấp cho kiểu đó. Ví dụ, nếu + người dùng nhấn giữ một số điện thoại cho một liên lạc và chọn Đặt mặc định, + khi đó hàng {@link android.provider.ContactsContract.Data} chứa số đó + có cột tương ứng {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} được đặt thành một + giá trị khác 0. +
+
+

Tên cột chung

+

+ Có 15 cột chung được đặt tên DATA1 thông qua + DATA15 thường có sẵn và thêm bốn cột + chung SYNC1 thông qua SYNC4 mà chỉ được sử dụng bởi trình điều hợp + đồng bộ. Các hằng số tên cột chung luôn có tác dụng, không phụ thuộc vào loại + dữ liệu mà hàng đó chứa. +

+

+ Cột DATA1 được đánh chỉ mục. Trình cung cấp Danh bạ luôn sử dụng cột này cho + dữ liệu mà trình cung cấp kỳ vọng sẽ là đối tượng truy vấn thường xuyên nhất. Ví dụ, + trong một hàng e-mail, cột này chứa địa chỉ e-mail thực sự. +

+

+ Theo quy ước, cột DATA15 được dành để lưu giữ dữ liệu Binary Large Object + (BLOB) chẳng hạn như hình thu nhỏ của ảnh. +

+

Tên cột theo kiểu

+

+ Để tạo điều kiện làm việc với các cột đối với một kiểu hàng cụ thể, Trình cung cấp Danh bạ + cũng cung cấp các hằng số tên cột theo kiểu, được định nghĩa trong các lớp con của + {@link android.provider.ContactsContract.CommonDataKinds}. Các hằng số chỉ cấp một + tên hằng số khác cho cùng tên cột, điều này giúp bạn truy cập dữ liệu trong một hàng thuộc + một kiểu cụ thể. +

+

+ Ví dụ, lớp {@link android.provider.ContactsContract.CommonDataKinds.Email} định nghĩa + các hằng số tên cột theo kiểu cho một hàng {@link android.provider.ContactsContract.Data} mà + có kiểu MIME + {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE}. Lớp chứa hằng số + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} cho cột + địa chỉ e-mail. Giá trị thực sự của + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} là "data1", giá trị này + giống hệt như tên chung của cột. +

+

+ Chú ý: Không được thêm dữ liệu tùy chỉnh của chính bạn vào bảng + {@link android.provider.ContactsContract.Data} bằng cách sử dụng một hàng có một trong các kiểu MIME được xác định trước + của trình cung cấp. Nếu làm vậy, bạn có thể làm mất dữ liệu hoặc khiến trình cung cấp + gặp trục trặc. Ví dụ, bạn không nên thêm một hàng có kiểu MIME + {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE} mà chứa tên người dùng thay vì địa chỉ e-mail trong cột + DATA1. Nếu sử dụng kiểu MIME tùy chỉnh của mình cho hàng, khi đó bạn được tự do + định nghĩa tên cột theo kiểu của chính mình và sử dụng các cột theo cách bạn muốn. +

+

+ Hình 2 minh họa cách các cột mô tả và cột dữ liệu xuất hiện trong hàng + {@link android.provider.ContactsContract.Data}, và cách mà tên cột theo kiểu "phủ lên" + tên cột chung +

+How type-specific column names map to generic column names +

+ Hình 2. Tên cột theo kiểu và tên cột chung. +

+

Lớp tên cột theo kiểu

+

+ Bảng 2 liệt kê các lớp tên cột theo kiểu thường được sử dụng nhất: +

+

+ Bảng 2. Lớp tên cột theo kiểu

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Lớp ánh xạKiểu dữ liệuLưu ý
{@link android.provider.ContactsContract.CommonDataKinds.StructuredName}Dữ liệu tên của liên lạc thô liên kết với hàng dữ liệu này.Một liên lạc thô chỉ có một trong những hàng này.
{@link android.provider.ContactsContract.CommonDataKinds.Photo}Ảnh chính của liên lạc thô được liên kết với hàng dữ liệu này.Một liên lạc thô chỉ có một trong những hàng này.
{@link android.provider.ContactsContract.CommonDataKinds.Email}Địa chỉ e-mail của liên lạc thô được liên kết với hàng dữ liệu này.Một liên lạc thô có thể có nhiều địa chỉ e-mail.
{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}Địa chỉ cổng của liên lạc thô được liên kết với hàng dữ liệu này.Một liên lạc thô có thể có nhiều địa chỉ cổng.
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}Mã định danh liên kết liên lạc thô với một trong các nhóm trong Trình cung cấp Danh bạ. + Nhóm là một tính năng tùy chọn của loại tài khoản và tên tài khoản. Chúng được mô tả + chi tiết hơn trong phần Nhóm liên lạc. +
+

Danh bạ

+

+ Trình cung cấp Danh bạ kết hợp các hàng liên lạc thô giữa tất cả các loại tài khoản và tên tài khoản + để tạo thành một liên lạc. Điều này tạo điều kiện để hiển thị và sửa đổi tất cả dữ liệu mà một + người dùng đã thu thập cho một người. Trình cung cấp Danh bạ quản lý việc tạo các hàng liên lạc mới + và tổng hợp các liên lạc thô với hàng liên lạc hiện có. Ứng dụng lẫn + trình điều hợp đồng bộ đều không được cho phép thêm liên lạc và một số cột trong một hàng liên lạc là cột chỉ đọc. +

+

+ Lưu ý: Nếu bạn cố gắng thêm một liên lạc vào Trình cung cấp Danh bạ có một + {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()}, bạn sẽ gặp + lỗi ngoại lệ {@link java.lang.UnsupportedOperationException}. Nếu bạn cố gắng cập nhật một cột + mà được liệt kê là "chỉ đọc," cập nhật sẽ bị bỏ qua. +

+

+ Trình cung cấp Danh bạ tạo một liên lạc mới để hồi đáp lại việc thêm một liên lạc thô mới + không khớp với bất kỳ liên lạc nào hiện có. Trình cung cấp cũng làm vậy nếu dữ liệu + của một liên lạc thô hiện có thay đổi sao cho nó không còn khớp với liên lạc mà trước đó + nó được gắn với. Nếu một ứng dụng hoặc trình điều hợp đồng bộ tạo một liên lạc thô mới mà + khớp với một liên lạc hiện tại, liên lạc thô mới sẽ được tổng hợp vào liên lạc + hiện có. +

+

+ Trình cung cấp Danh bạ sẽ liên kết một hàng liên lạc với các hàng liên lạc thô của nó bằng cột + _ID của hàng liên lạc đó trong bảng {@link android.provider.ContactsContract.Contacts Contacts} +. Cột CONTACT_ID của bảng liên lạc thô + {@link android.provider.ContactsContract.RawContacts} chứa các giá trị _ID cho + các hàng liên lạc liên kết với từng hàng liên lạc thô. +

+

+ Bảng {@link android.provider.ContactsContract.Contacts} cũng có cột + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} mà là một liên kết + "cố định" với hàng liên lạc đó. Vì Trình cung cấp Danh bạ tự động duy trì + các liên lạc, nó có thể thay đổi giá trị {@code android.provider.BaseColumns#_ID} của một hàng liên lạc + hồi đáp lại một sự tổng hợp hoặc đồng bộ. Ngay cả khi điều này xảy ra, URI nội dung + {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} kết hợp với + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} của liên lạc sẽ vẫn + chỉ về hàng liên lạc đó, vì thế bạn có thể sử dụng + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} + để duy trì các liên kết đến liên lạc "yêu thích", v.v. Cột này có định dạng riêng không + liên quan tới định dạng của cột {@code android.provider.BaseColumns#_ID}. +

+

+ Hình 3 minh họa mối liên quan giữa ba bảng chính này với nhau. +

+Contacts provider main tables +

+ Hình 3. Mối quan hệ giữa các bảng Danh bạ, Liên lạc Thô, và Chi tiết. +

+

Dữ liệu từ Trình điều hợp Đồng bộ

+

+ Người dùng nhập dữ liệu danh bạ trực tiếp vào thiết bị, nhưng dữ liệu cũng đi đến Trình cung cấp + Danh bạ từ các dịch vụ web thông qua trình điều hợp đồng bộ, giúp tự động + chuyển dữ liệu giữa thiết bị và các dịch vụ. Trình điều hợp đồng bộ chạy ngầm + dưới sự kiểm soát của hệ thống, và chúng gọi các phương pháp {@link android.content.ContentResolver} để + quản lý dữ liệu. +

+

+ Trong Android, dịch vụ web mà một trình điều hợp đồng bộ làm việc cùng sẽ được xác định bằng một loại tài khoản. + Mỗi trình điều hợp đồng bộ làm việc với một loại tài khoản, nhưng nó có thể hỗ trợ nhiều tên tài khoản cho + loại đó. Các loại tài khoản và tên tài khoản được mô tả sơ qua trong phần + Các nguồn dữ liệu liên lạc thô. Các định nghĩa sau trình bày + chi tiết hơn và mô tả mối liên quan giữa loại và tên tài khoản với các trình điều hợp đồng bộ và dịch vụ. +

+
+
+ Loại tài khoản +
+
+ Xác định một dịch vụ mà người dùng đã lưu giữ dữ liệu trong đó. Trong phần lớn thời gian, người dùng phải + xác thực dịch vụ. Ví dụ, Google Contacts là một loại tài khoản được xác định + bởi mã google.com. Giá trị này tương ứng với loại tài khoản được sử dụng bởi + {@link android.accounts.AccountManager}. +
+
+ Tên tài khoản +
+
+ Xác định một tài khoản hoặc đăng nhập cụ thể cho một loại tài khoản. Tài khoản Google Contacts + giống như tài khoản Google, chúng có một địa chỉ e-mail làm tên tài khoản. + Các dịch vụ khác có thể sử dụng tên người dùng là một từ hoặc id chữ số. +
+
+

+ Loại tài khoản không nhất thiết phải duy nhất. Một người dùng có thể cấu hình nhiều tài khoản Google Contacts + và tải xuống dữ liệu của chúng vào Trình cung cấp Danh bạ; điều này có thể xảy ra nếu người dùng có một tập hợp + các liên lạc cá nhân cho một tên tài khoản cá nhân, và một tập hợp khác cho cơ quan. Tên tài khoản thường + là duy nhất. Cùng nhau, chúng xác định một dòng dữ liệu cụ thể giữa Trình cung cấp Danh bạ và + một dịch vụ bên ngoài. +

+

+ Nếu muốn chuyển dữ liệu từ dịch vụ của bạn sang Trình cung cấp Danh bạ, bạn cần ghi + vào trình điều hợp đồng bộ của chính mình. Điều này được mô tả chi tiết hơn trong phần + Trình điều hợp Đồng bộ Trình cung cấp Danh bạ. +

+

+ Hình 4 minh họa cách mà Trình cung cấp Danh bạ phù hợp với dòng dữ liệu + về con người. Trong hộp được đánh dấu "trình điều hợp đồng bộ," mỗi trình điều hợp được ghi nhãn theo loại tài khoản của nó. +

+Flow of data about people +

+ Hình 4. Luồng dữ liệu của Trình cung cấp Danh bạ. +

+

Quyền được Yêu cầu

+

+ Những ứng dụng muốn truy cập Trình cung cấp Danh bạ phải yêu cầu các quyền + sau: +

+
+
Quyền truy cập đọc vào một hoặc nhiều bảng
+
+ {@link android.Manifest.permission#READ_CONTACTS}, được quy định trong + AndroidManifest.xml với phần tử + + <uses-permission> là + <uses-permission android:name="android.permission.READ_CONTACTS">. +
+
Quyền truy cập ghi vào một hoặc nhiều bảng
+
+ {@link android.Manifest.permission#WRITE_CONTACTS}, được quy định trong + AndroidManifest.xml với phần tử + + <uses-permission> là + <uses-permission android:name="android.permission.WRITE_CONTACTS">. +
+
+

+ Những quyền này không mở rộng sang dữ liệu hồ sơ người dùng. Hồ sơ người dùng và các quyền + được yêu cầu được đề cập trong phần sau, + Hồ sơ Người dùng. +

+

+ Nhớ rằng dữ liệu danh bạ của người dùng là dữ liệu cá nhân và nhạy cảm. Người dùng quan tâm về + quyền riêng tư của họ, vì thế họ không muốn các ứng dụng thu thập dữ liệu về mình hoặc danh bạ của mình. + Nếu không rõ ràng về lý do bạn cần quyền truy cập dữ liệu danh bạ của họ, họ có thể cho + ứng dụng của bạn đánh giá thấp hoặc từ chối cài đặt ứng dụng. +

+

Hồ sơ Người dùng

+

+ Bảng {@link android.provider.ContactsContract.Contacts} có một hàng đơn chứa + dữ liệu hồ sơ cho người dùng của thiết bị. Dữ liệu này mô tả user của thiết bị chứ không phải + của một trong các liên lạc của người dùng. Hàng liên lạc hồ sơ được liên kết với hàng + liên lạc thô đối với từng hệ thống sử dụng hồ sơ. + Mỗi hàng liên lạc thô của hồ sơ có thể có nhiều hàng dữ liệu. Các hằng số để truy cập hồ sơ + người dùng có sẵn trong lớp {@link android.provider.ContactsContract.Profile}. +

+

+ Truy cập hồ sơ người dùng đòi hỏi phải có các quyền đặc biệt. Ngoài các quyền + {@link android.Manifest.permission#READ_CONTACTS} và + {@link android.Manifest.permission#WRITE_CONTACTS} cần để đọc và ghi, truy cập + hồ sơ người dùng còn yêu cầu quyền {@code android.Manifest.permission#READ_PROFILE} và + {@code android.Manifest.permission#WRITE_PROFILE} tương ứng cho quyền truy cập đọc và + ghi. +

+

+ Nhớ rằng bạn nên coi hồ sơ của một người dùng là nội dung nhạy cảm. Quyền + {@code android.Manifest.permission#READ_PROFILE} cho phép bạn truy cập dữ liệu xác định cá nhân + của người dùng thiết bị. Chắc chắn phải nói cho người dùng biết lý do tại sao + bạn cần các quyền truy cập hồ sơ người dùng trong phần mô tả ứng dụng của mình. +

+

+ Để truy xuất hàng liên lạc chứa hồ sơ của người dùng, + hãy gọi {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) + ContentResolver.query()}. Đặt URI nội dung thành + {@link android.provider.ContactsContract.Profile#CONTENT_URI} và không cung cấp bất kỳ + tiêu chí lựa chọn nào. Bạn cũng có thể sử dụng URI nội dung này làm URI cơ sở để truy xuất các liên lạc thô + hoặc dữ liệu cho hồ sơ. Ví dụ, đoạn mã HTML này truy xuất dữ liệu cho hồ sơ: +

+
+// Sets the columns to retrieve for the user profile
+mProjection = new String[]
+    {
+        Profile._ID,
+        Profile.DISPLAY_NAME_PRIMARY,
+        Profile.LOOKUP_KEY,
+        Profile.PHOTO_THUMBNAIL_URI
+    };
+
+// Retrieves the profile from the Contacts Provider
+mProfileCursor =
+        getContentResolver().query(
+                Profile.CONTENT_URI,
+                mProjection ,
+                null,
+                null,
+                null);
+
+

+ Lưu ý: Nếu bạn truy xuất nhiều hàng liên lạc và muốn xác định xem một trong số chúng có phải + là hồ sơ người dùng không, hãy kiểm tra cột + {@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} của hàng. Cột này + được đặt thành "1" nếu liên lạc là hồ sơ người dùng. +

+

Siêu dữ liệu Trình cung cấp Danh bạ

+

+ Trình cung cấp Danh bạ quản lý dữ liệu theo dõi trạng thái của dữ liệu danh bạ trong + kho lưu giữ. Siêu dữ liệu về kho lưu giữ này được lưu giữ ở nhiều nơi khác nhau, bao gồm + các hàng bảng Liên lạc Thô, Dữ liệu, và Danh bạ, bảng + {@link android.provider.ContactsContract.Settings}, và bảng + {@link android.provider.ContactsContract.SyncState}. Bảng sau đây cho biết + ảnh hưởng của từng mục trong siêu dữ liệu này: +

+

+ Bảng 3. Siêu dữ liệu trong Trình cung cấp Danh bạ

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BảngCộtGiá trịÝ nghĩa
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#DIRTY}"0" - không thay đổi kể từ lần đồng bộ cuối cùng. + Đánh dấu các liên lạc thô đã được thay đổi trên thiết bị và phải được đồng bộ trở lại + máy chủ. Giá trị được đặt tự động bởi Trình cung cấp Danh bạ khi các ứng dụng + Android cập nhật một hàng. +

+ Các trình điều hợp đồng bộ sửa đổi bảng liên lạc thô hoặc dữ liệu nên luôn nối + xâu {@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER} với + URI nội dung mà chúng sử dụng. Làm vậy sẽ ngăn không cho trình cung cấp đánh dấu hàng là không tốt. + Nếu không, các sửa đổi trình điều hợp đồng bộ xem như sửa đổi cục bộ và được + gửi tới máy chủ, ngay cả khi máy chủ là nguồn sửa đổi. +

+
"1" - đã thay đổi kể từ lần đồng bộ cuối cùng, cần được đồng bộ lại máy chủ.
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#VERSION}Số phiên bản của hàng này. + Trình cung cấp Danh bạ tự động tăng dần giá trị này bất cứ khi nào hàng hoặc + dữ liệu có liên quan của hàng thay đổi. +
{@link android.provider.ContactsContract.Data}{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}Số phiên bản của hàng này. + Trình cung cấp Danh bạ tự động tăng dần giá trị này bất cứ khi nào hàng dữ liệu + bị thay đổi. +
{@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} + Một xâu giá trị xác định duy nhất liên lạc thô này cho tài khoản mà + nó được tạo trong đó. + + Khi một trình điều hợp đồng bộ tạo một liên lạc thô mới, cột này nên được đặt thành + ID duy nhất của máy chủ dành cho liên lạc thô đó. Khi một ứng dụng Android tạo một liên lạc thô + mới, ứng dụng đó sẽ để trống cột này. Điều này báo hiệu với trình điều hợp + đồng bộ rằng nó nên tạo một liên lạc thô mới trên máy chủ, và lấy một + giá trị cho {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID}. +

+ Cụ thể, id nguồn phải là duy nhất đối với từng loại tài khoản + và nên ổn định giữa các lần đồng bộ: +

+
    +
  • + Duy nhất: Mỗi liên lạc thô đối với một tài khoản phải có id nguồn riêng của mình. Nếu không + thi hành điều này, bạn sẽ gây ra sự cố trong ứng dụng danh bạ. + Để ý rằng hai liên lạc thô đối với cùng loại tài khoản có thể có + cùng id nguồn. Ví dụ, liên lạc thô "Thomas Higginson" đối với + tài khoản {@code emily.dickinson@gmail.com} được cho phép có cùng id nguồn + như liên lạc thô "Thomas Higginson" đối với tài khoản + {@code emilyd@gmail.com}. +
  • +
  • + Ổn định: Id nguồn là một bộ phận cố định của dữ liệu từ dịch vụ trực tuyến đối với + liên lạc thô. Ví dụ, nếu người dùng xóa Lưu trữ Danh bạ khỏi + cài đặt Ứng dụng và đồng bộ lại, các liên lạc thô được khôi phục sẽ có cùng + id nguồn như trước. Nếu bạn không thi hành điều này, các lối tắt sẽ dừng + hoạt động. +
  • +
+
{@link android.provider.ContactsContract.Groups}{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}"0" - Các liên lạc trong nhóm này không nên được hiển thị trong UI ứng dụng Android. + Cột này dành cho tính tương thích với các máy chủ mà cho phép người dùng ẩn các liên lạc trong + một số nhóm. +
"1" - Các liên lạc trong nhóm này được cho phép hiển thị trong UI ứng dụng.
{@link android.provider.ContactsContract.Settings} + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} + "0" - Đối với tài khoản và loại tài khoản này, những liên lạc không thuộc về nhóm + được ẩn đối với UI ứng dụng Android. + + Theo mặc định, các liên lạc được hiển thị nếu không có liên lạc thô nào của chúng thuộc về một nhóm + (Tư cách thành viên nhóm đối với một liên lạc thô được thể hiện bằng một hoặc nhiều hàng + {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} trong + bảng {@link android.provider.ContactsContract.Data}). + Bằng cách đặt cờ này trong hàng bảng {@link android.provider.ContactsContract.Settings} đối với + một loại tài khoản và tài khoản, bạn có thể buộc những liên lạc không có nhóm phải hiển thị. + Một công dụng của cờ này đó là để hiển thị liên lạc từ các máy chủ không sử dụng nhóm. +
+ "1" - Đối với tài khoản và loại tài khoản này, những liên lạc không thuộc về nhóm + sẽ được hiển thị đối với UI ứng dụng. +
{@link android.provider.ContactsContract.SyncState}(tất cả) + Sử dụng bảng này để lưu giữ siêu dữ liệu cho trình điều hợp đồng bộ của bạn. + + Với bảng này, bạn có thể lưu giữ trạng thái đồng bộ và các dữ liệu khác liên quan tới đồng bộ một cách lâu dài + trên thiết bị. +
+

Truy cập Trình cung cấp Danh bạ

+

+ Phần này mô tả các hướng dẫn về truy cập dữ liệu từ Trình cung cấp Danh bạ, tập trung vào những + nội dung sau: +

+
    +
  • + Truy vấn thực thể. +
  • +
  • + Sửa đổi hàng loạt. +
  • +
  • + Truy xuất và sửa đổi bằng ý định. +
  • +
  • + Toàn vẹn dữ liệu. +
  • +
+

+ Thực hiện sửa đổi từ một trình điều hợp đồng bộ cũng được đề cập chi tiết hơn trong phần + Trình điều hợp Đồng bộ Trình cung cấp Danh bạ. +

+

Truy vấn thực thể

+

+ Vì các bảng của Trình cung cấp Danh bạ được tổ chức theo một phân cấp, thường sẽ hữu ích nếu + truy xuất một hàng và tất cả hàng "con" được liên kết với nó. Ví dụ, để hiển thị + tất cả thông tin cho một người, bạn có thể muốn truy xuất tất cả hàng + {@link android.provider.ContactsContract.RawContacts} đối với một hàng + {@link android.provider.ContactsContract.Contacts} đơn, hoặc tất cả hàng + {@link android.provider.ContactsContract.CommonDataKinds.Email} đối với một hàng + {@link android.provider.ContactsContract.RawContacts} đơn. Để tạo điều kiện cho điều này, Trình cung cấp + Danh bạ sẽ cung cấp các cấu trúc thực thể đóng vai trò như liên kết cơ sở dữ liệu + giữa các bảng. +

+

+ Thực thể giống như một bảng bao gồm các cột được chọn từ một bảng mẹ và bảng con của nó. + Khi bạn truy vấn một thực thể, bạn cung cấp một dự thảo và các tiêu chí dựa trên các cột + có sẵn từ thực thể. Kết quả là một {@link android.database.Cursor} trong đó chứa + một hàng cho từng hàng bảng con được truy xuất. Ví dụ, nếu bạn truy vấn + {@link android.provider.ContactsContract.Contacts.Entity} cho một tên liên lạc + và tất cả hàng {@link android.provider.ContactsContract.CommonDataKinds.Email} đối với tất cả + liên lạc thô cho tên đó, bạn sẽ nhận lại một {@link android.database.Cursor} chứa một hàng + cho mỗi hàng {@link android.provider.ContactsContract.CommonDataKinds.Email}. +

+

+ Các thực thể sẽ đơn giản hóa việc truy vấn. Bằng cách sử dụng một thực thể, bạn có thể truy xuất ngay lập tức tất cả dữ liệu danh bạ cho một + liên lạc hoặc liên lạc thô, thay vì phải truy vấn bảng mẹ trước để nhận một + ID, và rồi phải truy xuất bảng con bằng ID đó. Đồng thời, Trình cung cấp Danh bạ xử lý + một truy vấn đối với một thực thể trong một giao tác đơn, điều này đảm bảo rằng dữ liệu được truy xuất sẽ được + nhất quán trong nội bộ. +

+

+ Lưu ý: Một thực thể thường không chứa tất cả cột của bảng mẹ và + bảng con. Nếu bạn cố gắng làm việc với một tên cột không có trong danh sách các hằng số tên cột + đối với thực thể đó, bạn sẽ nhận được một {@link java.lang.Exception}. +

+

+ Đoạn mã HTML sau cho biết cách truy xuất tất cả hàng liên lạc thô cho một liên lạc. Đoạn mã HTML + là bộ phận của một ứng dụng lớn hơn có hai hoạt động, "chính" và "chi tiết". Hoạt động chính + hiển thị một danh sách các hàng liên lạc; khi người dùng chọn một hàng, hoạt động sẽ gửi ID của hàng tới hoạt động + chi tiết. Hoạt động chi tiết sử dụng {@link android.provider.ContactsContract.Contacts.Entity} + để hiển thị tất cả hàng dữ liệu từ tất cả liên lạc thô được liên kết với liên lạc + đã chọn. +

+

+ Đoạn mã HTML này được lấy từ hoạt động "chi tiết": +

+
+...
+    /*
+     * Appends the entity path to the URI. In the case of the Contacts Provider, the
+     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
+     */
+    mContactUri = Uri.withAppendedPath(
+            mContactUri,
+            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
+
+    // Initializes the loader identified by LOADER_ID.
+    getLoaderManager().initLoader(
+            LOADER_ID,  // The identifier of the loader to initialize
+            null,       // Arguments for the loader (in this case, none)
+            this);      // The context of the activity
+
+    // Creates a new cursor adapter to attach to the list view
+    mCursorAdapter = new SimpleCursorAdapter(
+            this,                        // the context of the activity
+            R.layout.detail_list_item,   // the view item containing the detail widgets
+            mCursor,                     // the backing cursor
+            mFromColumns,                // the columns in the cursor that provide the data
+            mToViews,                    // the views in the view item that display the data
+            0);                          // flags
+
+    // Sets the ListView's backing adapter.
+    mRawContactList.setAdapter(mCursorAdapter);
+...
+@Override
+public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+
+    /*
+     * Sets the columns to retrieve.
+     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
+     * DATA1 contains the first column in the data row (usually the most important one).
+     * MIMETYPE indicates the type of data in the data row.
+     */
+    String[] projection =
+        {
+            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
+            ContactsContract.Contacts.Entity.DATA1,
+            ContactsContract.Contacts.Entity.MIMETYPE
+        };
+
+    /*
+     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
+     * contact collated together.
+     */
+    String sortOrder =
+            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
+            " ASC";
+
+    /*
+     * Returns a new CursorLoader. The arguments are similar to
+     * ContentResolver.query(), except for the Context argument, which supplies the location of
+     * the ContentResolver to use.
+     */
+    return new CursorLoader(
+            getApplicationContext(),  // The activity's context
+            mContactUri,              // The entity content URI for a single contact
+            projection,               // The columns to retrieve
+            null,                     // Retrieve all the raw contacts and their data rows.
+            null,                     //
+            sortOrder);               // Sort by the raw contact ID.
+}
+
+

+ Khi hoàn thành việc tải, {@link android.app.LoaderManager} gọi ra một lệnh gọi lại đến + {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) + onLoadFinished()}. Một trong các tham đối đến với phương pháp này là một + {@link android.database.Cursor} với các kết quả của truy vấn. Trong ứng dụng của chính mình, bạn có thể nhận dữ liệu + từ {@link android.database.Cursor} này để hiển thị nó hoặc thao tác thêm với nó. +

+

Sửa đổi hàng loạt

+

+ Bất cứ khi nào có thể, bạn nên chèn, cập nhật và xóa dữ liệu trong Trình cung cấp Danh bạ trong + "chế độ hàng loạt", bằng cách tạo một {@link java.util.ArrayList} của các đối tượng + {@link android.content.ContentProviderOperation} và gọi + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Vì + Trình cung cấp Danh bạ thực hiện tất cả thao tác trong một + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} trong một giao tác + đơn, các sửa đổi của bạn sẽ không bao giờ ra khỏi kho lưu giữ danh bạ một cách + không nhất quán. Sửa đổi hàng loạt cũng tạo điều kiện cho việc chèn một liên lạc thô và dữ liệu chi tiết của liên lạc + tại cùng thời điểm. +

+

+ Lưu ý: Để sửa đổi một liên lạc thô đơn, hãy xét gửi một ý định tới + ứng dụng danh bạ của thiết bị thay vì xử lý sửa đổi trong ứng dụng của bạn. + Việc làm này được mô tả chi tiết hơn trong phần + Truy xuất và sửa đổi bằng ý định. +

+

Điểm kết quả

+

+ Sửa đổi hàng loạt chứa nhiều thao tác có thể chặn các tiến trình khác, + dẫn đến trải nghiệm người dùng tổng thể không tốt. Để sắp xếp tổ chức tất cả sửa đổi mà bạn muốn + thực hiện trong ít danh sách riêng nhất có thể, và đồng thời ngăn chúng + chặn hệ thống, bạn nên đặt các điểm kết quả cho một hoặc nhiều thao tác. + Điểm kết quả là một đối tượng {@link android.content.ContentProviderOperation} có giá trị + {@link android.content.ContentProviderOperation#isYieldAllowed()} được đặt thành + true. Khi các Trình cung cấp Danh bạ gặp phải một điểm kết quả, nó tạm dừng công việc để + cho phép các tiến trình khác chạy và đóng giao tác hiện tại. Khi trình cung cấp bắt đầu lại, nó + tiếp tục với thao tác tiếp theo trong {@link java.util.ArrayList} và bắt đầu một giao tác + mới. +

+

+ Điểm kết quả dẫn đến có nhiều hơn một giao tác trên mỗi lệnh gọi tới + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Vì + điều này, bạn nên đặt một điểm kết quả cho thao tác cuối cùng đối với một tập hợp các hàng có liên quan. + Ví dụ, bạn nên đặt một điểm kết quả cho thao tác cuối cùng trong một tập hợp mà thêm + các hàng liên lạc thô và hàng dữ liệu liên kết của chúng, hoặc thao tác cuối cùng đối với một tập hợp các hàng liên quan tới + một liên lạc riêng lẻ. +

+

+ Điểm kết quả cũng là một đơn vị thao tác nguyên tử. Tất cả truy cập giữa hai điểm kết quả sẽ + hoặc thành công hoặc thất bại như một đơn vị riêng lẻ. Nếu bạn không đặt bất kỳ điểm kết quả nào, thao tác + nguyên tử nhỏ nhất chính là toàn bộ loạt thao tác. Nếu sử dụng điểm kết quả, bạn ngăn cản + các thao tác làm giảm hiệu suất của hệ thống, đồng thời đảm bảo rằng một tập con của + thao tác là tập nguyên tử. +

+

Tham chiếu lại sửa đổi

+

+ Khi bạn đang chèn một hàng liên lạc thô mới và các hàng dữ liệu liên kết của nó như một tập hợp các đối tượng + {@link android.content.ContentProviderOperation}, bạn phải liên kết các hàng dữ liệu với + hàng liên lạc thô bằng cách chèn giá trị + {@code android.provider.BaseColumns#_ID} của liên lạc thô làm giá trị + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Tuy nhiên, giá trị + này không có sẵn khi bạn đang tạo {@link android.content.ContentProviderOperation} + cho hàng dữ liệu, vì bạn chưa áp dụng + {@link android.content.ContentProviderOperation} cho hàng liên lạc thô. Để khắc phục điều này, + lớp {@link android.content.ContentProviderOperation.Builder} có phương pháp + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}. + Phương pháp này cho phép bạn chèn hoặc sửa đổi một cột bằng + kết quả của một thao tác trước đó. +

+

+ Phương pháp {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + có hai tham đối: +

+
+
+ key +
+
+ Khóa của một cặp khóa-giá trị. Giá trị của tham đối này nên là tên của một cột + trong bảng mà bạn đang sửa đổi. +
+
+ previousResult +
+
+ Chỉ mục dựa trên 0 của một giá trị trong mảng đối tượng + {@link android.content.ContentProviderResult} từ + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. Khi + thao tác hàng loạt được áp dụng, kết quả của mỗi thao tác được lưu giữ trong một mảng kết quả + trung gian. Giá trị previousResult là chỉ mục + của một trong những kết quả này, nó được truy xuất và lưu giữ với giá trị key +. Điều này cho phép bạn chèn một bản ghi liên lạc thô mới và nhận lại giá trị + {@code android.provider.BaseColumns#_ID} của nó, rồi thực hiện một "tham chiếu ngược" về + giá trị đó khi bạn thêm một hàng {@link android.provider.ContactsContract.Data}. +

+ Toàn bộ mảng kết quả được tạo khi bạn lần đầu gọi + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}, + với kích cỡ bằng với kích cỡ của {@link java.util.ArrayList} của các đối tượng + {@link android.content.ContentProviderOperation} mà bạn cung cấp. Tuy nhiên, tất cả + các phần tử trong mảng kết quả được đặt thành null, và nếu bạn cố gắng + thực hiện tham chiếu ngược tới một kết quả cho một thao tác chưa được áp dụng, +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + sẽ đưa ra một lỗi {@link java.lang.Exception}. + +

+
+
+

+ Các đoạn mã HTML sau minh họa cách chèn một liên lạc thô mới và dữ liệu hàng loạt. Chúng + bao gồm mã thiết lập một điểm kết quả và sử dụng một tham chiếu lại. Đoạn mã HTML là một + phiên bản mở rộng của phương pháp createContacEntry(), nó là một phần của lớp + ContactAdder trong ứng dụng mẫu + + Contact Manager. +

+

+ Đoạn mã HTML đầu tiên truy xuất dữ liệu liên lạc từ UI. Tại điểm này, người dùng đã + chọn tài khoản mà liên lạc thô mới nên được thêm cho tài khoản đó. +

+
+// Creates a contact entry from the current UI values, using the currently-selected account.
+protected void createContactEntry() {
+    /*
+     * Gets values from the UI
+     */
+    String name = mContactNameEditText.getText().toString();
+    String phone = mContactPhoneEditText.getText().toString();
+    String email = mContactEmailEditText.getText().toString();
+
+    int phoneType = mContactPhoneTypes.get(
+            mContactPhoneTypeSpinner.getSelectedItemPosition());
+
+    int emailType = mContactEmailTypes.get(
+            mContactEmailTypeSpinner.getSelectedItemPosition());
+
+

+ Đoạn mã HTML tiếp theo tạo một thao tác để chèn hàng liên lạc thô vào bảng + {@link android.provider.ContactsContract.RawContacts}: +

+
+    /*
+     * Prepares the batch operation for inserting a new raw contact and its data. Even if
+     * the Contacts Provider does not have any data for this person, you can't add a Contact,
+     * only a raw contact. The Contacts Provider will then add a Contact automatically.
+     */
+
+     // Creates a new array of ContentProviderOperation objects.
+    ArrayList<ContentProviderOperation> ops =
+            new ArrayList<ContentProviderOperation>();
+
+    /*
+     * Creates a new raw contact with its account type (server type) and account name
+     * (user's account). Remember that the display name is not stored in this row, but in a
+     * StructuredName data row. No other data is required.
+     */
+    ContentProviderOperation.Builder op =
+            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
+            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+

+ Tiếp theo, mã tạo các hàng dữ liệu cho hàng tên hiển thị, điện thoại và e-mail. +

+

+ Từng đối tượng bộ dựng thao tác sẽ sử dụng + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} + để nhận + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}. Tham chiếu đó sẽ trỏ + ngược về đối tượng {@link android.content.ContentProviderResult} từ thao tác đầu tiên, + là thao tác thêm hàng liên lạc thô và trả về giá trị {@code android.provider.BaseColumns#_ID} + mới của nó. Kết quả là, mỗi hàng dữ liệu được tự động liên kết bởi + {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} + của nó với hàng {@link android.provider.ContactsContract.RawContacts} mới mà nó thuộc về. +

+

+ Đối tượng {@link android.content.ContentProviderOperation.Builder} thêm hàng e-mail sẽ được + gắn cờ bằng {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) + withYieldAllowed()}, mà điều này đặt một điểm kết quả: +

+
+    // Creates the display name for the new raw contact, as a StructuredName data row.
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * withValueBackReference sets the value of the first argument to the value of
+             * the ContentProviderResult indexed by the second argument. In this particular
+             * call, the raw contact ID column of the StructuredName data row is set to the
+             * value of the result returned by the first operation, which is the one that
+             * actually adds the raw contact row.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to StructuredName
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+
+            // Sets the data row's display name to the name in the UI.
+            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+    // Inserts the specified phone number and type as a Phone data row
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * Sets the value of the raw contact id column to the new raw contact ID returned
+             * by the first operation in the batch.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to Phone
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+
+            // Sets the phone number and type
+            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
+            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+    // Inserts the specified email and type as a Phone data row
+    op =
+            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+            /*
+             * Sets the value of the raw contact id column to the new raw contact ID returned
+             * by the first operation in the batch.
+             */
+            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+
+            // Sets the data row's MIME type to Email
+            .withValue(ContactsContract.Data.MIMETYPE,
+                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+
+            // Sets the email address and type
+            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
+            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
+
+    /*
+     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
+     * will yield priority to other threads. Use after every set of operations that affect a
+     * single contact, to avoid degrading performance.
+     */
+    op.withYieldAllowed(true);
+
+    // Builds the operation and adds it to the array of operations
+    ops.add(op.build());
+
+

+ Đoạn mã HTML cuối cùng hiển thị lệnh gọi tới + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} mà + chèn liên lạc thô mới và các hàng dữ liệu. +

+
+    // Ask the Contacts Provider to create a new contact
+    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
+            mSelectedAccount.getType() + ")");
+    Log.d(TAG,"Creating contact: " + name);
+
+    /*
+     * Applies the array of ContentProviderOperation objects in batch. The results are
+     * discarded.
+     */
+    try {
+
+            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
+    } catch (Exception e) {
+
+            // Display a warning
+            Context ctx = getApplicationContext();
+
+            CharSequence txt = getString(R.string.contactCreationFailure);
+            int duration = Toast.LENGTH_SHORT;
+            Toast toast = Toast.makeText(ctx, txt, duration);
+            toast.show();
+
+            // Log exception
+            Log.e(TAG, "Exception encountered while inserting contact: " + e);
+    }
+}
+
+

+ Thao tác hàng loạt cũng cho phép bạn triển khai kiểm soát đồng thời lạc quan, + một phương pháp áp dụng các giao tác sửa đổi mà không phải khóa kho lưu giữ liên quan. + Để sử dụng phương pháp này, bạn áp dụng giao tác đó rồi kiểm tra các sửa đổi khác mà + có thể đã được thực hiện tại cùng thời điểm. Nếu bạn thấy đã diễn ra một sửa đổi không nhất quán, + hãy quay lui giao tác của bạn và thử lại. +

+

+ Kiểm soát đồng thời lạc quan rất hữu ích đối với thiết bị di động, khi đó mỗi lúc chỉ có một người dùng + và việc truy cập đồng thời vào một kho lưu giữ dữ liệu hiếm khi xảy ra. Vì không sử dụng khóa nên + không bị lãng phí thời gian cho việc thiết đặt khóa hay chờ các giao tác khác nhả khóa của mình. +

+

+ Để sử dụng kiểm soát đồng thời lạc quan trong khi đang cập nhật một hàng + {@link android.provider.ContactsContract.RawContacts} đơn, hãy làm theo các bước sau: +

+
    +
  1. + Truy xuất cột {@link android.provider.ContactsContract.SyncColumns#VERSION} + của liên lạc thô cùng với dữ liệu khác mà bạn truy xuất. +
  2. +
  3. + Tạo một đối tượng {@link android.content.ContentProviderOperation.Builder} phù hợp để + thi hành một ràng buộc, bằng cách sử dụng phương pháp + {@link android.content.ContentProviderOperation#newAssertQuery(Uri)}. Đối với URI nội dung, + sử dụng {@link android.provider.ContactsContract.RawContacts#CONTENT_URI + RawContacts.CONTENT_URI} + với {@code android.provider.BaseColumns#_ID} của liên lạc thô được nối với nó. +
  4. +
  5. + Đối với đối tượng {@link android.content.ContentProviderOperation.Builder}, hãy gọi + {@link android.content.ContentProviderOperation.Builder#withValue(String, Object) + withValue()} để so sánh cột {@link android.provider.ContactsContract.SyncColumns#VERSION} + với số phiên bản bạn vừa truy xuất. +
  6. +
  7. + Đối với cùng {@link android.content.ContentProviderOperation.Builder}, hãy gọi + {@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) + withExpectedCount()} để đảm bảo rằng chỉ một hàng được kiểm tra bằng xác nhận này. +
  8. +
  9. + Gọi {@link android.content.ContentProviderOperation.Builder#build()} để tạo đối tượng + {@link android.content.ContentProviderOperation}, rồi thêm đối tượng này làm + đối tượng đầu tiên trong {@link java.util.ArrayList} mà bạn chuyển cho + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()}. +
  10. +
  11. + Áp dụng giao tác hàng loạt. +
  12. +
+

+ Nếu hàng liên lạc thô được cập nhật bởi một thao tác khác giữa thời điểm bạn đọc hàng và + thời điểm bạn cố gắng sửa đổi nó, "xác nhận" {@link android.content.ContentProviderOperation} + sẽ thất bại, và toàn bộ loạt thao tác sẽ được rút khỏi. Sau đó, bạn có thể chọn thử lại + loạt hoặc thực hiện một hành động khác. +

+

+ Đoạn mã HTML sau minh họa cách tạo một "xác nhận" + {@link android.content.ContentProviderOperation} sau khi truy vấn một liên lạc thô đơn bằng cách sử dụng + một {@link android.content.CursorLoader}: +

+
+/*
+ * The application uses CursorLoader to query the raw contacts table. The system calls this method
+ * when the load is finished.
+ */
+public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+
+    // Gets the raw contact's _ID and VERSION values
+    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
+    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
+}
+
+...
+
+// Sets up a Uri for the assert operation
+Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
+
+// Creates a builder for the assert operation
+ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
+
+// Adds the assertions to the assert operation: checks the version and count of rows tested
+assertOp.withValue(SyncColumns.VERSION, mVersion);
+assertOp.withExpectedCount(1);
+
+// Creates an ArrayList to hold the ContentProviderOperation objects
+ArrayList ops = new ArrayList<ContentProviderOperationg>;
+
+ops.add(assertOp.build());
+
+// You would add the rest of your batch operations to "ops" here
+
+...
+
+// Applies the batch. If the assert fails, an Exception is thrown
+try
+    {
+        ContentProviderResult[] results =
+                getContentResolver().applyBatch(AUTHORITY, ops);
+
+    } catch (OperationApplicationException e) {
+
+        // Actions you want to take if the assert operation fails go here
+    }
+
+

Truy xuất và sửa đổi bằng ý định

+

+ Việc gửi một ý định tới ứng dụng danh bạ của thiết bị cho phép bạn truy cập Trình cung cấp Danh bạ + một cách gián tiếp. Ý định sẽ khởi động UI ứng dụng danh bạ của thiết bị, trong đó người dùng có thể + thực hiện công việc liên quan tới danh bạ. Với kiểu truy cập này, người dùng có thể: +

    +
  • Chọn một liên lạc từ danh sách và trả nó về ứng dụng của bạn để làm việc tiếp.
  • +
  • Chỉnh sửa dữ liệu của một liên lạc hiện có.
  • +
  • Chèn một liên lạc thô mới cho bất kỳ tài khoản nào của họ.
  • +
  • Xóa một liên lạc hoặc dữ liệu danh bạ.
  • +
+

+ Nếu người dùng đang chèn hoặc cập nhật dữ liệu, bạn có thể thu thập dữ liệu trước và gửi nó như + một phần của ý định. +

+

+ Khi bạn sử dụng ý định để truy cập Trình cung cấp Danh bạ thông qua ứng dụng danh bạ của thiết bị, bạn + không phải ghi UI hay mã của chính mình để truy nhập trình cung cấp. Bạn cũng không phải + yêu cầu quyền đọc hoặc ghi đến trình cung cấp. Ứng dụng danh bạ của thiết bị có thể + cấp quyền đọc đối với một liên lạc cho bạn, và vì bạn đang thực hiện sửa đổi đối với + trình cung cấp thông qua một ứng dụng khác, bạn không cần phải có quyền ghi. +

+

+ Tiến trình chung để gửi một ý định nhằm truy cập một trình cung cấp được mô tả chi tiết trong hướng dẫn + + Nội dung Cơ bản về Trình cung cấp Nội dung trong phần "Truy cập dữ liệu thông qua ý định." Hành động, + kiểu MIME, và các giá trị dữ liệu bạn sử dụng cho các tác vụ có sẵn được tóm tắt trong Bảng 4, trong khi các giá trị + phụ thêm mà bạn có thể sử dụng với + {@link android.content.Intent#putExtra(String, String) putExtra()} được liệt kê trong + tài liệu tham khảo cho {@link android.provider.ContactsContract.Intents.Insert}: +

+

+ Bảng 4. Ý định của Trình cung cấp Danh bạ. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Tác vụHành độngDữ liệuKiểu MIMELưu ý
Chọn một liên lạc từ danh sách{@link android.content.Intent#ACTION_PICK} + Một trong: +
    +
  • +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}, + mà hiển thị một danh sách các liên lạc. +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI}, + mà hiển thị một danh sách các số điện thoại cho một liên lạc thô. +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI}, + mà hiển thị một danh sách các địa chỉ bưu điện cho một liên lạc thô. +
  • +
  • +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}, + mà hiển thị một danh sách các địa chỉ e-mail cho một liên lạc thô. +
  • +
+
+ Không sử dụng + + Hiển thị một danh sách các liên lạc thô hoặc danh sách dữ liệu từ một liên lạc thô, tùy vào kiểu + URI nội dung mà bạn cung cấp. +

+ Gọi + {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}, + nó trả về URI nội dung của hàng được chọn. Hình thức của URI là URI nội dung + của bảng với LOOKUP_ID của hàng được nối với nó. + Ứng dụng danh bạ của thiết bị cấp quyền đọc và ghi cho URI nội dung này + trong suốt thời gian hoạt động của bạn. Xem hướng dẫn + + Nội dung Cơ bản về Trình cung cấp Nội dung để biết thêm chi tiết. +

+
Chèn một liên lạc thô mới{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}Không áp dụng + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE + RawContacts.CONTENT_TYPE}, kiểu MIME cho một tập hợp liên các lạc thô. + + Hiển thị màn hình Thêm Liên lạc của ứng dụng danh bạ của thiết bị. Các + giá trị phụ thêm mà bạn thêm vào ý định sẽ được hiển thị. Nếu được gửi bằng + {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}, + URI nội dung của liên lạc thô mới thêm sẽ được chuyển lại cho phương pháp gọi lại + {@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()} + của hoạt động của bạn trong tham đối {@link android.content.Intent}, trong + trường "dữ liệu". Để nhận giá trị, hãy gọi {@link android.content.Intent#getData()}. +
Chỉnh sửa một liên lạc{@link android.content.Intent#ACTION_EDIT} + {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} đối với + liên lạc. Hoạt động của trình chỉnh sửa sẽ cho phép người dùng chỉnh sửa bất kỳ dữ liệu nào được liên kết + với liên lạc này. + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE + Contacts.CONTENT_ITEM_TYPE}, một liên lạc đơn. + Hiển thị màn hình Chỉnh sửa Liên lạc trong ứng dụng danh bạ. Các giá trị phụ thêm mà bạn thêm + vào ý định sẽ được hiển thị. Khi người dùng nhấp vào Xong để lưu các + chỉnh sửa, hoạt động của bạn quay lại tiền cảnh. +
Hiển thị một trình chọn mà cũng có thể thêm dữ liệu.{@link android.content.Intent#ACTION_INSERT_OR_EDIT} + Không áp dụng + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + + Ý định này luôn hiển thị màn hình bộ chọn của ứng dụng danh bạ. Người dùng có thể hoặc + chọn một liên lạc để chỉnh sửa, hoặc thêm một liên lạc mới. Hoặc màn hình chỉnh sửa hoặc màn hình thêm + sẽ xuất hiện, tùy vào lựa chọn của người dùng, và dữ liệu phụ thêm mà bạn chuyển trong ý định + sẽ được hiển thị. Nếu ứng dụng của bạn hiển thị dữ liệu chẳng hạn như e-mail hoặc số điện thoại, hãy sử dụng + ý định này để cho phép người dùng thêm dữ liệu vào một liên lạc hiện tại. + liên lạc, +

+ Lưu ý: Không cần gửi một giá trị tên trong phần phụ thêm của ý định, + vì người dùng luôn chọn một tên hiện có hoặc thêm một tên mới. Thêm nữa, + nếu bạn gửi một tên, và người dùng chọn thực hiện chỉnh sửa, ứng dụng danh bạ sẽ + hiển thị tên mà bạn gửi, ghi đè giá trị trước. Nếu người dùng không + để ý thấy điều này và lưu chỉnh sửa, giá trị cũ sẽ bị mất. +

+
+

+ Ứng dụng danh bạ của thiết bị không cho phép bạn xóa một liên lạc thô hay bất kỳ dữ liệu nào bằng một + ý định. Thay vào đó, để xóa một liên lạc thô, hãy sử dụng + {@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} + hoặc {@link android.content.ContentProviderOperation#newDelete(Uri) + ContentProviderOperation.newDelete()}. +

+

+ Đoạn mã HTML sau minh họa cách xây dựng và gửi một ý định để chèn một liên lạc thô mới và + dữ liệu: +

+
+// Gets values from the UI
+String name = mContactNameEditText.getText().toString();
+String phone = mContactPhoneEditText.getText().toString();
+String email = mContactEmailEditText.getText().toString();
+
+String company = mCompanyName.getText().toString();
+String jobtitle = mJobTitle.getText().toString();
+
+// Creates a new intent for sending to the device's contacts application
+Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
+
+// Sets the MIME type to the one expected by the insertion activity
+insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+
+// Sets the new contact name
+insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
+
+// Sets the new company and job title
+insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
+insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
+
+/*
+ * Demonstrates adding data rows as an array list associated with the DATA key
+ */
+
+// Defines an array list to contain the ContentValues objects for each row
+ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
+
+
+/*
+ * Defines the raw contact row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues rawContactRow = new ContentValues();
+
+// Adds the account type and name to the row
+rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
+rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
+
+// Adds the row to the array
+contactData.add(rawContactRow);
+
+/*
+ * Sets up the phone number data row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues phoneRow = new ContentValues();
+
+// Specifies the MIME type for this data row (all data rows must be marked by their type)
+phoneRow.put(
+        ContactsContract.Data.MIMETYPE,
+        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
+);
+
+// Adds the phone number and its type to the row
+phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
+
+// Adds the row to the array
+contactData.add(phoneRow);
+
+/*
+ * Sets up the email data row
+ */
+
+// Sets up the row as a ContentValues object
+ContentValues emailRow = new ContentValues();
+
+// Specifies the MIME type for this data row (all data rows must be marked by their type)
+emailRow.put(
+        ContactsContract.Data.MIMETYPE,
+        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
+);
+
+// Adds the email address and its type to the row
+emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
+
+// Adds the row to the array
+contactData.add(emailRow);
+
+/*
+ * Adds the array to the intent's extras. It must be a parcelable object in order to
+ * travel between processes. The device's contacts app expects its key to be
+ * Intents.Insert.DATA
+ */
+insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
+
+// Send out the intent to start the device's contacts app in its add contact activity.
+startActivity(insertIntent);
+
+

Toàn vẹn dữ liệu

+

+ Vì kho lưu giữ danh bạ chứa dữ liệu quan trọng và nhạy cảm mà người dùng cho là + đúng và cập nhật, Trình cung cấp Danh bạ có các quy tắc về toàn vẹn dữ liệu được định nghĩa rõ ràng. Bạn có + trách nhiệm tuân theo những quy tắc này khi sửa đổi dữ liệu danh bạ. Các quy tắc + quan trọng được liệt kê ở đây: +

+
+
+ Luôn thêm một hàng {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} cho + mỗi hàng {@link android.provider.ContactsContract.RawContacts} mà bạn thêm. +
+
+ Hàng {@link android.provider.ContactsContract.RawContacts} không có một hàng + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} trong bảng + {@link android.provider.ContactsContract.Data} có thể gây ra sự cố trong khi + tổng hợp. +
+
+ Luôn liên kết các hàng {@link android.provider.ContactsContract.Data} mới với hàng + {@link android.provider.ContactsContract.RawContacts} mẹ của chúng. +
+
+ Mỗi hàng {@link android.provider.ContactsContract.Data} mà không được liên kết với một + {@link android.provider.ContactsContract.RawContacts} sẽ không hiển thị trong ứng dụng danh bạ + của thiết bị, và nó có thể gây ra sự cố với trình điều hợp đồng bộ. +
+
+ Chỉ thay đổi dữ liệu đối với những liên lạc thô mà bạn sở hữu. +
+
+ Nhớ rằng Trình cung cấp Danh bạ luôn quản lý dữ liệu từ vài + loại tài khoản/dịch vụ trực tuyến khác nhau. Bạn cần đảm bảo rằng ứng dụng của bạn chỉ sửa đổi + hoặc xóa dữ liệu đối với các hàng thuộc về bạn, và rằng nó chỉ chèn dữ liệu có + loại và tên tài khoản mà bạn kiểm soát. +
+
+ Luôn sử dụng các hằng số được định nghĩa trong {@link android.provider.ContactsContract} và các lớp con của nó + đối với thẩm quyền, URI nội dung, đường dẫn URI, tên cột, kiểu MIME, và các giá trị + {@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE}. +
+
+ Sử dụng những hằng số này sẽ giúp bạn tránh gặp lỗi. Bạn cũng sẽ được thông báo bằng cảnh báo + từ trình biên dịch nếu bất kỳ hằng số nào không được chấp nhận. +
+
+

Hàng dữ liệu tùy chỉnh

+

+ Bằng cách tạo và sử dụng các kiểu MIME tùy chỉnh của chính mình, bạn có thể chèn, chỉnh sửa, xóa và truy xuất + các hàng dữ liệu của chính mình trong bảng {@link android.provider.ContactsContract.Data}. Các hàng của bạn + bị giới hạn bằng cách sử dụng cột được định nghĩa trong + {@link android.provider.ContactsContract.DataColumns}, mặc dù bạn có thể ánh xạ + tên cột theo kiểu của chính mình với tên cột mặc định. Trong ứng dụng danh bạ của thiết bị, + dữ liệu cho các hàng của bạn được hiển thị nhưng không thể chỉnh sửa hay xóa được, và người dùng không thể thêm + dữ liệu bổ sung. Để cho phép người dùng sửa đổi các hàng dữ liệu tùy chỉnh của mình, bạn phải cung cấp một hoạt động + trình chỉnh sửa trong ứng dụng của chính mình. +

+

+ Để hiển thị dữ liệu tùy chỉnh của mình, hãy cung cấp một tệp contacts.xml chứa một phần tử + <ContactsAccountType> và một hoặc nhiều phần tử con + <ContactsDataKind> của nó. Điều này được mô tả chi tiết hơn trong + phần <ContactsDataKind> element. +

+

+ Để tìm hiểu thêm về các kiểu MIME tùy chỉnh, hãy đọc hướng dẫn + + Tạo một Trình cung cấp Nội dung. +

+

Trình điều hợp Đồng bộ Trình cung cấp Danh bạ

+

+ Trình cung cấp Danh bạ được thiết kế riêng để xử lý đồng bộ hoá + dữ liệu danh bạ giữa một thiết bị và một dịch vụ trực tuyến. Điều này cho phép người dùng tải + dữ liệu hiện có xuống một thiết bị mới và tải dữ liệu hiện có lên một tài khoản mới. + Đồng bộ hoá cũng đảm bảo rằng người dùng có sẵn dữ liệu mới nhất, không phụ thuộc vào + nguồn của các bổ sung và thay đổi. Một ưu điểm khác của đồng bộ hoá đó là nó khiến + dữ liệu danh bạ có sẵn ngay cả khi thiết bị không được kết nối với mạng. +

+

+ Mặc dù bạn có thể triển khai đồng bộ hoá theo nhiều cách, hệ thống Android cung cấp + một khuôn khổ đồng bộ hóa bổ trợ có khả năng tự động hóa những tác vụ sau: +

    + +
  • + Kiểm tra sự sẵn sàng của mạng. +
  • +
  • + Lập lịch biểu và thực hiện đồng bộ hoá dựa trên tùy chọn của người dùng. +
  • +
  • + Khởi động lại những đồng bộ hoá đã dừng. +
  • +
+

+ Để sử dụng khuôn khổ này, bạn phải cung cấp một phần bổ trợ trình điều hợp đồng bộ. Mỗi trình điều hợp đồng bộ là duy nhất đối với + một dịch vụ và trình cung cấp nội dung, nhưng có thể xử lý nhiều tên tài khoản cho cùng dịch vụ. Khuôn khổ + cũng cho phép nhiều trình điều hợp đồng bộ cho cùng dịch vụ và trình cung cấp. +

+

Các lớp và tệp trình điều hợp đồng bộ

+

+ Bạn triển khai một trình điều hợp đồng bộ làm lớp con của + {@link android.content.AbstractThreadedSyncAdapter} và cài đặt nó như một phần của một ứng dụng + Android. Hệ thống biết về trình điều hợp đồng bộ từ các phần tử trong bản kê khai ứng dụng + của nó, và từ một tệp XML đặc biệt được chỉ đến trong bản kê khai. Tệp XML sẽ định nghĩa + loại tài khoản cho dịch vụ trực tuyến và thẩm quyền cho trình cung cấp nội dung, cùng nhau chúng + xác định duy nhất một trình điều hợp. Trình điều hợp đồng bộ không được kích hoạt cho tới khi người dùng thêm một + tài khoản cho loại tài khoản của trình điều hợp đồng bộ và kích hoạt đồng bộ hoá cho trình cung cấp + nội dung mà trình điều hợp đồng bộ sẽ đồng bộ cùng. Tại thời điểm đó, hệ thống bắt đầu quản lý trình điều hợp, + gọi nó nếu cần thiết để đồng bộ hoá giữa trình cung cấp nội dung và máy chủ. +

+

+ Lưu ý: Việc sử dụng một loại tài khoản để tham gia nhận biết trình điều hợp đồng bộ sẽ cho phép + hệ thống phát hiện và nhóm cùng nhau những trình điều hợp đồng bộ truy cập các dịch vụ khác nhau từ + cùng tổ chức. Ví dụ, các trình điều hợp đồng bộ cho dịch vụ trực tuyến của Google đều có cùng + loại tài khoản com.google. Khi người dùng thêm một tài khoản Google vào thiết bị của mình, tất cả + trình điều hợp đồng bộ được cài đặt cho dịch vụ Google được liệt kê cùng nhau; mỗi trình điều hợp đồng bộ + được liệt kê sẽ đồng bộ với một trình cung cấp nội dung khác nhau trên thiết bị. +

+

+ Vì hầu hết dịch vụ đều yêu cầu người dùng xác minh danh tính của họ trước khi truy cập + dữ liệu, hệ thống Android cung cấp một khuôn khổ xác thực tương tự như và thường + được sử dụng cùng với khuôn khổ của trình điều hợp đồng bộ. Khuôn khổ xác thực sử dụng + các trình xác thực bổ trợ là lớp con của + {@link android.accounts.AbstractAccountAuthenticator}. Một trình xác thực sẽ xác minh + danh tính của người dùng theo các bước sau: +

    +
  1. + Thu thập tên, mật khẩu hoặc thông tin tương tự của người dùng ( +thông tin xác thực của người dùng). +
  2. +
  3. + Gửi thông tin xác thực tới dịch vụ +
  4. +
  5. + Kiểm tra trả lời của dịch vụ. +
  6. +
+

+ Nếu dịch vụ chấp nhận thông tin xác thực, trình xác thực có thể + lưu giữ thông tin xác thực đó để sử dụng sau. Vì khuôn khổ trình xác thực bổ trợ, + {@link android.accounts.AccountManager} có thể cung cấp quyền truy cập bất kỳ token xác thực nào mà một trình xác thực + hỗ trợ và chọn hiện ra, chẳng hạn như token xác thực OAuth2. +

+

+ Mặc dù không yêu cầu xác thực, phần lớn dịch vụ danh bạ đều sử dụng nó. + Tuy nhiên, bạn không phải sử dụng khuôn khổ xác thực của Android để thực hiện xác thực. +

+

Triển khai trình điều hợp đồng bộ

+

+ Để triển khai một trình điều hợp đồng bộ cho Trình cung cấp Danh bạ, bạn bắt đầu bằng cách tạo một + ứng dụng Android chứa: +

+
+
+ Một thành phần {@link android.app.Service} để hồi đáp lại các yêu cầu từ hệ thống nhằm + gắn kết với trình điều hợp đồng bộ. +
+
+ Khi hệ thống muốn chạy đồng bộ hoá, nó gọi phương pháp + {@link android.app.Service#onBind(Intent) onBind()} của dịch vụ và nhận một + {@link android.os.IBinder} cho trình điều hợp đồng bộ. Điều này cho phép hệ thống thực hiện + lệnh gọi liên tiến trình tới các phương pháp của trình điều hợp. +

+ Trong ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu, tên lớp của dịch vụ này là + com.example.android.samplesync.syncadapter.SyncService. +

+
+
+ Trình điều hợp đồng bộ thực tế, được triển khai như một lớp con cụ thể của + {@link android.content.AbstractThreadedSyncAdapter}. +
+
+ Lớp này thực hiện công việc tải xuống dữ liệu từ máy chủ, tải lên dữ liệu từ + thiết bị, và xử lý xung đột. Công việc chính của trình điều hợp được + thực hiện trong phương pháp {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( + Account, Bundle, String, ContentProviderClient, SyncResult) + onPerformSync()}. Lớp này phải được khởi tạo như một đối tượng duy nhất (singleton). +

+ Trong ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu, trình điều hợp đồng bộ được định nghĩa trong lớp + com.example.android.samplesync.syncadapter.SyncAdapter. +

+
+
+ Một lớp con của {@link android.app.Application}. +
+
+ Lớp này đóng vai trò như một nhà máy cho đối tượng duy nhất của trình điều hợp đồng bộ. Sử dụng phương pháp + {@link android.app.Application#onCreate()} để khởi tạo trình điều hợp đồng bộ, và + cung cấp một phương pháp "bộ nhận" tĩnh để trả đối tượng duy nhất về phương pháp + {@link android.app.Service#onBind(Intent) onBind()} của dịch vụ + của trình điều hợp đồng bộ. +
+
+ Tùy chọn: Một thành phần {@link android.app.Service} để hồi đáp lại + các yêu cầu từ hệ thống về xác thực người dùng. +
+
+ {@link android.accounts.AccountManager} khởi động dịch vụ này để bắt đầu tiến trình + xác thực. Phương pháp {@link android.app.Service#onCreate()} của dịch vụ này sẽ khởi tạo một + đối tượng trình xác thực. Khi hệ thống muốn xác thực một tài khoản người dùng cho trình điều hợp đồng bộ + của ứng dụng, nó sẽ gọi phương pháp + {@link android.app.Service#onBind(Intent) onBind()} của dịch vụ để nhận một + {@link android.os.IBinder} cho trình xác thực. Điều này cho phép hệ thống thực hiện + lệnh gọi liên tiến trình tới các phương pháp của trình xác thực. +

+ Trong ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu, tên lớp của dịch vụ này là + com.example.android.samplesync.authenticator.AuthenticationService. +

+
+
+ Tùy chọn: Một lớp con cụ thể của + {@link android.accounts.AbstractAccountAuthenticator} để xử lý các yêu cầu về + xác thực. +
+
+ Lớp này cung cấp các phương pháp mà {@link android.accounts.AccountManager} gọi ra + để xác thực các thông tin xác thực của người dùng với máy chủ. Các chi tiết của + tiến trình xác thực rất khác nhau dựa trên công nghệ máy chủ đang sử dụng. Bạn nên + tham khảo tài liệu cho phần mềm máy chủ của mình để tìm hiểu thêm về xác thực. +

+ Trong ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu, trình xác thực được định nghĩa trong lớp + com.example.android.samplesync.authenticator.Authenticator. +

+
+
+ Các tệp XML để định nghĩa trình điều hợp đồng bộ và trình xác thực cho hệ thống. +
+
+ Các thành phần dịch vụ trình điều hợp đồng bộ và trình xác thực đã nêu được + định nghĩa trong các phần tử +<service> + ở bản kê khai của ứng dụng. Những phần tử này + chứa các phần tử con +<meta-data> +mà cung cấp dữ liệu cụ thể cho + hệ thống: +
    +
  • + Phần tử +<meta-data> + cho dịch vụ trình điều hợp đồng bộ sẽ trỏ về + tệp XML res/xml/syncadapter.xml. Đến lượt mình, tệp này quy định + một URI cho dịch vụ web mà sẽ được đồng bộ hóa với Trình cung cấp Danh bạ, + và một loại tài khoản cho dịch vụ web. +
  • +
  • + Tùy chọn: Phần tử +<meta-data> + cho trình xác thực sẽ trỏ về tệp XML + res/xml/authenticator.xml. Đến lượt mình, tệp này quy định + loại tài khoản mà trình xác thực này hỗ trợ, cũng như các tài nguyên UI mà + xuất hiện trong tiến trình xác thực. Loại tài khoản được quy định trong phần tử + này phải giống như loại tài khoản được quy định cho trình điều hợp + đồng bộ. +
  • +
+
+
+

Dữ liệu từ Luồng Xã hội

+

+ Các bảng {@code android.provider.ContactsContract.StreamItems} và + {@code android.provider.ContactsContract.StreamItemPhotos} quản lý + dữ liệu đến từ các mạng xã hội. Bạn có thể ghi một trình điều hợp đồng bộ mà thêm dữ liệu luồng từ + mạng của chính mình vào những bảng này, hoặc bạn có thể đọc dữ liệu luồng từ những bảng này và + hiển thị nó trong ứng dụng của chính mình, hoặc cả hai. Với những tính năng này, các dịch vụ và ứng dụng + mạng xã hội của bạn có thể được tích hợp vào trải nghiệm mạng xã hội của Android. +

+

Văn bản từ luồng xã hội

+

+ Các mục dòng dữ liệu luôn được liên kết với một liên lạc thô. + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} liên kết với giá trị + _ID của liên lạc thô mới. Loại tài khoản và tên tài khoản của liên lạc thô + cũng được lưu giữ trong hàng mục dòng. +

+

+ Lưu giữ dữ liệu từ luồng của bạn vào những cột sau: +

+
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} +
+
+ Bắt buộc. Loại tài khoản của người dùng đối với liên lạc thô được liên kết với mục dòng + này. Nhớ đặt giá trị này khi bạn chèn một mục dòng. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} +
+
+ Bắt buộc. Tên tài khoản của người dùng đối với liên lạc thô được liên kết với mục dòng + này. Nhớ đặt giá trị này khi bạn chèn một mục dòng. +
+
+ Cột mã định danh +
+
+ Bắt buộc. Bạn phải chèn các cột mã định danh sau khi chèn + một mục dòng: +
    +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}: Giá trị + {@code android.provider.BaseColumns#_ID} của liên lạc mà mục dòng + này được liên kết với. +
  • +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}: Giá trị + {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} của liên lạc + mà mục dòng này được liên kết với. +
  • +
  • + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}: Giá trị + {@code android.provider.BaseColumns#_ID} của liên lạc thô mà mục dòng này + được liên kết với. +
  • +
+
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} +
+
+ Tùy chọn. Lưu giữ thông tin tóm tắt mà bạn có thể hiển thị ở phần đầu của một mục dòng. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} +
+
+ Văn bản của mục dòng, hoặc là nội dung đã được đăng bởi nguồn của mục đó, + hoặc là mô tả về một số hành động đã khởi tạo mục dòng. Cột này có thể chứa + bất kỳ hình ảnh tài nguyên định dạng và được nhúng nào mà có thể được kết xuất bởi + {@link android.text.Html#fromHtml(String) fromHtml()}. Trình cung cấp có thể cắt bớt hoặc + cắt ngắn bằng dấu ba chấm các nội dung dài, nhưng sẽ cố gắng tránh làm hỏng các tag. +
+
+ {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} +
+
+ Xâu văn bản chứa thời gian mà mục dòng được chèn hoặc cập nhật, có + dạng mili giây trôi qua kể từ giờ epoch. Những ứng dụng chèn hoặc cập nhật mục dòng sẽ chịu + trách nhiệm duy trì cột này; nó không được tự động duy trì bởi + Trình cung cấp Danh bạ. +
+
+

+ Để hiển thị thông tin nhận dạng cho các mục dòng của bạn, hãy sử dụng + {@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}, + {@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL}, và + {@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} để liên kết với các tài nguyên + trong ứng dụng của mình. +

+

+ Bảng {@code android.provider.ContactsContract.StreamItems} chứa các cột + {@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} thông qua + {@code android.provider.ContactsContract.StreamItemsColumns#SYNC4} dành riêng để sử dụng + trình điều hợp đồng bộ. +

+

Ảnh từ luồng xã hội

+

+ Bảng {@code android.provider.ContactsContract.StreamItemPhotos} lưu giữ ảnh được liên kết + với một mục dòng. Cột + {@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} của bảng + liên kết với các giá trị trong {@code android.provider.BaseColumns#_ID} của bảng + {@code android.provider.ContactsContract.StreamItems}. Các tham chiếu ảnh được lưu giữ trong + bảng ở những cột này: +

+
+
+ Cột {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} (một BLOB). +
+
+ Biểu diễn dạng nhị phân của ảnh, được trình cung cấp đổi kích cỡ để lưu giữ và hiển thị. + Cột này có sẵn để tương thích ngược với các phiên bản trước của Trình cung cấp + Danh bạ mà đã sử dụng nó để lưu giữ ảnh. Tuy nhiên, trong phiên bản hiện tại + bạn không nên sử dụng cột này để lưu giữ ảnh. Thay vào đó, hãy sử dụng + hoặc {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} hoặc + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (cả hai + đều được mô tả trong các điểm sau) để lưu giữ ảnh trong một tệp. Lúc này, cột này + chứa một hình thu nhỏ của ảnh sẵn sàng để đọc. +
+
+ {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} +
+
+ Một mã định danh dạng số của ảnh cho một liên lạc thô. Nối giá trị này với hằng số + {@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI} + để nhận một URI nội dung trỏ về một tệp ảnh đơn, rồi gọi + {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) + openAssetFileDescriptor()} để nhận một điều khiển (handle) cho tệp ảnh. +
+
+ {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} +
+
+ Một URI nội dung trỏ trực tiếp tới tệp ảnh cho ảnh được đại diện bởi hàng này. + Gọi {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) + openAssetFileDescriptor()} bằng URI này để nhận một điều khiển (handle) cho tệp ảnh. +
+
+

Sử dụng các bảng luồng xã hội

+

+ Những bảng này hoạt động giống như các bảng chính khác trong Trình cung cấp Danh bạ, ngoại trừ: +

+
    +
  • + Những bảng này yêu cầu quyền truy cập bổ sung. Để đọc từ chúng, ứng dụng của bạn + phải có quyền {@code android.Manifest.permission#READ_SOCIAL_STREAM}. Để + sửa đổi chúng, ứng dụng của bạn phải có quyền + {@code android.Manifest.permission#WRITE_SOCIAL_STREAM}. +
  • +
  • + Đối với bảng {@code android.provider.ContactsContract.StreamItems}, số hàng + được lưu giữ cho mỗi liên lạc thô sẽ bị giới hạn. Sau khi đạt đến giới hạn này, + Trình cung cấp Danh bạ sẽ tạo khoảng trống cho các hàng mục dòng mới bằng cách tự động xóa + những hàng có + {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} lâu nhất. Để nhận + giới hạn, hãy phát hành một truy vấn tới URI nội dung + {@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}. Bạn có thể để + tất cả các tham đối khác ngoài URI nội dung được đặt về null. Truy vấn + trả về một Con chạy chứa một hàng đơn, với cột đơn + {@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}. +
  • +
+ +

+ Lớp {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} định nghĩa một + bảng con {@code android.provider.ContactsContract.StreamItemPhotos} chứa các hàng ảnh + cho một mục dòng đơn. +

+

Tương tác từ luồng xã hội

+

+ Dữ liệu từ luồng xã hội được quản lý bởi Trình cung cấp Danh bạ, kết hợp với + ứng dụng danh bạ của thiết bị, cung cấp một cách hiệu quả để kết nối hệ thống mạng xã hội của bạn + với các liên lạc hiện tại. Có sẵn những tính năng sau: +

+
    +
  • + Bằng cách đồng bộ dịch vụ mạng xã hội của bạn với Trình cung cấp Danh bạ bằng một trình điều hợp + đồng bộ, bạn có thể truy xuất hoạt động gần đây đối với danh bạ của một người dùng và lưu giữ nó trong + các bảng {@code android.provider.ContactsContract.StreamItems} và + {@code android.provider.ContactsContract.StreamItemPhotos} để sử dụng sau. +
  • +
  • + Bên cạnh việc đồng bộ hoá thường xuyên, bạn có thể kích khởi trình điều hợp đồng bộ của mình để truy xuất + dữ liệu bổ sung khi người dùng chọn một liên lạc để xem. Điều này cho phép trình điều hợp đồng bộ của bạn + truy xuất ảnh độ phân giải cao và các mục dòng gần đây nhất cho liên lạc đó. +
  • +
  • + Bằng cách đăng ký một thông báo với ứng dụng danh bạ của thiết bị và Trình cung cấp + Danh bạ, bạn có thể nhận một ý định khi một liên lạc được xem, và tại thời điểm đó, + cập nhật trạng thái của liên lạc đó từ dịch vụ của bạn. Phương pháp này có thể nhanh hơn và sử dụng ít + băng thông hơn việc thực hiện đồng bộ đầy đủ với một trình điều hợp đồng bộ. +
  • +
  • + Người dùng có thể thêm một liên lạc vào dịch vụ mạng xã hội của mình trong khi đang xem liên lạc đó + trong ứng dụng danh bạ của thiết bị. Bạn kích hoạt điều này bằng tính năng "mời liên lạc", + theo đó cho phép kết hợp một hoạt động để thêm một liên lạc hiện có vào mạng + của bạn, và một tệp XML để cung cấp cho ứng dụng danh bạ của thiết bị và + Trình cung cấp Danh bạ thông tin chi tiết về ứng dụng của bạn. +
  • +
+

+ Đồng bộ hóa thường xuyên các mục dòng với Trình cung cấp Danh bạ giống như + các trường hợp đồng bộ hoá khác. Để tìm hiểu thêm về đồng bộ hoá, hãy xem phần + Trình điều hợp Đồng bộ Trình cung cấp Danh bạ. Việc đăng ký thông tin và + mời liên lạc được đề cập trong hai phần tiếp theo. +

+

Đăng ký để xử lý các lượt xem mạng xã hội

+

+ Để đăng ký để trình điều hợp đồng bộ của bạn nhận thông báo khi người dùng xem một liên lạc do + trình điều hợp đồng bộ của bạn quản lý: +

+
    +
  1. + Tạo một tệp có tên contacts.xml trong thư mục res/xml/ + của dự án của bạn. Nếu đã có tệp này, bạn có thể bỏ qua bước này. +
  2. +
  3. + Trong tệp này, hãy thêm phần tử +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này. +
  4. +
  5. + Để đăng ký một dịch vụ được thông báo khi người dùng mở trang chi tiết của một liên lạc trong + ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính + viewContactNotifyService="serviceclass" vào phần tử, trong đó + serviceclass là tên lớp được đáp ứng đầy đủ của dịch vụ + mà sẽ nhận ý định từ ứng dụng danh bạ của thiết bị. Đối với dịch vụ + trình thông báo, hãy sử dụng một lớp mở rộng {@link android.app.IntentService}, để cho phép dịch vụ + nhận các ý định. Dữ liệu trong ý định đến chứa URI nội dung của liên lạc + thô mà người dùng đã nhấp vào. Từ dịch vụ trình thông báo, bạn có thể gắn kết với rồi gọi trình điều hợp đồng bộ + của bạn để cập nhật dữ liệu cho liên lạc thô. +
  6. +
+

+ Để đăng ký một hoạt động sẽ được gọi khi người dùng nhấp vào một mục dòng hay ảnh hoặc cả hai: +

+
    +
  1. + Tạo một tệp có tên contacts.xml trong thư mục res/xml/ + của dự án của bạn. Nếu đã có tệp này, bạn có thể bỏ qua bước này. +
  2. +
  3. + Trong tệp này, hãy thêm phần tử +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này. +
  4. +
  5. + Để đăng ký một trong các hoạt động của bạn sẽ xử lý khi người dùng nhấp vào một mục dòng trong + ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính + viewStreamItemActivity="activityclass" vào phần tử đó, trong đó + activityclass là tên lớp được xác định đầy đủ của hoạt động + mà sẽ nhận ý định từ ứng dụng danh bạ của thiết bị. +
  6. +
  7. + Để đăng ký một trong các hoạt động của bạn sẽ xử lý khi người dùng nhấp vào một ảnh luồng trong + ứng dụng danh bạ của thiết bị, hãy thêm thuộc tính + viewStreamItemPhotoActivity="activityclass" vào phần tử đó, trong đó + activityclass là tên lớp được xác định đầy đủ của hoạt động + mà sẽ nhận ý định từ ứng dụng danh bạ của thiết bị. +
  8. +
+

+ Phần tử <ContactsAccountType> được mô tả chi tiết hơn trong mục + phần tử <ContactsAccountType>. +

+

+ Ý định đến chứa URI nội dung của mục hoặc ảnh mà người dùng đã nhấp vào. + Để có các hoạt động riêng cho các mục văn bản và ảnh, hãy sử dụng cả hai thuộc tính trong cùng tệp. +

+

Tương tác với dịch vụ mạng xã hội của bạn

+

+ Người dùng không phải rời ứng dụng danh bạ của thiết bị để mời một liên lạc tới trang mạng xã hội + của bạn. Thay vào đó, bạn có thể thiết đặt để ứng dụng danh bạ của thiết bị gửi một ý định để mời + liên lạc đó tới một trong các hoạt động của mình. Để thiết đặt điều này: +

+
    +
  1. + Tạo một tệp có tên contacts.xml trong thư mục res/xml/ + của dự án của bạn. Nếu đã có tệp này, bạn có thể bỏ qua bước này. +
  2. +
  3. + Trong tệp này, hãy thêm phần tử +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">. + Nếu phần tử này đã tồn tại, bạn có thể bỏ qua bước này. +
  4. +
  5. + Thêm các thuộc tính sau: +
      +
    • inviteContactActivity="activityclass"
    • +
    • + inviteContactActionLabel="@string/invite_action_label" +
    • +
    + Giá trị activityclass là tên lớp được xác định đầy đủ của hoạt động + mà sẽ nhận được ý định. Giá trị invite_action_label + là một xâu văn bản được hiển thị trong menu Thêm Kết nối trong ứng dụng danh bạ + của thiết bị. +
  6. +
+

+ Lưu ý: ContactsSource là một tên tag không được chấp nhận đối với + ContactsAccountType. +

+

Tham chiếu contacts.xml

+

+ Tệp contacts.xml chứa các phần tử XML có chức năng kiểm soát tương tác giữa + trình điều hợp đồng bộ và ứng dụng của bạn với ứng dụng danh bạ và Trình cung cấp Danh bạ. Những + phần tử này được mô tả trong các mục sau. +

+

Thành phần <ContactsAccountType>

+

+ Phần tử <ContactsAccountType> kiểm soát tương tác giữa + ứng dụng của bạn với ứng dụng danh bạ. Nó có những cú pháp sau: +

+
+<ContactsAccountType
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        inviteContactActivity="activity_name"
+        inviteContactActionLabel="invite_command_text"
+        viewContactNotifyService="view_notify_service"
+        viewGroupActivity="group_view_activity"
+        viewGroupActionLabel="group_action_text"
+        viewStreamItemActivity="viewstream_activity_name"
+        viewStreamItemPhotoActivity="viewphotostream_activity_name">
+
+

+ chứa trong: +

+

+ res/xml/contacts.xml +

+

+ có thể chứa: +

+

+ <ContactsDataKind> +

+

+ Mô tả: +

+

+ Khai báo các thành phần Android và nhãn UI mà cho phép người dùng mời một trong các liên lạc của mình đến + một mạng xã hội, thông báo người dùng khi một trong các luồng mạng xã hội của họ được cập nhật, + v.v. +

+

+ Để ý rằng tiền tố thuộc tính android: không nhất thiết áp dụng cho các thuộc tính + của <ContactsAccountType>. +

+

+ Thuộc tính: +

+
+
{@code inviteContactActivity}
+
+ Tên lớp được xác định đầy đủ của hoạt động trong ứng dụng của bạn mà bạn muốn + kích hoạt khi người dùng chọn Thêm kết nối từ ứng dụng danh bạ + của thiết bị. +
+
{@code inviteContactActionLabel}
+
+ Một xâu văn bản được hiển thị cho hoạt động được quy định trong + {@code inviteContactActivity}, trong menu Thêm kết nối. + Ví dụ, bạn có thể sử dụng xâu "Follow in my network". Bạn có thể sử dụng mã định danh + tài nguyên xâu cho nhãn này. +
+
{@code viewContactNotifyService}
+
+ Tên lớp được xác định đầy đủ của một dịch vụ trong ứng dụng của bạn mà sẽ nhận được + thông báo khi người dùng xem một liên lạc. Thông báo này được gửi từ ứng dụng danh bạ + của thiết bị; nó cho phép ứng dụng của bạn tạm hoãn các thao tác dùng nhiều dữ liệu tới + khi cần. Ví dụ, ứng dụng của bạn có thể hồi đáp lại thông báo này + bằng cách đọc và hiển thị ảnh độ phân giải cao của danh bạ và các mục dòng mạng xã hội + gần đây nhất. Tính năng này được mô tả chi tiết hơn trong phần + Tương tác với luồng xã hội. Bạn có thể thấy một + ví dụ về dịch vụ thông báo trong tệp NotifierService.java trong ứng dụng mẫu + SampleSyncAdapter +. +
+
{@code viewGroupActivity}
+
+ Tên lớp được xác định đầy đủ của một hoạt động trong ứng dụng của bạn mà có thể + hiển thị thông tin nhóm. Khi người dùng nhấp vào nhãn nhóm trong ứng dụng danh bạ + của thiết bị, UI cho hoạt động này sẽ được hiển thị. +
+
{@code viewGroupActionLabel}
+
+ Nhãn mà ứng dụng danh bạ hiển thị cho một điều khiển UI có cho phép + người dùng xem các nhóm trong ứng dụng của bạn. +

+ Ví dụ, nếu bạn cài đặt ứng dụng Google+ trên thiết bị của mình và bạn đồng bộ + Google+ với ứng dụng danh bạ, bạn sẽ thấy các vòng tròn Google+ được liệt kê thành các nhóm + trong tab Nhóm của ứng dụng danh bạ của bạn. Nếu bạn nhấp vào một vòng tròn + Google+, bạn sẽ thấy những người trong vòng tròn đó được liệt kê thành một "nhóm". Phía bên trên của + hiển thị, bạn sẽ thấy một biểu tượng Google+; nếu bạn nhấp vào đó, điều khiển sẽ chuyển sang ứng dụng + Google+. Ứng dụng danh bạ làm điều này bằng + {@code viewGroupActivity}, bằng cách sử dụng biểu tượng Google+ làm giá trị của + {@code viewGroupActionLabel}. +

+

+ Một mã định danh tài nguyên xâu được cho phép cho thuộc tính này. +

+
+
{@code viewStreamItemActivity}
+
+ Tên lớp được xác định đầy đủ của một hoạt động trong ứng dụng của bạn mà + ứng dụng danh bạ của thiết bị khởi chạy khi người dùng nhấp vào một mục dòng đối với một liên lạc thô. +
+
{@code viewStreamItemPhotoActivity}
+
+ Tên lớp được xác định đầy đủ của một hoạt động trong ứng dụng của bạn mà + ứng dụng danh bạ của thiết bị khởi chạy khi người dùng nhấp vào một ảnh trong mục dòng + đối với một liên lạc thô. +
+
+

Phần tử <ContactsDataKind>

+

+ Phần tử <ContactsDataKind> kiểm soát việc hiển thị các hàng + dữ liệu tùy chỉnh của ứng dụng của bạn trong UI của ứng dụng danh bạ. Nó có những cú pháp sau: +

+
+<ContactsDataKind
+        android:mimeType="MIMEtype"
+        android:icon="icon_resources"
+        android:summaryColumn="column_name"
+        android:detailColumn="column_name">
+
+

+ chứa trong: +

+<ContactsAccountType> +

+ Mô tả: +

+

+ Sử dụng phần tử này để ứng dụng danh bạ hiển thị các nội dung trong một hàng dữ liệu tùy chỉnh như + một phần chi tiết của một liên lạc thô. Mỗi phần tử con <ContactsDataKind> + của <ContactsAccountType> đại diện cho một kiểu hàng dữ liệu tùy chỉnh mà trình điều hợp + đồng bộ của bạn thêm vào bảng {@link android.provider.ContactsContract.Data}. Thêm một phần tử + <ContactsDataKind> cho mỗi kiểu MIME tùy chỉnh mà bạn sử dụng. Bạn không phải + thêm phần tử nếu có một hàng dữ liệu tùy chỉnh mà bạn không muốn hiển thị dữ liệu. +

+

+ Thuộc tính: +

+
+
{@code android:mimeType}
+
+ Kiểu MIME tùy chỉnh mà bạn đã định nghĩa cho một trong các kiểu hàng dữ liệu tùy chỉnh của bạn trong bảng + {@link android.provider.ContactsContract.Data}. Ví dụ, giá trị + vnd.android.cursor.item/vnd.example.locationstatus có thể là một kiểu + MIME tùy chỉnh cho một hàng dữ liệu có chức năng ghi lại vị trí được biết đến cuối cùng của một liên lạc. +
+
{@code android:icon}
+
+ Một tài nguyên + có thể vẽ của Android + mà ứng dụng danh bạ hiển thị bên cạnh dữ liệu của bạn. Sử dụng nó để thể hiện với + người dùng rằng dữ liệu xuất phát từ dịch vụ của bạn. +
+
{@code android:summaryColumn}
+
+ Tên cột của giá trị thứ nhất trong hai giá trị được truy xuất từ hàng dữ liệu. Giá trị + được hiển thị là dòng thứ nhất của mục nhập cho hàng dữ liệu này. Dòng thứ nhất có + mục đích sử dụng làm bản tóm tắt dữ liệu, nhưng điều đó là tùy chọn. Xem thêm + android:detailColumn. +
+
{@code android:detailColumn}
+
+ Tên cột của giá trị thứ hai trong hai giá trị được truy xuất từ hàng dữ liệu. Giá trị + được hiển thị là dòng thứ hai của mục nhập cho hàng dữ liệu này. Xem thêm + {@code android:summaryColumn}. +
+
+

Các Tính năng Bổ sung của Trình cung cấp Danh bạ

+

+ Bên cạnh các tính năng chính được mô tả trong các phần trước, Trình cung cấp Danh bạ còn cung cấp + những tính năng hữu ích sau khi làm việc với dữ liệu danh bạ: +

+
    +
  • Nhóm liên lạc
  • +
  • Tính năng ảnh
  • +
+

Nhóm liên lạc

+

+ Trình cung cấp Danh bạ có thể tùy chọn đánh nhãn các bộ sưu tập liên lạc có liên quan bằng dữ liệu + nhóm. Nếu máy chủ liên kết với một tài khoản người dùng + muốn duy trì nhóm, trình điều hợp đồng bộ cho loại tài khoản của tài khoản đó sẽ chuyển + dữ liệu nhóm giữa Trình cung cấp Danh bạ và máy chủ. Khi người dùng thêm một liên lạc mới vào + máy chủ, trình điều hợp đồng bộ phải thêm nhóm mới + vào bảng {@link android.provider.ContactsContract.Groups}. Nhóm hoặc các nhóm mà một liên lạc + thô thuộc về được lưu giữ trong bảng {@link android.provider.ContactsContract.Data}, bằng cách sử dụng + kiểu MIME {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}. +

+

+ Nếu bạn đang thiết kế một trình điều hợp đồng bộ mà sẽ thêm dữ liệu liên lạc thô từ + máy chủ tới Trình cung cấp Danh bạ, và bạn không sử dụng các nhóm, khi đó bạn cần báo cho + Trình cung cấp làm các dữ liệu của bạn thấy được. Trong đoạn mã được thực hiện khi một người dùng thêm một tài khoản + vào thiết bị, hãy cập nhật hàng {@link android.provider.ContactsContract.Settings} + mà Trình cung cấp Danh bạ thêm cho tài khoản. Trong hàng này, đặt giá trị của cột + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE + Settings.UNGROUPED_VISIBLE} thành 1. Khi bạn làm vậy, Trình cung cấp Danh bạ sẽ luôn + làm cho dữ liệu danh bạ của bạn thấy được, ngay cả khi bạn không sử dụng nhóm. +

+

Ảnh liên lạc

+

+ Bảng {@link android.provider.ContactsContract.Data} lưu giữ ảnh thành hàng với kiểu MIME + {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE + Photo.CONTENT_ITEM_TYPE}. Cột + {@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} của hàng được liên kết với cột + {@code android.provider.BaseColumns#_ID} của liên lạc thô mà nó thuộc về. + Lớp {@link android.provider.ContactsContract.Contacts.Photo} định nghĩa một bảng con của + {@link android.provider.ContactsContract.Contacts} chứa thông tin ảnh về ảnh chính + của một liên lạc, đây là ảnh chính của liên lạc thô chính của liên lạc. Tương tự, + lớp {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} định nghĩa một bảng con + của {@link android.provider.ContactsContract.RawContacts} chứa thông tin ảnh đối với ảnh chính + của một liên lạc thô. +

+

+ Tài liệu tham khảo cho {@link android.provider.ContactsContract.Contacts.Photo} và + {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} có các ví dụ về + việc truy xuất thông tin ảnh. Không có lớp thuận tiện cho việc truy xuất hình thu nhỏ + chính đối với một liên lạc thô, nhưng bạn có thể gửi một truy vấn tới bảng + {@link android.provider.ContactsContract.Data}, chọn + {@code android.provider.BaseColumns#_ID} của liên lạc thô, + {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE + Photo.CONTENT_ITEM_TYPE}, và cột {@link android.provider.ContactsContract.Data#IS_PRIMARY} + để tìm hàng ảnh chính của liên lạc thô. +

+

+ Dữ liệu từ luồng xã hội đối với một người cũng có thể bao gồm ảnh. Những ảnh này được lưu giữ trong bảng + {@code android.provider.ContactsContract.StreamItemPhotos}, được mô tả chi tiết hơn + trong phần Ảnh từ luồng xã hội. +

diff --git a/docs/html-intl/intl/vi/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/vi/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 0000000000000000000000000000000000000000..5f868cacf5aea33b0b795e2e405cfa7de0c01a12 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=Nội dung Cơ bản về Trình cung cấp Nội dung +@jd:body + + + +

+ Trình cung cấp nội dung quản lý truy cập vào một kho dữ liệu tập trung. Trình cung cấp + là bộ phận của một ứng dụng Android, nó thường cung cấp UI của chính mình để làm việc cùng + dữ liệu. Tuy nhiên, trình cung cấp nội dung được thiết kế chủ yếu cho các ứng dụng khác + sử dụng, giúp truy cập trình cung cấp bằng cách sử dụng một đối tượng máy khách cung cấp. Cùng nhau, trình cung cấp + và máy khách cung cấp sẽ mang đến một giao diện nhất quán, tiêu chuẩn cho dữ liệu, giao diện này + cũng đồng thời xử lý truyền thông liên tiến trình và bảo mật truy cập dữ liệu. +

+

+ Chủ đề này đề cập đến những nội dung cơ bản sau đây: +

+
    +
  • Cách trình cung cấp nội dung hoạt động.
  • +
  • API bạn sử dụng để truy xuất dữ liệu từ một trình cung cấp nội dung.
  • +
  • API bạn sử dụng để chèn, cập nhật, hoặc xóa dữ liệu trong một trình cung cấp nội dung.
  • +
  • Các tính năng API khác tạo điều kiện làm việc cùng các trình cung cấp.
  • +
+ + +

Tổng quan

+

+ Trình cung cấp nội dung trình bày dữ liệu cho các ứng dụng bên ngoài dưới dạng một hoặc nhiều bảng tương tự + như các bảng được tìm thấy trong một cơ sở dữ liệu quan hệ. Mỗi hàng thể hiện một thực thể của một số kiểu dữ liệu + mà trình cung cấp thu thập, và mỗi cột trong hàng thể hiện một phần riêng biệt của dữ liệu được thu thập + đối với một thực thể. +

+

+ Ví dụ, một trong các trình cung cấp tích hợp trong nền tảng Android đó là từ điển người dùng, nó + lưu giữ chính tả của những từ phi tiêu chuẩn mà người dùng muốn giữ lại. Bảng 1 minh họa + cách mà dữ liệu có thể được trình bày trong bảng của trình cung cấp này: +

+

+ Bảng 1: Bảng từ điển người dùng mẫu. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
từid ứng dụngtần suấtbản địa_ID
mapreduceuser1100en_US1
precompileruser14200fr_FR2
appletuser2225fr_CA3
constuser1255pt_BR4
intuser5100en_UK5
+

+ Trong bảng 1, mỗi hàng thể hiện một thực thể của một từ mà có thể không thấy có + trong từ điển chuẩn. Mỗi cột thể hiện một số dữ liệu cho từ đó, chẳng hạn như + từ bản địa được dùng lần đầu cho từ đó. Tiêu đề cột là các tên cột được lưu giữ trong + trình cung cấp. Để tham khảo tới bản địa của một hàng, bạn tham khảo tới cột locale của hàng đó. Đối với + trình cung cấp này, cột _ID đóng vai trò là cột "khóa chính" mà + trình cung cấp tự động duy trì. +

+

+ Lưu ý: Trình cung cấp không bắt buộc phải có một khóa chính, và không bắt buộc phải + sử dụng _ID làm tên cột của một khóa chính nếu có khóa. Tuy nhiên, + nếu bạn muốn gắn kết dữ liệu từ một trình cung cấp với một {@link android.widget.ListView}, một trong các + tên cột sẽ phải là _ID. Yêu cầu này được giải thích chi tiết hơn trong + phần Hiển thị các kết quả truy vấn. +

+

Truy cập một trình cung cấp

+

+ Một ứng dụng truy cập dữ liệu từ một trình cung cấp nội dung bằng + một đối tượng máy khách {@link android.content.ContentResolver}. Đối tượng này có các phương pháp để gọi + những phương pháp có tên giống nhau trong đối tượng trình cung cấp, một thực thể của một trong những lớp con + cụ thể của {@link android.content.ContentProvider}. Các phương pháp + {@link android.content.ContentResolver} cung cấp các chức năng + "CRUD" (tạo, truy xuất, cập nhật, và xóa) cơ bản của thiết bị lưu trữ liên tục. +

+

+ Đối tượng {@link android.content.ContentResolver} trong tiến trình + của ứng dụng máy khách và đối tượng {@link android.content.ContentProvider} trong ứng dụng mà sở hữu + trình cung cấp sẽ tự động xử lý truyền thông liên tiến trình. + {@link android.content.ContentProvider} cũng đóng vai trò như một lớp rút gọn giữa kho dữ liệu + của nó và biểu diễn bên ngoài của dữ liệu dưới dạng bảng. +

+

+ Lưu ý: Để truy cập một trình cung cấp, ứng dụng của bạn thường phải yêu cầu các quyền + cụ thể trong tệp bản kê khai của mình. Điều này được mô tả chi tiết hơn trong phần + Quyền của Trình cung cấp Nội dung +

+

+ Ví dụ, để có một danh sách các từ và nội dung bản địa của chúng từ Trình cung cấp Từ điển Người dùng, + bạn hãy gọi {@link android.content.ContentResolver#query ContentResolver.query()}. + Phương pháp {@link android.content.ContentResolver#query query()} sẽ gọi phương pháp + {@link android.content.ContentProvider#query ContentProvider.query()} được định nghĩa bởi + Trình cung cấp Từ điển Người dùng. Các dòng mã sau thể hiện một lệnh gọi + {@link android.content.ContentResolver#query ContentResolver.query()}: +

+

+// Queries the user dictionary and returns results
+mCursor = getContentResolver().query(
+    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
+    mProjection,                        // The columns to return for each row
+    mSelectionClause                    // Selection criteria
+    mSelectionArgs,                     // Selection criteria
+    mSortOrder);                        // The sort order for the returned rows
+
+

+ Bảng 2 cho biết các tham đối tới + {@link android.content.ContentResolver#query + query(Uri,projection,selection,selectionArgs,sortOrder)} khớp với một câu lệnh SQL SELECT như thế nào: +

+

+ Bảng 2: Query() so với truy vấn SQL. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
tham đối query()Từ khóa/tham số SELECTLưu ý
UriFROM table_nameUri ánh xạ tới bảng trong trình cung cấp có tên table_name.
projectioncol,col,col,... + projection là một mảng gồm các cột nên được đưa vào đối với mỗi hàng + được truy xuất. +
selectionWHERE col = valueselection quy định các tiêu chí để lựa chọn hàng.
selectionArgs + (Không có sự tương đương chính xác. Các tham đối lựa chọn sẽ thay thế các chỗ dành sẵn ? trong + mệnh đề lựa chọn.) +
sortOrderORDER BY col,col,... + sortOrder quy định thứ tự các hàng xuất hiện trong + {@link android.database.Cursor} được trả về. +
+

URI Nội dung

+

+ URI nội dung là một URI xác định dữ liệu trong một trình cung cấp. URI nội dung + bao gồm tên biểu tượng của toàn bộ trình cung cấp (quyền của nó) và một + tên trỏ đến một bảng (đường dẫn). Khi bạn gọi + một phương pháp máy khách để truy cập một bảng trong một trình cung cấp, URI nội dung cho bảng là một trong các + tham đối. +

+

+ Trong các dòng mã trước, hằng số + {@link android.provider.UserDictionary.Words#CONTENT_URI} chứa URI nội dung của + bảng "từ" của từ điển người dùng. Đối tượng {@link android.content.ContentResolver} + sẽ phân tích quyền của URI, và sử dụng nó để "giải quyết" trình cung cấp bằng cách + so sánh quyền với một bảng hệ thống của các trình cung cấp đã biết. Khi đó, + {@link android.content.ContentResolver} có thể phân phối các tham đối truy vấn tới đúng + trình cung cấp. +

+

+ {@link android.content.ContentProvider} sử dụng phần đường dẫn của URI nội dung nhằm chọn + bảng để truy cập. Trình cung cấp thường có một đường dẫn cho mỗi bảng mà nó hiện ra. +

+

+ Trong các dòng mã trước, URI đầy đủ cho bảng "từ" là: +

+
+content://user_dictionary/words
+
+

+ trong đó xâu user_dictionary là quyền của trình cung cấp, và +xâu words là đường dẫn của bảng. Xâu + content:// (lược đồ) sẽ luôn có mặt, + và xác định đây là một URI nội dung. +

+

+ Nhiều trình cung cấp cho phép bạn truy cập một hàng đơn lẻ trong một bảng bằng cách nối một giá trị ID + với đuôi của URI. Ví dụ, để truy xuất một hàng có _ID là + 4 từ một từ điển người dùng, bạn có thể sử dụng URI nội dung này: +

+
+Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
+
+

+ Bạn thường sử dụng các giá trị id khi bạn đã truy xuất một tập hợp các hàng, sau đó muốn cập nhật hoặc xóa + một trong số chúng. +

+

+ Lưu ý: Các lớp {@link android.net.Uri} và {@link android.net.Uri.Builder} + chứa các phương pháp thuận tiện để xây dựng đối tượng URI định dạng tốt từ các xâu. + {@link android.content.ContentUris} chứa các phương pháp thuận tiện để nối các giá trị id với + một URI. Đoạn mã HTML trước sử dụng {@link android.content.ContentUris#withAppendedId +withAppendedId()} để nối một id với URI nội dung Từ điển Người dùng. +

+ + + +

Truy xuất Dữ liệu từ Trình cung cấp

+

+ Phần này mô tả cách truy xuất dữ liệu từ một trình cung cấp bằng cách sử dụng Trình cung cấp Từ điển Người dùng + làm ví dụ. +

+

+ Để giải thích rõ, đoạn mã HTML trong phần này gọi + {@link android.content.ContentResolver#query ContentResolver.query()} trên "luồng UI"". Tuy nhiên, trong + mã thực sự, bạn nên thực hiện các truy vấn không đồng bộ trên một luồng riêng. Một cách để làm + điều này đó là sử dụng lớp {@link android.content.CursorLoader}, nó được mô tả chi tiết hơn + trong hướng dẫn + Trình tải. Bênh cạnh đó, các dòng mã chỉ là đoạn mã HTML; chúng không thể hiện một ứng dụng + hoàn chỉnh. +

+

+ Để truy xuất dữ liệu từ một trình cung cấp, hãy làm theo các bước cơ bản sau: +

+
    +
  1. + Yêu cầu quyền truy cập đọc cho trình cung cấp. +
  2. +
  3. + Định nghĩa mã để gửi một truy vấn tới trình cung cấp. +
  4. +
+

Yêu cầu quyền truy cập đọc

+

+ Để truy xuất dữ liệu từ một trình cung cấp, ứng dụng của bạn cần "quyền truy cập đọc" cho + trình cung cấp. Bạn không thể yêu cầu quyền này trong thời gian chạy; thay vào đó, bạn phải chỉ định rằng + bạn cần quyền này trong bản kê khai của mình bằng cách sử dụng phần tử +<uses-permission> + và tên quyền chính xác được định nghĩa bởi + trình cung cấp. Khi bạn chỉ định phần tử này trong bản kê khai của mình, bạn đang thực tế hóa "yêu cầu" quyền + này cho ứng dụng của mình. Khi người dùng cài đặt ứng dụng của bạn, họ ngầm hiểu cấp + yêu cầu này. +

+

+ Để tìm tên chính xác của quyền truy cập đọc cho trình cung cấp bạn đang sử dụng, cũng như + tên cho các quyền truy cập khác được sử dụng bởi trình truy cập, hãy xem trong tài liệu + của trình cung cấp. +

+

+ Vai trò của quyền trong việc truy cập các trình cung cấp được mô tả chi tiết hơn trong phần + Quyền của Trình cung cấp Nội dung. +

+

+ Trình cung cấp Từ điển Người dùng sẽ định nghĩa quyền + android.permission.READ_USER_DICTIONARY trong tệp bản kê khai của nó, vì vậy một + ứng dụng muốn đọc từ trình cung cấp sẽ phải yêu cầu quyền này. +

+ +

Xây dựng truy vấn

+

+ Bước tiếp theo trong việc truy xuất dữ liệu từ một trình cung cấp đó là xây dựng một truy vấn. Đoạn mã HTML đầu tiên + này định nghĩa một số biến cho việc truy cập Trình cung cấp Từ điển Người dùng: +

+
+
+// A "projection" defines the columns that will be returned for each row
+String[] mProjection =
+{
+    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
+    UserDictionary.Words.WORD,   // Contract class constant for the word column name
+    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
+};
+
+// Defines a string to contain the selection clause
+String mSelectionClause = null;
+
+// Initializes an array to contain selection arguments
+String[] mSelectionArgs = {""};
+
+
+

+ Đoạn mã HTML tiếp theo cho biết cách sử dụng + {@link android.content.ContentResolver#query ContentResolver.query()}, bằng cách sử dụng Trình cung cấp Từ điển + Người dùng như một ví dụ. Truy vấn máy khách trình cung cấp tương tự như một truy vấn SQL, và nó chứa một + tập hợp các cột để trả về, một tập hợp các tiêu chí lựa chọn, và một thứ tự sắp xếp. +

+

+ Tập hợp các cột mà truy vấn cần trả về được gọi là dự thảo + (biến mProjection). +

+

+ Biểu thức để chỉ định các hàng cần truy xuất sẽ được chia thành một mệnh đề lựa chọn và + tham đối lựa chọn. Mệnh đề lựa chọn là sự kết hợp giữa các biểu thức lô-gic và biểu thức Boolean, + tên cột, và giá trị (biến mSelectionClause). Nếu bạn chỉ định + tham số thay thế được ? thay vì một giá trị, phương pháp truy vấn sẽ truy xuất giá trị + từ mảng tham đối lựa chọn (biến mSelectionArgs). +

+

+ Trong đoạn mã HTML tiếp theo, nếu người dùng không điền từ thì mệnh đề lựa chọn được đặt thành + null, và truy vấn trả về tất cả các từ trong trình cung cấp. Nếu người dùng nhập + một từ, mệnh đề lựa chọn được đặt thành UserDictionary.Words.WORD + " = ?" và + phần tử đầu tiên của mảng tham đối lựa chọn được đặt thành từ mà người dùng đã nhập. +

+
+/*
+ * This defines a one-element String array to contain the selection argument.
+ */
+String[] mSelectionArgs = {""};
+
+// Gets a word from the UI
+mSearchString = mSearchWord.getText().toString();
+
+// Remember to insert code here to check for invalid or malicious input.
+
+// If the word is the empty string, gets everything
+if (TextUtils.isEmpty(mSearchString)) {
+    // Setting the selection clause to null will return all words
+    mSelectionClause = null;
+    mSelectionArgs[0] = "";
+
+} else {
+    // Constructs a selection clause that matches the word that the user entered.
+    mSelectionClause = UserDictionary.Words.WORD + " = ?";
+
+    // Moves the user's input string to the selection arguments.
+    mSelectionArgs[0] = mSearchString;
+
+}
+
+// Does a query against the table and returns a Cursor object
+mCursor = getContentResolver().query(
+    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
+    mProjection,                       // The columns to return for each row
+    mSelectionClause                   // Either null, or the word the user entered
+    mSelectionArgs,                    // Either empty, or the string the user entered
+    mSortOrder);                       // The sort order for the returned rows
+
+// Some providers return null if an error occurs, others throw an exception
+if (null == mCursor) {
+    /*
+     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
+     * call android.util.Log.e() to log this error.
+     *
+     */
+// If the Cursor is empty, the provider found no matches
+} else if (mCursor.getCount() < 1) {
+
+    /*
+     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
+     * an error. You may want to offer the user the option to insert a new row, or re-type the
+     * search term.
+     */
+
+} else {
+    // Insert code here to do something with the results
+
+}
+
+

+ Truy vấn này tương tự như câu lệnh SQL: +

+
+SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
+
+

+ Trong câu lệnh SQL này, tên cột thực tế được sử dụng thay vì các hằng số lớp hợp đồng. +

+

Bảo vệ trước mục nhập độc hại

+

+ Nếu dữ liệu được quản lý bởi trình cung cấp nội dung nằm trong một cơ sở dữ liệu SQL, việc điền dữ liệu không được tin cậy từ bên ngoài + vào các câu lệnh SQL thô có thể dẫn đến tiêm lỗi SQL. +

+

+ Hãy xét mệnh đề lựa chọn sau: +

+
+// Constructs a selection clause by concatenating the user's input to the column name
+String mSelectionClause =  "var = " + mUserInput;
+
+

+ Nếu bạn làm vậy, bạn đang cho phép người dùng ghép nối SQL độc hại lên câu lệnh SQL của mình. + Ví dụ, người dùng có thể điền "nothing; DROP TABLE *;" cho mUserInput, làm vậy + sẽ dẫn đến mệnh đề lựa chọn var = nothing; DROP TABLE *;. Do + mệnh đề lựa chọn được coi như một câu lệnh SQL, điều này có thể khiến trình cung cấp xóa tất cả + bảng trong cơ sở dữ liệu SQLite cơ bản (trừ khi trình cung cấp được thiết lập để bắt những lần thử + tiêm lỗi SQL). +

+

+ Để tránh vấn đề này, hãy sử dụng một mệnh đề lựa chọn mà sử dụng ? làm tham số + thay thế được và một mảng các tham đối lựa chọn riêng. Khi bạn làm như vậy, mục nhập của người dùng + được gắn kết trực tiếp với truy vấn thay vì được giải nghĩa như một phần của câu lệnh SQL. + Vì nó không được coi như SQL, mục nhập của người dùng không thể tiêm lỗi SQL độc hại. Thay vì sử dụng + ghép nối để điền mục nhập của người dùng, hãy sử dụng mệnh đề lựa chọn này: +

+
+// Constructs a selection clause with a replaceable parameter
+String mSelectionClause =  "var = ?";
+
+

+ Thiết lập mảng các tham đối lựa chọn như sau: +

+
+// Defines an array to contain the selection arguments
+String[] selectionArgs = {""};
+
+

+ Đặt một giá trị trong mảng các tham đối lựa chọn như sau: +

+
+// Sets the selection argument to the user's input
+selectionArgs[0] = mUserInput;
+
+

+ Mệnh đề lựa chọn mà sử dụng ? như một tham số thay thế được và một mảng + các tham đối lựa chọn là cách được ưu tiên để chỉ định một lựa chọn, ngay cả khi trình cung cấp không + được dựa trên cơ sở dữ liệu SQL. +

+ +

Hiển thị các kết quả truy vấn

+

+ Phương pháp máy khách {@link android.content.ContentResolver#query ContentResolver.query()} luôn trả về + một {@link android.database.Cursor} chứa các cột được chỉ định bởi dự thảo của + truy vấn cho các hàng khớp với các tiêu chí lựa chọn của truy vấn. Một đối tượng + {@link android.database.Cursor} cung cấp truy cập đọc ngẫu nhiên vào các hàng và cột mà nó + chứa. Bằng cách sử dụng phương pháp {@link android.database.Cursor}, bạn có thể lặp lại các hàng trong + kết quả, xác định kiểu dữ liệu của từng cột, lấy dữ liệu ra khỏi cột, và kiểm tra các tính chất khác + của kết quả. Một số triển khai {@link android.database.Cursor} sẽ tự động + cập nhật đối tượng khi dữ liệu của trình cung cấp thay đổi, hoặc kích khởi các phương pháp trong một đối tượng quan sát + khi {@link android.database.Cursor} thay đổi, hoặc cả hai. +

+

+ Lưu ý: Một trình cung cấp có thể hạn chế truy cập vào các cột dựa trên tính chất của + đối tượng thực hiện truy vấn. Ví dụ, Trình cung cấp Danh bạ hạn chế truy cập đối với một số cột cho + các trình điều hợp đồng bộ, vì thế nó sẽ không trả chúng về một hoạt động hay dịch vụ. +

+

+ Nếu không hàng nào khớp với các tiêu chí lựa chọn, trình cung cấp + sẽ trả về một đối tượng {@link android.database.Cursor} mà trong đó + {@link android.database.Cursor#getCount Cursor.getCount()} bằng 0 (con chạy trống). +

+

+ Nếu xảy ra một lỗi nội bộ, các kết quả của truy vấn sẽ phụ thuộc vào trình cung cấp cụ thể. Nó có thể + chọn trả về null, hoặc nó có thể đưa ra một lỗi {@link java.lang.Exception}. +

+

+ Do {@link android.database.Cursor} là một "danh sách" hàng, một cách hay để hiển thị + nội dung của một {@link android.database.Cursor} đó là liên kết nó với một {@link android.widget.ListView} + thông qua một {@link android.widget.SimpleCursorAdapter}. +

+

+ Đoạn mã HTML sau tiếp tục từ đoạn mã HTML trước. Nó tạo một đối tượng + {@link android.widget.SimpleCursorAdapter} chứa {@link android.database.Cursor} + được truy xuất bởi truy vấn, và đặt đối tượng này thành trình điều hợp cho một + {@link android.widget.ListView}: +

+
+// Defines a list of columns to retrieve from the Cursor and load into an output row
+String[] mWordListColumns =
+{
+    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
+    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
+};
+
+// Defines a list of View IDs that will receive the Cursor columns for each row
+int[] mWordListItems = { R.id.dictWord, R.id.locale};
+
+// Creates a new SimpleCursorAdapter
+mCursorAdapter = new SimpleCursorAdapter(
+    getApplicationContext(),               // The application's Context object
+    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
+    mCursor,                               // The result from the query
+    mWordListColumns,                      // A string array of column names in the cursor
+    mWordListItems,                        // An integer array of view IDs in the row layout
+    0);                                    // Flags (usually none are needed)
+
+// Sets the adapter for the ListView
+mWordList.setAdapter(mCursorAdapter);
+
+

+ Lưu ý: Để lùi {@link android.widget.ListView} bằng một + {@link android.database.Cursor}, con chạy phải chứa một cột có tên _ID. + Vì điều này, truy vấn được hiện lúc trước truy xuất cột _ID cho + bảng "từ" mặc dù {@link android.widget.ListView} không hiển thị nó. + Hạn chế này cũng giải thích lý do tại sao phần lớn trình cung cấp đều có một cột _ID cho mỗi + bảng của nó. +

+ + +

Lấy dữ liệu từ các kết quả truy vấn

+

+ Thay vì chỉ hiển thị các kết quả truy vấn, bạn có thể sử dụng chúng cho các tác vụ khác. Ví + dụ, bạn có thể truy xuất chính tả từ một từ điển người dùng, rồi sau đó tìm kiếm từ đó trong + các trình cung cấp khác. Để làm điều này, bạn lặp lại các hàng trong {@link android.database.Cursor}: +

+
+
+// Determine the column index of the column named "word"
+int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
+
+/*
+ * Only executes if the cursor is valid. The User Dictionary Provider returns null if
+ * an internal error occurs. Other providers may throw an Exception instead of returning null.
+ */
+
+if (mCursor != null) {
+    /*
+     * Moves to the next row in the cursor. Before the first movement in the cursor, the
+     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
+     * exception.
+     */
+    while (mCursor.moveToNext()) {
+
+        // Gets the value from the column.
+        newWord = mCursor.getString(index);
+
+        // Insert code here to process the retrieved word.
+
+        ...
+
+        // end of while loop
+    }
+} else {
+
+    // Insert code here to report an error if the cursor is null or the provider threw an exception.
+}
+
+

+ Triển khai {@link android.database.Cursor} sẽ chứa một vài phương pháp “get" để + truy xuất các kiểu dữ liệu khác nhau từ đối tượng. Ví dụ, đoạn mã HTML trước + sử dụng {@link android.database.Cursor#getString getString()}. Chúng cũng có một phương pháp + {@link android.database.Cursor#getType getType()} để trả về một giá trị cho biết + kiểu dữ liệu của cột. +

+ + + +

Quyền của Trình cung cấp Nội dung

+

+ Ứng dụng của một trình cung cấp có thể chỉ định các quyền mà ứng dụng khác có thể có để + truy cập dữ liệu của trình cung cấp đó. Những quyền này đảm bảo rằng người dùng biết một ứng dụng + sẽ cố gắng truy cập dữ liệu nào. Dựa trên các yêu cầu của trình cung cấp, các ứng dụng khác + yêu cầu quyền mà chúng cần để truy cập trình dữ liệu. Người dùng cuối thấy các quyền + được yêu cầu khi họ cài đặt ứng dụng. +

+

+ Nếu ứng dụng của một trình cung cấp không chỉ định bất kỳ quyền nào, khi đó các ứng dụng khác không có + quyền truy cập dữ liệu của trình cung cấp. Tuy nhiên, các thành phần trong ứng dụng của trình cung cấp luôn có + đầy đủ quyền truy nhập đọc và ghi, không phụ thuộc vào các quyền được chỉ định. +

+

+ Như đã lưu ý, Trình cung cấp Từ điển Người dùng sẽ yêu cầu + quyền android.permission.READ_USER_DICTIONARY để truy xuất dữ liệu từ nó. + Trình cung cấp có quyền android.permission.WRITE_USER_DICTIONARY + riêng để chèn, cập nhật, hoặc xóa dữ liệu. +

+

+ Để nhận các quyền cần để truy cập một trình cung cấp, ứng dụng yêu cầu chúng bằng một phần tử +<uses-permission> + trong tệp bản kê khai của nó. Khi Trình quản lý Gói Android cài đặt các ứng dụng, người dùng + phải phê chuẩn tất cả quyền mà ứng dụng yêu cầu. Nếu người dùng phê chuẩn tất cả quyền, khi đó + Trình quản lý Gói sẽ tiếp tục cài đặt; nếu người dùng không phê chuẩn chúng, Trình quản lý Gói sẽ + hủy bỏ việc cài đặt. +

+

+ Phần tử +<uses-permission> + sau yêu cầu quyền truy cập đọc vào Trình cung cấp Từ điển Người dùng: +

+
+    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
+
+

+ Tác động của các quyền tới việc truy cập trình cung cấp được giải thích chi tiết hơn trong + hướng dẫn Bảo mật và Quyền. +

+ + + +

Chèn, Cập nhật, và Xóa Dữ liệu

+

+ Giống như cách bạn truy xuất dữ liệu từ một trình cung cấp, bạn cũng có thể sử dụng tương tác giữa + một máy khách cung cấp và {@link android.content.ContentProvider} của trình cung cấp để sửa đổi dữ liệu. + Bạn gọi một phương pháp {@link android.content.ContentResolver} với các tham đối được chuyển sang + phương pháp {@link android.content.ContentProvider} tương ứng. Trình cung cấp và + máy khách cung cấp sẽ tự động xử lý bảo mật và truyền thông liên tiến trình. +

+

Chèn dữ liệu

+

+ Để chèn dữ liệu vào một trình cung cấp, bạn gọi phương pháp + {@link android.content.ContentResolver#insert ContentResolver.insert()} +. Phương pháp này chèn một hàng mới vào trình cung cấp và trả về một URI nội dung cho hàng đó. + Đoạn mã HTML này cho biết cách chèn một từ mới vào Trình cung cấp Từ điển Người dùng: +

+
+// Defines a new Uri object that receives the result of the insertion
+Uri mNewUri;
+
+...
+
+// Defines an object to contain the new values to insert
+ContentValues mNewValues = new ContentValues();
+
+/*
+ * Sets the values of each column and inserts the word. The arguments to the "put"
+ * method are "column name" and "value"
+ */
+mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
+mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
+mNewValues.put(UserDictionary.Words.WORD, "insert");
+mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
+
+mNewUri = getContentResolver().insert(
+    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
+    mNewValues                          // the values to insert
+);
+
+

+ Dữ liệu cho hàng mới đi vào trong một đối tượng {@link android.content.ContentValues} đơn lẻ, đối tượng này + có dạng tương tự như con chạy một hàng. Các cột trong đối tượng này không cần có + cùng kiểu dữ liệu, và nếu hoàn toàn không muốn chỉ định một giá trị, bạn có thể đặt một cột + thành null bằng cách sử dụng {@link android.content.ContentValues#putNull ContentValues.putNull()}. +

+

+ Đoạn mã HTML không thêm cột _ID vì cột này được tự động + duy trì. Trình cung cấp sẽ gán một giá trị duy nhất _ID cho mỗi hàng được + thêm. Các trình cung cấp thường sử dụng giá trị này làm khóa chính của bảng. +

+

+ URI nội dung được trả về trong newUri sẽ xác định hàng mới thêm, có + định dạng như sau: +

+
+content://user_dictionary/words/<id_value>
+
+

+ <id_value> là nội dung của _ID cho hàng mới. + Hầu hết các trình cung cấp đều có thể tự động phát hiện dạng URI nội dung này rồi thực hiện thao tác được yêu cầu + trên hàng cụ thể đó. +

+

+ Để nhận giá trị _ID từ {@link android.net.Uri} được trả về, hãy gọi + {@link android.content.ContentUris#parseId ContentUris.parseId()}. +

+

Cập nhật dữ liệu

+

+ Để cập nhật một hàng, bạn sử dụng một đối tượng {@link android.content.ContentValues} với các giá trị + được cập nhật giống như cách bạn làm với việc chèn, và các tiêu chí lựa chọn giống như cách bạn làm với truy vấn. + Phương pháp máy khách mà bạn sử dụng là + {@link android.content.ContentResolver#update ContentResolver.update()}. Bạn chỉ cần thêm + các giá trị vào đối tượng {@link android.content.ContentValues} cho các cột mà bạn đang cập nhật. Nếu bạn + muốn xóa các nội dung của một cột, hãy đặt giá trị thành null. +

+

+ Đoạn mã HTML sau thay đổi tất cả hàng với cột bản địa có ngôn ngữ "en" thành cột + có bản địa là null. Giá trị trả về là số hàng đã được cập nhật: +

+
+// Defines an object to contain the updated values
+ContentValues mUpdateValues = new ContentValues();
+
+// Defines selection criteria for the rows you want to update
+String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
+String[] mSelectionArgs = {"en_%"};
+
+// Defines a variable to contain the number of updated rows
+int mRowsUpdated = 0;
+
+...
+
+/*
+ * Sets the updated value and updates the selected words.
+ */
+mUpdateValues.putNull(UserDictionary.Words.LOCALE);
+
+mRowsUpdated = getContentResolver().update(
+    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
+    mUpdateValues                       // the columns to update
+    mSelectionClause                    // the column to select on
+    mSelectionArgs                      // the value to compare to
+);
+
+

+ Bạn cũng nên thanh lọc thông tin đầu vào của người dùng khi gọi + {@link android.content.ContentResolver#update ContentResolver.update()}. Để tìm hiểu thêm về + điều này, hãy đọc phần Bảo vệ trước mục nhập độc hại. +

+

Xóa dữ liệu

+

+ Xóa hàng tương tự như truy xuất dữ liệu hàng: bạn chỉ định các tiêu chí lựa chọn cho hàng + mà bạn muốn xóa và phương pháp máy khách trả về số hàng được xóa. + Đoạn mã HTML sau xóa các hàng có appid khớp với "user". Phương pháp sẽ trả về + số hàng được xóa. +

+
+
+// Defines selection criteria for the rows you want to delete
+String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
+String[] mSelectionArgs = {"user"};
+
+// Defines a variable to contain the number of rows deleted
+int mRowsDeleted = 0;
+
+...
+
+// Deletes the words that match the selection criteria
+mRowsDeleted = getContentResolver().delete(
+    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
+    mSelectionClause                    // the column to select on
+    mSelectionArgs                      // the value to compare to
+);
+
+

+ Bạn cũng nên thanh lọc thông tin đầu vào của người dùng khi gọi + {@link android.content.ContentResolver#delete ContentResolver.delete()}. Để tìm hiểu thêm về + điều này, hãy đọc phần Bảo vệ trước mục nhập độc hại. +

+ +

Các Kiểu Dữ liệu của Trình cung cấp

+

+ Trình cung cấp nội dung có thể cung cấp nhiều kiểu dữ liệu khác nhau. Trình cung cấp Từ điển Người dùng chỉ cung cấp + văn bản, nhưng trình cung cấp cũng có thể cung cấp các định dạng sau: +

+
    +
  • + integer +
  • +
  • + long integer (long) +
  • +
  • + floating point +
  • +
  • + long floating point (double) +
  • +
+

+ Một kiểu dữ liệu khác mà các trình cung cấp thường sử dụng đó là Binary Large OBject (BLOB) được triển khai như một + mảng 64KB byte. Bạn có thể xem các kiểu dữ liệu có sẵn bằng cách xem các phương pháp "get" lớp + {@link android.database.Cursor}. +

+

+ Kiểu dữ liệu đối với mỗi cột trong một trình cung cấp thường được liệt kê trong tài liệu của trình cung cấp đó. + Các kiểu dữ liệu dành cho Trình cung cấp Từ điển Người dùng được liệt kê trong tài liệu tham khảo + cho lớp hợp đồng {@link android.provider.UserDictionary.Words} của nó (lớp hợp đồng được + mô tả trong phần Các Lớp Hợp đồng). + Bạn cũng có thể xác định kiểu dữ liệu bằng cách gọi {@link android.database.Cursor#getType + Cursor.getType()}. +

+

+ Trình cung cấp cũng duy trì thông tin về kiểu dữ liệu MIME cho mỗi URI nội dung mà chúng định nghĩa. Bạn có thể + sử dụng thông tin về kiểu MIME để tìm hiểu xem ứng dụng của mình có thể xử lý dữ liệu mà + trình cung cấp đưa ra hay không, hoặc để chọn một kiểu xử lý dựa trên kiểu MIME. Bạn thường cần kiểu + MIME khi đang làm việc với một trình cung cấp chứa các cấu trúc hoặc tệp + dữ liệu phức tạp. Ví dụ, bảng {@link android.provider.ContactsContract.Data} + trong Trình cung cấp Danh bạ sử dụng các kiểu MIME để dán nhãn kiểu dữ liệu liên lạc được lưu trữ trong từng + hàng. Để nhận được kiểu MIME tương ứng với một URI nội dung, hãy gọi + {@link android.content.ContentResolver#getType ContentResolver.getType()}. +

+

+ Phần Tham khảo Kiểu MIME mô tả + cú pháp của cả kiểu MIME tiêu chuẩn lẫn tùy chỉnh. +

+ + + +

Các Hình thức Truy cập Trình cung cấp Thay thế

+

+ Có ba hình thức truy cập trình cung cấp thay thế quan trọng trong phát triển ứng dụng: +

+
    +
  • + Truy cập hàng loạt: Bạn có thể tạo một loạt lệnh gọi truy cập bằng các phương pháp trong + lớp {@link android.content.ContentProviderOperation}, rồi sau đó áp dụng chúng với + {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. +
  • +
  • + Truy vấn không đồng bộ: Bạn nên thực hiện các truy vấn trong một luồng riêng. Một cách để làm điều này đó là + sử dụng một đối tượng {@link android.content.CursorLoader}. Các ví dụ trong + hướng dẫn Trình tải sẽ minh họa + cách làm điều này. +
  • +
  • + Truy cập dữ liệu thông qua ý định: Mặc dù không thể gửi một ý định + trực tiếp tới một trình cung cấp, bạn có thể gửi một ý định tới ứng dụng của trình cung cấp đó, + đây thường là cách tốt nhất để sửa đổi dữ liệu của trình cung cấp. +
  • +
+

+ Truy cập hàng loạt và sửa đổi thông qua ý định được mô tả trong các phần sau. +

+

Truy cập hàng loạt

+

+ Truy cập hàng loạt vào một trình cung cấp là cách hữu ích để chèn nhiều hàng, hoặc để chèn + các hàng vào nhiều bảng trong cùng lệnh gọi phương pháp, hoặc nhìn chung để thực hiện một tập hợp + thao tác qua các ranh giới tiến trình như một giao tác (thao tác nguyên tử). +

+

+ Để truy cập một trình cung cấp trong "chế độ hàng loạt", + bạn tạo một mảng đối tượng {@link android.content.ContentProviderOperation} rồi + phân phối chúng tới một trình cung cấp nội dung bằng + {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()}. Bạn chuyển + quyền của trình cung cấp nội dung cho phương pháp này thay vì một URI nội dung cụ thể. +Điều này cho phép đối tượng {@link android.content.ContentProviderOperation} trong mảng có tác dụng + đối với một bảng khác. Một lệnh gọi tới {@link android.content.ContentResolver#applyBatch + ContentResolver.applyBatch()} trả về một mảng kết quả. +

+

+ Mô tả lớp hợp đồng {@link android.provider.ContactsContract.RawContacts} + bao gồm một đoạn mã HTML thể hiện việc chèn hàng loạt. Ứng dụng mẫu + Trình quản lý Danh bạ + có một ví dụ về truy cập hàng loạt trong tệp nguồn ContactAdder.java + của nó. +

+ +

Truy cập dữ liệu thông qua ý định

+

+ Ý định có thể cho phép truy cập gián tiếp vào một trình cung cấp nội dung. Bạn cho phép người dùng truy cập + dữ liệu trong một trình cung cấp ngay cả khi ứng dụng của bạn không có quyền truy cập, hoặc bằng cách + nhận lại một ý định kết quả từ một ứng dụng có quyền, hoặc bằng cách kích hoạt một + ứng dụng có phép và cho phép người dùng được làm việc trong nó. +

+

Được truy cập với các quyền tạm thời

+

+ Bạn có thể truy cập dữ liệu trong một trình cung cấp nội dung, ngay cả khi bạn không có quyền + truy nhập phù hợp, bằng cách gửi một ý định tới một ứng dụng có quyền và + nhận lại một ý định kết quả chứa quyền "URI". + Đây là những quyền cho một URI nội dung cụ thể kéo dài tới khi hoạt động nhận chúng + được hoàn thành. Ứng dụng có quyền lâu dài sẽ cấp quyền tạm thời + bằng cách đặt một cờ trong ý định kết quả: +

+
    +
  • + Quyền đọc: + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} +
  • +
  • + Quyền ghi: + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} +
  • +
+

+ Lưu ý: Những cờ này không cấp quyền truy cập đọc và ghi nói chung cho trình cung cấp + mà có quyền được chứa trong URI nội dung. Quyền truy cập này chỉ áp dụng cho chính URI đó. +

+

+ Trình cung cấp sẽ định nghĩa quyền URI cho các URI nội dung trong bản kê khai của nó, bằng cách sử dụng thuộc tính +android:grantUriPermission + của phần tử +<provider> + cũng như phần tử con +<grant-uri-permission> + của phần tử +<provider> + . Cơ chế cấp quyền URI này được giải thích chi tiết hơn trong + hướng dẫn Bảo mật và Quyền, + trong phần "Quyền URI". +

+

+ Ví dụ, bạn có thể truy xuất dữ liệu cho một liên lạc trong Trình cung cấp Danh bạ, ngay cả khi bạn không + có quyền {@link android.Manifest.permission#READ_CONTACTS}. Bạn có thể muốn thực hiện điều này + trong một ứng dụng gửi thiệp mừng điện tử tới một liên lạc vào ngày sinh nhật của người đó. Thay vì + yêu cầu {@link android.Manifest.permission#READ_CONTACTS}, là nơi cấp cho bạn quyền truy cập tất cả liên lạc + của người dùng và tất cả thông tin của họ, bạn nên cho phép người dùng kiểm soát những liên lạc + nào được sử dụng bởi ứng dụng của bạn. Để làm điều này, bạn sử dụng tiến trình sau: +

+
    +
  1. + Ứng dụng của bạn gửi một ý định chứa hành động + {@link android.content.Intent#ACTION_PICK} và kiểu MIME "danh bạ" +{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}, bằng cách sử dụng + phương pháp {@link android.app.Activity#startActivityForResult + startActivityForResult()}. +
  2. +
  3. + Vì ý định này khớp với bộ lọc ý định cho hoạt động + "lựa chọn" của ứng dụng Danh bạ, hoạt động sẽ đi đến tiền cảnh. +
  4. +
  5. + Trong hoạt động lựa chọn, người dùng chọn một + liên lạc để cập nhật. Khi điều này xảy ra, hoạt động lựa chọn sẽ gọi + {@link android.app.Activity#setResult setResult(resultcode, intent)} + để thiết lập một ý định nhằm gửi lại ứng dụng của bạn. Ý định chứa URI nội dung + của liên lạc mà người dùng đã chọn, và các cờ "phụ thêm" + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}. Những cờ này cấp quyền URI + cho ứng dụng của bạn để đọc dữ liệu cho liên lạc được trỏ đến bởi + URI nội dung. Sau đó, hoạt động lựa chọn gọi {@link android.app.Activity#finish()} để + trả kiểm soát về ứng dụng của bạn. +
  6. +
  7. + Hoạt động của bạn trả về tiền cảnh, và hệ thống sẽ gọi phương pháp + {@link android.app.Activity#onActivityResult onActivityResult()} + của hoạt động của bạn. Phương pháp này nhận được ý định kết quả do hoạt động lựa chọn tạo trong + ứng dụng Danh bạ. +
  8. +
  9. + Với URI nội dung từ ý định kết quả, bạn có thể đọc dữ liệu của liên lạc + từ Trình cung cấp Danh bạ, ngay cả khi bạn không yêu cầu quyền truy cập đọc lâu dài + vào trình cung cấp trong bản kê khai của mình. Sau đó, bạn có thể nhận thông tin ngày sinh của liên lạc + hoặc địa chỉ e-mail của người đó rồi gửi thiệp mừng điện tử. +
  10. +
+

Sử dụng một ứng dụng khác

+

+ Một cách đơn giản để cho phép người dùng sửa đổi dữ liệu mà bạn không có quyền truy cập đó là + kích hoạt một ứng dụng có quyền và cho phép người dùng làm việc ở đó. +

+

+ Ví dụ, ứng dụng Lịch chấp nhận một + ý định {@link android.content.Intent#ACTION_INSERT}, nó cho phép bạn kích hoạt UI chèn + của ứng dụng. Bạn có thể chuyển dữ liệu "phụ thêm" trong ý định này mà được ứng dụng sử dụng + để điền trước vào UI. Vì các sự kiện định kỳ có cú pháp phức tạp, cách + ưu tiên để chèn sự kiện vào Trình cung cấp Lịch đó là kích hoạt ứng dụng Lịch với một + {@link android.content.Intent#ACTION_INSERT} rồi để người dùng chèn sự kiện tại đó. +

+ +

Các Lớp Hợp đồng

+

+ Lớp hợp đồng định nghĩa các hằng số sẽ giúp ứng dụng hoạt động với các URI nội dung, tên + cột, hành động ý định, và các tính năng khác của một trình cung cấp nội dung. Các lớp hợp đồng không + được tự động đưa vào cùng một trình cung cấp; nhà phát triển của trình cung cấp phải định nghĩa chúng rồi + cung cấp chúng cho các nhà phát triển khác. Nhiều trình cung cấp được bao gồm cùng với nền tảng + Android có các lớp hợp đồng tương ứng trong gói {@link android.provider}. +

+

+ Ví dụ, Trình cung cấp Từ điển Người dùng có một lớp hợp đồng + {@link android.provider.UserDictionary} chứa các hằng số URI nội dung và tên cột. URI nội dung + đối với bảng "từ" được định nghĩa trong hằng số + {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI}. + Lớp {@link android.provider.UserDictionary.Words} cũng chứa các hằng số tên cột, + chúng được sử dụng trong đoạn mã HTML mẫu trong hướng dẫn này. Ví dụ, một dự thảo truy vấn có thể được + định nghĩa là: +

+
+String[] mProjection =
+{
+    UserDictionary.Words._ID,
+    UserDictionary.Words.WORD,
+    UserDictionary.Words.LOCALE
+};
+
+

+ Một lớp hợp đồng khác là {@link android.provider.ContactsContract} dành cho Trình cung cấp Danh bạ. + Tài liệu tham khảo cho lớp này bao gồm các đoạn mã HTML mẫu. Một trong số các + lớp con của nó, {@link android.provider.ContactsContract.Intents.Insert}, là một lớp hợp đồng + chứa các hằng số cho ý định và dữ liệu ý định. +

+ + + +

Tham khảo Kiểu MIME

+

+ Trình cung cấp nội dung có thể trả về các kiểu phương tiện MIME tiêu chuẩn, hoặc xâu kiểu MIME tùy chỉnh, hoặc cả hai. +

+

+ Các kiểu MIME có định dạng +

+
+type/subtype
+
+

+ Ví dụ, kiểu MIME thông dụng text/html có kiểu text và + kiểu con html. Nếu trình cung cấp trả về loại này cho một URI, điều đó có nghĩa rằng một + truy vấn đang sử dụng URI đó sẽ trả về văn bản chứa thẻ HTML. +

+

+ Các xâu kiểu MIME tùy chỉnh, còn gọi là kiểu MIME "theo nhà cung cấp", có các giá trị + kiểukiểu con phức tạp hơn. Giá trị kiểu luôn luôn +

+
+vnd.android.cursor.dir
+
+

+ áp dụng cho nhiều hàng, hoặc +

+
+vnd.android.cursor.item
+
+

+ áp dụng cho một hàng. +

+

+ Giá trị kiểu con áp dụng theo trình cung cấp. Các trình cung cấp được tích hợp trong Android thường có một kiểu con + đơn giản. Ví dụ, khi ứng dụng Danh bạ tạo một hàng cho một số điện thoại, + nó đặt kiểu MIME sau trong hàng: +

+
+vnd.android.cursor.item/phone_v2
+
+

+ Để ý rằng giá trị kiểu con đơn giản là phone_v2. +

+

+ Các nhà phát triển trình cung cấp khác có thể tạo mẫu hình kiểu con của riêng mình dựa trên quyền + và tên bảng của trình cung cấp. Ví dụ, xét một trình cung cấp chứa các biểu thời gian lịch tàu. + Quyền của trình cung cấp là com.example.trains, và nó chứa các bảng + Line1, Line2, và Line3. Để phản hồi lại URI nội dung +

+

+

+content://com.example.trains/Line1
+
+

+ đối với bảng Line1, trình cung cấp trả về kiểu MIME +

+
+vnd.android.cursor.dir/vnd.example.line1
+
+

+ Để phản hồi lại URI nội dung +

+
+content://com.example.trains/Line2/5
+
+

+ đối với hàng 5 trong bảng Line2, trình cung cấp trả về kiểu MIME +

+
+vnd.android.cursor.item/vnd.example.line2
+
+

+ Hầu hết các trình cung cấp nội dung đều định nghĩa hằng số lớp hợp đồng cho các kiểu MIME mà chúng sử dụng. Ví dụ như lớp hợp đồng + của Trình cung cấp Danh bạ {@link android.provider.ContactsContract.RawContacts}, + sẽ định nghĩa hằng số + {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} cho kiểu MIME của + một hàng liên lạc thô duy nhất. +

+

+ Các URI nội dung đối với hàng duy nhất được mô tả trong phần + URI nội dung. +

diff --git a/docs/html-intl/intl/vi/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/vi/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..2e8579a1e92eff57d75cc3cc8e37f3457f6de54f --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=Tạo một Trình cung cấp Nội dung +@jd:body + + + +

+ Trình cung cấp nội dung quản lý truy cập vào một kho dữ liệu tập trung. Bạn triển khai một + trình cung cấp thành một hoặc nhiều lớp trong một ứng dụng Android, bên cạnh các phần tử trong + tệp bản kê khai. Một trong các lớp của bạn triển khai một lớp con + {@link android.content.ContentProvider}, đây là giao diện giữa trình cung cấp của bạn và + các ứng dụng khác. Mặc dù mục đích của các trình cung cấp nội dung khác là cung cấp dữ liệu có sẵn cho các + ứng dụng khác, dĩ nhiên bạn có thể ra lệnh cho các hoạt động trong ứng dụng của mình + truy vấn và sửa đổi dữ liệu được quản lý bởi trình cung cấp của bạn. +

+

+ Phần còn lại của chủ đề này là một danh sách cơ bản về các bước để xây dựng một trình cung cấp nội dung và một danh sách + các API để sử dụng. +

+ + + +

Trước khi Bạn Bắt đầu Xây dựng

+

+ Trước khi bạn bắt đầu xây dựng một trình cung cấp, hãy làm việc sau: +

+
    +
  1. + Quyết định xem bạn có cần một trình cung cấp nội dung không. Bạn cần xây dựng một trình cung cấp + nội dung nếu muốn cung cấp một hoặc nhiều tính năng sau đây: +
      +
    • Bạn muốn cung cấp dữ liệu hoặc tệp phức tạp cho các ứng dụng khác.
    • +
    • Bạn muốn cho phép người dùng sao chép dữ liệu phức tạp từ ứng dụng của bạn vào các ứng dụng khác.
    • +
    • Bạn muốn cung cấp các gợi ý tìm kiếm tùy chỉnh bằng cách sử dụng khuôn khổ tìm kiếm.
    • +
    +

    + Bạn không cần trình cung cấp phải sử dụng một cơ sở dữ liệu SQLite nếu việc sử dụng hoàn toàn + diễn ra trong ứng dụng của bạn. +

    +
  2. +
  3. + Nếu bạn chưa làm như vậy, hãy đọc chủ đề + + Nội dung Cơ bản về Trình cung cấp Nội dung để tìm hiểu thêm về trình cung cấp. +
  4. +
+

+ Tiếp theo, hãy làm theo những bước sau để xây dựng trình cung cấp của bạn: +

+
    +
  1. + Thiết kế kho lưu trữ thô cho dữ liệu của bạn. Một trình cung cấp nội dung sẽ cung cấp dữ liệu theo hai cách: +
    +
    + Dữ liệu tệp +
    +
    + Dữ liệu mà thường đến các tệp chẳng hạn như + ảnh, âm thanh, hoặc video. Lưu trữ các tệp ở không gian + riêng tư trong ứng dụng của bạn. Để hồi đáp lại một yêu cầu tệp từ một ứng dụng khác, trình cung cấp + của bạn có thể cung cấp một núm điều tác cho tệp. +
    +
    + Dữ liệu "cấu trúc" +
    +
    + Dữ liệu mà thường đến một cơ sở dữ liệu, mảng, hoặc cấu trúc tương tự. + Lưu trữ dữ liệu dưới dạng tương thích với các bảng hàng cột. Hàng + biểu diễn một đối tượng, chẳng hạn như một người hoặc khoản mục trong kiểm kê. Cột biểu diễn + một số dữ liệu cho đối tượng, chẳng hạn như tên của một người hoặc giá của một khoản mục. Một cách thường dùng để + lưu trữ loại dữ liệu này đó là trong cơ sở dữ liệu SQLite, nhưng bạn có thể sử dụng bất kỳ loại + kho lưu trữ lâu dài nào. Để tìm hiểu thêm về các loại kho lưu trữ có sẵn trong + hệ thống Android, hãy xem phần + Thiết kế Kho lưu trữ Dữ liệu. +
    +
    +
  2. +
  3. + Định nghĩa một triển khai cụ thể của lớp {@link android.content.ContentProvider} và + các phương pháp được yêu cầu của nó. Lớp này là giao diện giữa dữ liệu của bạn và phần còn lại của + hệ thống Android. Để biết thêm thông tin về lớp này, hãy xem phần + Triển khai Lớp ContentProvider. +
  4. +
  5. + Định nghĩa xâu thẩm quyền của trình cung cấp, URI nội dung của nó, và các tên cột. Nếu bạn muốn + ứng dụng của trình cung cấp xử lý các ý định, hãy định nghĩa các hành động ý định, dữ liệu phụ thêm, + và cờ. Đồng thời, hãy định nghĩa các quyền mà bạn sẽ yêu cầu cho những ứng dụng muốn + truy cập dữ liệu của bạn. Bạn nên cân nhắc định nghĩa tất cả những giá trị này là hằng số trong một + lớp riêng; sau đó, bạn có thể cho hiện lớp này ra với các nhà phát triển khác. Để biết thêm + thông tin về URI nội dung, hãy xem + phần Thiết kế URI Nội dung. + Để biết thêm thông tin về ý định, hãy xem + phần Ý định và Truy cập Dữ liệu. +
  6. +
  7. + Thêm các nội dung tùy chọn khác, chẳng hạn như dữ liệu mẫu hoặc triển + khai {@link android.content.AbstractThreadedSyncAdapter} mà có thể đồng bộ hoá dữ liệu giữa + trình cung cấp và dữ liệu nền đám mây. +
  8. +
+ + + +

Thiết kế Kho lưu trữ Dữ liệu

+

+ Trình cung cấp nội dung là giao diện đối với dữ liệu được lưu theo một định dạng cấu trúc. Trước khi tạo + giao diện, bạn phải quyết định cách lưu trữ dữ liệu. Bạn có thể lưu trữ dữ liệu theo bất kỳ dạng nào + mà bạn muốn rồi thiết kế giao diện để đọc và ghi dữ liệu nếu cần thiết. +

+

+ Có một số công nghệ lưu trữ dữ liệu có sẵn trong Android: +

+
    +
  • + Hệ thống Android bao gồm một API cơ sở dữ liệu SQLite mà các trình cung cấp của chính Androi sử dụng + để lưu trữ dữ liệu theo định hướng bảng. Lớp + {@link android.database.sqlite.SQLiteOpenHelper} giúp bạn tạo cơ sở dữ liệu, và lớp + {@link android.database.sqlite.SQLiteDatabase} là lớp cơ bản để đánh giá + các cơ sở dữ liệu. +

    + Nhớ rằng bạn không phải sử dụng một cơ sở dữ liệu để triển khai kho lưu giữ của mình. Bề ngoài, một trình cung cấp + có dạng như là một tập hợp bảng, tương tự như một cơ sở dữ liệu quan hệ, nhưng đây + không phải là một yêu cầu đối với việc triển khai nội bộ của trình cung cấp. +

    +
  • +
  • + Để lưu trữ dữ liệu tệp, Android có nhiều API định hướng tệp khác nhau. + Để tìm hiểu thêm về lưu trữ tệp, hãy đọc chủ đề + Kho lưu trữ Dữ liệu. Nếu bạn + đang thiết kế một trình cung cấp dữ liệu liên quan tới phương tiện chẳng hạn như nhạc hay video, bạn có thể + có một trình cung cấp cho phép kết hợp dữ liệu bảng và các tệp. +
  • +
  • + Để làm việc với dữ liệu trên nền mạng, hãy sử dụng các lớp trong {@link java.net} và + {@link android.net}. Bạn cũng có thể đồng bộ hoá dữ liệu trên nền mạng với một kho lưu trữ dữ liệu cục bộ + chẳng hạn như một cơ sở dữ liệu, rồi cung cấp dữ liệu dưới dạng bảng hoặc tệp. + Ứng dụng mẫu + Trình điều hợp Đồng bộ Mẫu minh họa loại đồng bộ hoá này. +
  • +
+

+ Những nội dung cần xem xét khi thiết kế dữ liệu +

+

+ Sau đây là một số mẹo để thiết kế cấu trúc dữ liệu cho trình cung cấp của bạn: +

+
    +
  • + Dữ liệu bảng nên luôn có một cột "khóa chính" mà trình cung cấp duy trì + như một giá trị số duy nhất cho mỗi hàng. Bạn có thể sử dụng giá trị này để liên kết hàng với các hàng + có liên quan trong các bảng khác (sử dụng nó làm "khóa ngoại"). Mặc dù bạn có thể sử dụng bất kỳ tên gọi nào + cho cột này, sử dụng {@link android.provider.BaseColumns#_ID BaseColumns._ID} là lựa chọn tốt nhất + vì việc liên kết các kết quả của một truy vấn trình cung cấp với + {@link android.widget.ListView} đòi hỏi một trong các cột được truy xuất phải có tên + _ID. +
  • +
  • + Nếu bạn muốn cung cấp các hình ảnh bitmap hoặc nội dung dữ liệu định hướng tệp rất lớn khác, hãy lưu trữ + dữ liệu vào một tệp rồi cung cấp nó gián tiếp thay vì lưu trữ nó trực tiếp trong một + bảng. Nếu làm vậy, bạn cần báo cho người dùng trình cung cấp của bạn rằng họ cần sử dụng một phương pháp tệp + {@link android.content.ContentResolver} để truy cập dữ liệu. +
  • +
  • + Sử dụng kiểu dữ liệu Binary Large OBject (BLOB) để lưu trữ dữ liệu có kích cỡ khác nhau hoặc có một + cấu trúc thay đổi. Ví dụ, bạn có thể sử dụng cột BLOB để lưu trữ một + bộ đệm giao thức hay + cấu trúc JSON. +

    + Bạn cũng có thể sử dụng một BLOB để triển khai một bảng độc lập với sơ đồ. Trong + kiểu bảng này, bạn định nghĩa một cột khóa chính, một cột kiểu MIME, và một hoặc + nhiều cột chung là BLOB. Ý nghĩa của dữ liệu trong cột BLOB được thể hiện + bởi giá trị trong cột kiểu MIME. Điều này cho phép bạn lưu trữ các kiểu hàng khác nhau trong + cùng bảng. Bảng "dữ liệu" + {@link android.provider.ContactsContract.Data} của Trình cung cấp Danh bạ là một ví dụ về bảng + độc lập với sơ đồ. +

    +
  • +
+ +

Thiết kế URI Nội dung

+

+ URI nội dung là một URI xác định dữ liệu trong một trình cung cấp. URI nội dung bao gồm + tên mang tính biểu tượng của toàn bộ trình cung cấp (quyền của nó) và một + tên trỏ đến một bảng hoặc tệp (đường dẫn). Phần id tùy chọn chỉ đến một + hàng riêng lẻ trong một bảng. Mọi phương thức truy cập dữ liệu + {@link android.content.ContentProvider} đều có một URI nội dung là một tham đối; điều này cho phép bạn + xác định bảng, hàng, hoặc tệp để truy cập. +

+

+ Nội dung cơ bản của URI nội dung được mô tả trong chủ đề + + Nội dung Cơ bản về Trình cung cấp Nội dung. +

+

Thiết kế một thẩm quyền

+

+ Một trình cung cấp thường có một thẩm quyền duy nhất, đóng vai trò là tên nội bộ Android của nó. Để + tránh xung đột với các trình cung cấp khác, bạn nên sử dụng quyền sở hữu miền Internet (đảo ngược) + làm cơ sở cho thẩm quyền của trình cung cấp của mình. Vì đề xuất này cũng đúng đối với tên gói + Android, bạn có thể định nghĩa thẩm quyền trình cung cấp của mình là phần mở rộng của tên + gói chứa trình cung cấp. Ví dụ, nếu tên gói Android là + com.example.<appname>, bạn nên cấp cho trình cung cấp của mình + thẩm quyền com.example.<appname>.provider. +

+

Thiết kế một cấu trúc đường dẫn

+

+ Nhà phát triển thường tạo URI nội dung từ thẩm quyền bằng cách nối các đường dẫn trỏ đến + các bảng riêng lẻ. Ví dụ, nếu bạn có hai bảng table1 và + table2, bạn kết hợp thẩm quyền từ ví dụ trước để tạo ra + các URI nội dung + com.example.<appname>.provider/table1 và + com.example.<appname>.provider/table2. Các đường dẫn + không bị giới hạn ở một phân đoạn duy nhất, và không cần phải có một bảng cho từng cấp của đường dẫn. +

+

Xử lý ID URI nội dung

+

+ Theo quy ước, các trình cung cấp cho phép truy cập một hàng đơn trong một bảng bằng cách chấp nhận một URI nội dung + có một giá trị ID cho hàng đó ở cuối URI. Cũng theo quy ước, các trình cung cấp sẽ so khớp + giá trị ID với cột _ID của bảng, và thực hiện truy cập yêu cầu đối với + hàng trùng khớp. +

+

+ Quy ước này tạo điều kiện cho một kiểu mẫu thiết kế chung cho các ứng dụng truy cập một trình cung cấp. Ứng dụng + tiến hành truy vấn đối với trình cung cấp và hiển thị kết quả {@link android.database.Cursor} + trong một {@link android.widget.ListView} bằng cách sử dụng {@link android.widget.CursorAdapter}. + Định nghĩa {@link android.widget.CursorAdapter} yêu cầu một trong các cột trong + {@link android.database.Cursor} phải là _ID +

+

+ Sau đó, người dùng chọn một trong các hàng được hiển thị từ UI để xem hoặc sửa đổi + dữ liệu. Ứng dụng sẽ nhận được hàng tương ứng từ {@link android.database.Cursor} làm nền cho + {@link android.widget.ListView}, nhận giá trị _ID cho hàng này, nối nó với + URI nội dung, và gửi yêu cầu truy cập tới trình cung cấp. Sau đó, trình cung cấp có thể thực hiện + truy vấn hoặc sửa đổi đối với chính xác hàng mà người dùng đã chọn. +

+

Kiểu mẫu URI nội dung

+

+ Để giúp bạn chọn hành động nào sẽ thực hiện cho URI nội dung đến, API của trình cung cấp sẽ bao gồm + lớp thuận tiện {@link android.content.UriMatcher}, nó ánh xạ "kiểu mẫu" URI nội dung với + các giá trị số nguyên. Bạn có thể sử dụng các giá trị số nguyên trong một câu lệnh switch mà chọn + hành động mong muốn cho URI nội dung hoặc URI mà khớp với một kiểu mẫu cụ thể. +

+

+ Kiểu mẫu URI nội dung sẽ so khớp các URI nội dung bằng cách sử dụng ký tự đại diện: +

+
    +
  • + *: Khớp một xâu ký tự hợp lệ bất kỳ với chiều dài bất kỳ. +
  • +
  • + #: Khớp một xâu ký tự số có chiều dài bất kỳ. +
  • +
+

+ Lấy một ví dụ về thiết kế và tạo mã xử lý URI nội dung, hãy xét một trình cung cấp có + thẩm quyền com.example.app.provider mà nhận ra các URI nội dung + trỏ đến các bảng sau: +

+
    +
  • + content://com.example.app.provider/table1: Một bảng gọi là table1. +
  • +
  • + content://com.example.app.provider/table2/dataset1: Một bảng gọi là + dataset1. +
  • +
  • + content://com.example.app.provider/table2/dataset2: Một bảng gọi là + dataset2. +
  • +
  • + content://com.example.app.provider/table3: Một bảng gọi là table3. +
  • +
+

+ Trình cung cấp cũng nhận ra những URI nội dung này nếu chúng có một ID hàng được nối kèm, như + ví dụ content://com.example.app.provider/table3/1 đối với hàng được nhận biết bởi + 1 trong table3. +

+

+ Sẽ có thể có các kiểu mẫu URI nội dung sau: +

+
+
+ content://com.example.app.provider/* +
+
+ Khớp với bất kỳ URI nội dung nào trong trình cung cấp. +
+
+ content://com.example.app.provider/table2/*: +
+
+ Khớp với một URI nội dung cho các bảng dataset1 + và dataset2, nhưng không khớp với URI nội dung cho table1 hoặc + table3. +
+
+ content://com.example.app.provider/table3/#: Khớp với một URI nội dung + cho các hàng đơn trong table3, chẳng hạn như + content://com.example.app.provider/table3/6 đối với hàng được xác định bởi + 6. +
+
+

+ Đoạn mã HTML sau cho biết cách hoạt động của các phương pháp trong {@link android.content.UriMatcher}. + Đoạn mã này xử lý các URI cho toàn bộ một bảng khác với URI cho một + hàng đơn, bằng cách sử dụng mẫu hình URI nội dung + content://<authority>/<path> cho các bảng, và + content://<authority>/<path>/<id> cho các hàng đơn. +

+

+ Phương pháp {@link android.content.UriMatcher#addURI(String, String, int) addURI()} ánh xạ một + thẩm quyền và đường dẫn tới một giá trị số nguyên. Phương pháp {@link android.content.UriMatcher#match(Uri) + match()} trả về giá trị số nguyên cho một URI. Câu lệnh switch sẽ chọn + giữa truy vấn toàn bộ bảng và truy vấn cho một bản ghi đơn: +

+
+public class ExampleProvider extends ContentProvider {
+...
+    // Creates a UriMatcher object.
+    private static final UriMatcher sUriMatcher;
+...
+    /*
+     * The calls to addURI() go here, for all of the content URI patterns that the provider
+     * should recognize. 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
+     */
+    sUriMatcher.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.
+     */
+    sUriMatcher.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 (sUriMatcher.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 is not recognized, you should do some error handling here.
+        }
+        // call the code to actually do the query
+    }
+
+

+ Một lớp khác, {@link android.content.ContentUris}, sẽ cung cấp các phương pháp thuận tiện để làm việc + với phần id của URI nội dung. Các lớp {@link android.net.Uri} và + {@link android.net.Uri.Builder} bao gồm các phương pháp thuận tiện cho việc phân tích các đối tượng + {@link android.net.Uri} hiện có và xây dựng các đối tượng mới. +

+ + +

Triển khai Lớp Trình cung cấp Nội dung

+

+ Thực thể {@link android.content.ContentProvider} quản lý truy cập vào + một tập dữ liệu cấu trúc bằng cách xử lý yêu cầu từ các ứng dụng khác. Tất cả các dạng + truy cập cuối cùng đều gọi {@link android.content.ContentResolver}, sau đó nó gọi ra một phương pháp + cụ thể của {@link android.content.ContentProvider} để lấy quyền truy cập. +

+

Phương pháp được yêu cầu

+

+ Lớp tóm tắt {@link android.content.ContentProvider} sẽ định nghĩa sáu phương pháp tóm tắt + mà bạn phải triển khai như một phần lớp con cụ thể của mình. Tất cả những phương pháp này ngoại trừ + {@link android.content.ContentProvider#onCreate() onCreate()} đều được gọi ra bởi một ứng dụng máy khách + đang cố truy cập trình cung cấp nội dung của bạn: +

+
+
+ {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + query()} +
+
+ Truy xuất dữ liệu từ trình cung cấp của bạn. Sử dụng các tham đối để chọn bảng để + truy vấn, các hàng và cột để trả về, và thứ tự sắp xếp của kết quả. + Trả về dữ liệu như một đối tượng {@link android.database.Cursor}. +
+
+ {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} +
+
+ Chèn một hàng mới vào trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn + bảng đích và nhận các giá trị cột để sử dụng. Trả về một URI nội dung cho + hàng mới chèn. +
+
+ {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} +
+
+ Cập nhật các hàng hiện tại trong trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn bảng và hàng + để cập nhật và nhận các giá trị cột được cập nhật. Trả về số hàng được cập nhật. +
+
+ {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +
+
+ Xóa hàng khỏi trình cung cấp của bạn. Sử dụng các tham đối để lựa chọn bảng và các hàng + cần xóa. Trả về số hàng được xóa. +
+
+ {@link android.content.ContentProvider#getType(Uri) getType()} +
+
+ Trả về kiểu MIME tương ứng với một URI nội dung. Phương pháp này được mô tả chi tiết hơn + trong phần Triển khai Kiểu MIME của Trình cung cấp Nội dung. +
+
+ {@link android.content.ContentProvider#onCreate() onCreate()} +
+
+ Khởi tạo trình cung cấp của bạn. Hệ thống Android sẽ gọi ra phương pháp này ngay lập tức sau khi nó + tạo trình cung cấp của bạn. Để ý rằng trình cung cấp của bạn không được tạo cho đến khi đối tượng + {@link android.content.ContentResolver} cố truy cập nó. +
+
+

+ Để ý rằng những phương pháp này có cùng chữ ký như các phương pháp + {@link android.content.ContentResolver} được đặt tên như nhau. +

+

+ Việc bạn triển khai những phương pháp này nên xét tới các nội dung sau: +

+
    +
  • + Tất cả phương pháp này ngoại trừ {@link android.content.ContentProvider#onCreate() onCreate()} + đều có thể được gọi đồng thời bằng nhiều luồng, vì thế chúng phải an toàn đối với luồng. Để tìm hiểu + thêm về nhiều luồng, hãy xem chủ đề + + Tiến trình và Luồng. +
  • +
  • + Tránh thực hiện những thao tác dài trong {@link android.content.ContentProvider#onCreate() + onCreate()}. Hoãn các tác vụ khởi tạo tới khi chúng thực sự cần thiết. + Phần Triển khai phương pháp onCreate() + sẽ bàn kỹ hơn về vấn đề này. +
  • +
  • + Mặc dù bạn phải triển khai những phương pháp này, mã của bạn không nhất thiết phải làm gì ngoại trừ việc + trả về kiểu dữ liệu kỳ vọng. Ví dụ, bạn có thể muốn ngăn những ứng dụng khác + chèn dữ liệu vào một số bảng. Để làm điều này, bạn có thể bỏ qua lệnh gọi tới + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} và trả về + 0. +
  • +
+

Triển khai phương pháp query()

+

+ Phương pháp + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} phải trả về một đối tượng {@link android.database.Cursor}, nếu không nó sẽ thất bại +, đưa ra một lỗi {@link java.lang.Exception}. Nếu bạn đang sử dụng một cơ sở dữ liệu SQLite làm kho lưu trữ dữ liệu của mình +, bạn có thể chỉ cần trả về {@link android.database.Cursor} được trả về bởi một trong các phương pháp + query() của lớp {@link android.database.sqlite.SQLiteDatabase}. + Nếu truy vấn không khớp với bất kỳ hàng nào, bạn nên trả về một thực thể {@link android.database.Cursor} + có phương pháp {@link android.database.Cursor#getCount()} trả về 0. + Bạn chỉ nên trả về null nếu đã xảy ra một lỗi nội bộ trong tiến trình truy vấn. +

+

+ Nếu bạn không đang sử dụng một cơ sở dữ liệu SQLite làm kho lưu trữ dữ liệu của mình, hãy sử dụng một trong các lớp con cụ thể + của {@link android.database.Cursor}. Ví dụ, lớp {@link android.database.MatrixCursor} sẽ triển khai + một con chạy trong đó mỗi hàng là một mảng của {@link java.lang.Object}. Với lớp này, + hãy sử dụng {@link android.database.MatrixCursor#addRow(Object[]) addRow()} để thêm một hàng mới. +

+

+ Nhớ rằng hệ thống Android phải có thể giao tiếp với {@link java.lang.Exception} + qua các ranh giới tiến trình. Android có thể làm vậy cho những trường hợp ngoại lệ sau, điều này có thể hữu ích + trong xử lý lỗi truy vấn: +

+
    +
  • + {@link java.lang.IllegalArgumentException} (Bạn có thể chọn đưa ra lỗi này nếu trình cung cấp của bạn + nhận một URI nội dung không hợp lệ) +
  • +
  • + {@link java.lang.NullPointerException} +
  • +
+

Triển khai phương pháp insert()

+

+ Phương pháp {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} sẽ thêm một + hàng mới vào bảng phù hợp bằng cách sử dụng các giá trị trong tham đối {@link android.content.ContentValues} +. Nếu tên cột không nằm trong tham đối {@link android.content.ContentValues}, bạn có thể + muốn cung cấp một giá trị mặc định cho nó hoặc trong mã trình cung cấp của bạn hoặc trong sơ đồ + cơ sở dữ liệu của bạn. +

+

+ Phương pháp này sẽ trả về URI nội dung cho hàng mới. Để xây dựng điều này, hãy nối + giá trị _ID của hàng mới (hay khóa chính khác) với URI nội dung của bảng bằng cách sử dụng + {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()}. +

+

Triển khai phương pháp delete()

+

+ Phương pháp {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} + không cần phải xóa hàng thực chất khỏi kho lưu trữ dữ liệu của bạn. Nếu bạn đang sử dụng một trình điều hợp đồng bộ + với trình cung cấp của mình, bạn nên cân nhắc đánh dấu một hàng đã xóa + bằng cờ "xóa" thay vì gỡ bỏ hàng một cách hoàn toàn. Trình điều hợp đồng bộ có thể + kiểm tra các hàng đã xóa và gỡ bỏ chúng khỏi máy chủ trước khi xóa chúng khỏi trình cung cấp. +

+

Triển khai phương pháp update()

+

+ Phương pháp {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} lấy cùng tham đối {@link android.content.ContentValues} được sử dụng bởi + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()}, và + cùng tham đối selectionselectionArgs được sử dụng bởi + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} và + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()}. Điều này có thể cho phép bạn sử dụng lại mã giữa những phương pháp này. +

+

Triển khai phương pháp onCreate()

+

+ Hệ thống Android sẽ gọi {@link android.content.ContentProvider#onCreate() + onCreate()} khi nó khởi động trình cung cấp. Bạn chỉ nên thực hiện các tác vụ khởi tạo chạy nhanh + trong phương pháp này, và hoãn việc tạo cơ sở dữ liệu và nạp dữ liệu tới khi trình cung cấp thực sự + nhận được yêu cầu cho dữ liệu. Nếu bạn thực hiện các tác vụ dài trong + {@link android.content.ContentProvider#onCreate() onCreate()}, bạn sẽ làm chậm lại + quá trình khởi động của trình cung cấp. Đến lượt mình, điều này sẽ làm chậm hồi đáp từ trình cung cấp đối với các + ứng dụng khác. +

+

+ Ví dụ, nếu bạn đang sử dụng một cơ sở dữ liệu SQLite, bạn có thể tạo + một đối tượng {@link android.database.sqlite.SQLiteOpenHelper} mới trong + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}, + rồi tạo các bảng SQL lần đầu tiên khi bạn mở cơ sở dữ liệu. Để tạo điều kiện cho điều này, + lần đầu tiên bạn gọi {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase + getWritableDatabase()}, nó sẽ tự động gọi ra phương pháp + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()}. +

+

+ Hai đoạn mã HTML sau minh họa tương tác giữa + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} và + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()}. Đoạn mã HTML đầu tiên là triển khai + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}: +

+
+public class ExampleProvider extends ContentProvider
+
+    /*
+     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
+     * in a following snippet.
+     */
+    private MainDatabaseHelper mOpenHelper;
+
+    // Defines the database name
+    private static final String DBNAME = "mydb";
+
+    // Holds the database object
+    private SQLiteDatabase db;
+
+    public boolean onCreate() {
+
+        /*
+         * Creates a new helper object. This method always returns quickly.
+         * Notice that the database itself isn't created or opened
+         * until SQLiteOpenHelper.getWritableDatabase is called
+         */
+        mOpenHelper = new MainDatabaseHelper(
+            getContext(),        // the application context
+            DBNAME,              // the name of the database)
+            null,                // uses the default SQLite cursor
+            1                    // the version number
+        );
+
+        return true;
+    }
+
+    ...
+
+    // Implements the provider's insert method
+    public Cursor insert(Uri uri, ContentValues values) {
+        // Insert code here to determine which table to open, handle error-checking, and so forth
+
+        ...
+
+        /*
+         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
+         *
+         */
+        db = mOpenHelper.getWritableDatabase();
+    }
+}
+
+

+ Đoạn mã HTML tiếp theo là triển khai + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()}, bao gồm một lớp trình trợ giúp: +

+
+...
+// A string that defines the SQL statement for creating a table
+private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
+    "main " +                       // Table's name
+    "(" +                           // The columns in the table
+    " _ID INTEGER PRIMARY KEY, " +
+    " WORD TEXT"
+    " FREQUENCY INTEGER " +
+    " LOCALE TEXT )";
+...
+/**
+ * Helper class that actually creates and manages the provider's underlying data repository.
+ */
+protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
+
+    /*
+     * Instantiates an open helper for the provider's SQLite data repository
+     * Do not do database creation and upgrade here.
+     */
+    MainDatabaseHelper(Context context) {
+        super(context, DBNAME, null, 1);
+    }
+
+    /*
+     * Creates the data repository. This is called when the provider attempts to open the
+     * repository and SQLite reports that it doesn't exist.
+     */
+    public void onCreate(SQLiteDatabase db) {
+
+        // Creates the main table
+        db.execSQL(SQL_CREATE_MAIN);
+    }
+}
+
+ + + +

Triển khai Kiểu MIME của Trình cung cấp Nội dung

+

+ Lớp {@link android.content.ContentProvider} có hai phương pháp để trả về các kiểu MIME: +

+
+
+ {@link android.content.ContentProvider#getType(Uri) getType()} +
+
+ Một trong các phương pháp được yêu cầu mà bạn phải triển khai cho bất kỳ trình cung cấp nào. +
+
+ {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +
+
+ Một phương pháp mà bạn được dự tính sẽ triển khai nếu trình cung cấp của bạn cung cấp tệp. +
+
+

Kiểu MIME cho bảng

+

+ Phương pháp {@link android.content.ContentProvider#getType(Uri) getType()} trả về một + {@link java.lang.String} theo định dạng MIME mà mô tả kiểu dữ liệu được trả về bởi tham đối + URI nội dung. Tham đối {@link android.net.Uri} có thể là một mẫu hình thay vì một URI cụ thể; + trong trường hợp này, bạn nên trả về kiểu dữ liệu được liên kết với các URI nội dung mà khớp với + mẫu hình đó. +

+

+ Đối với các kiểu dữ liệu phổ biến như văn bản, HTML, hay JPEG, + {@link android.content.ContentProvider#getType(Uri) getType()} sẽ trả về + kiểu MIME tiêu chuẩn cho dữ liệu đó. Một danh sách đầy đủ về những kiểu tiêu chuẩn này có sẵn trên trang web + IANA MIME Media Types +. +

+

+ Đối với các URI nội dung mà trỏ tới một hàng hoặc các hàng của bảng dữ liệu, + {@link android.content.ContentProvider#getType(Uri) getType()} sẽ trả về + một kiểu MIME theo định dạng MIME riêng cho nhà cung cấp của Android: +

+
    +
  • + Bộ phận kiểu: vnd +
  • +
  • + Bộ phận kiểu con: +
      +
    • + Nếu mẫu hình URI áp dụng cho một hàng đơn: android.cursor.item/ +
    • +
    • + Nếu mẫu hình URI áp dụng cho nhiều hơn một hàng: android.cursor.dir/ +
    • +
    +
  • +
  • + Bộ phận riêng theo trình cung cấp: vnd.<name>.<type> +

    + Bạn cung cấp <name><type>. + Giá trị <name> nên là giá trị duy nhất toàn cục, + và giá trị <type> nên là giá trị duy nhất đối với mẫu hình + URI tương ứng. Một lựa chọn hay cho <name> đó là tên công ty của bạn hoặc + một thành phần nào đó trong tên gói Android cho ứng dụng của bạn. Một lựa chọn hay cho + <type> đó là một xâu xác định bảng được liên kết với + URI. +

    + +
  • +
+

+ Ví dụ, nếu thẩm quyền của một trình cung cấp là + com.example.app.provider, và nó làm hiện ra một bảng có tên + table1 thì kiểu MIME cho nhiều hàng trong table1 là: +

+
+vnd.android.cursor.dir/vnd.com.example.provider.table1
+
+

+ Đối với một hàng đơn của table1, kiểu MIME là: +

+
+vnd.android.cursor.item/vnd.com.example.provider.table1
+
+

Kiểu MIME cho tệp

+

+ Nếu trình cung cấp của bạn cung cấp tệp, hãy triển khai + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}. + Phương pháp này sẽ trả về một mảng {@link java.lang.String} của kiểu MIME đối với các tệp mà trình cung cấp của bạn + có thể trả về cho một URI nội dung cho trước. Bạn nên lọc các kiểu MIME mà mình cung cấp bằng tham đối bộ lọc + kiểu MIME, sao cho bạn chỉ trả về những kiểu MIME mà máy khách muốn xử lý. +

+

+ Ví dụ, xét một trình cung cấp hình ảnh dưới dạng tệp có định dạng .jpg, + .png.gif. + Nếu một ứng dụng gọi {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} bằng xâu bộ lọc image/* ( + mà là một "hình ảnh"), + khi đó phương pháp {@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} sẽ trả về mảng: +

+
+{ "image/jpeg", "image/png", "image/gif"}
+
+

+ Nếu ứng dụng chỉ quan tâm đến các tệp .jpg, vậy nó có thể gọi + {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} bằng xâu bộ lọc *\/jpeg, và + {@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} sẽ trả về: +

+{"image/jpeg"}
+
+

+ Nếu trình cung cấp của bạn không cung cấp bất kỳ kiểu MIME nào được yêu cầu trong xâu bộ lọc, + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} + sẽ trả về null. +

+ + + +

Triển khai một Lớp Hợp đồng

+

+ Lớp hợp đồng là một lớp public final chứa các định nghĩa hằng số cho + URI, tên cột, kiểu MIME, và siêu dữ liệu khác liên quan tới trình cung cấp. Lớp này + sẽ thiết lập một hợp đồng giữa trình cung cấp và các ứng dụng khác bằng cách đảm bảo rằng trình cung cấp + có thể được truy cập đúng ngay cả khi có thay đổi về giá trị thực sự của URI, tên cột, + v.v. +

+

+ Lớp hợp đồng cũng giúp các nhà phát triển vì chúng thường có tên dễ nhớ cho các hằng số của mình, + vì vậy các nhà phát triển ít có khả năng sử dụng các giá trị không đúng cho tên cột hay URI hơn. Do đó là một + lớp, nó có thể chứa tài liệu Javadoc. Các môi trường phát triển tích hợp như + Eclipse có thể tự động điền các tên hằng số từ lớp hợp đồng và hiển thị Javadoc cho các + hằng số đó. +

+

+ Các nhà phát triển không thể truy cập tệp lớp của lớp hợp đồng từ ứng dụng của mình, nhưng họ có thể + lặng lẽ biên dịch nó vào ứng dụng của họ từ một tệp .jar mà bạn cung cấp. +

+

+ Lớp {@link android.provider.ContactsContract} và các lớp lồng nhau của nó là các ví dụ về + lớp hợp đồng. +

+

Triển khai Quyền của Trình cung cấp Nội dung

+

+ Quyền và truy cập đối với tất cả khía cạnh trong hệ thống Android được mô tả chi tiết trong + chủ đề Bảo mật và Quyền. + Chủ đề Kho lưu trữ Dữ liệu cũng + mô tả bảo mật và các quyền có hiệu lực cho nhiều loại kho lưu trữ khác nhau. + Nói tóm lại, các điểm quan trọng là: +

+
    +
  • + Theo mặc định, các tệp dữ liệu được lưu trữ trên bộ nhớ trong của thiết bị là dữ liệu riêng tư + đối với ứng dụng và trình cung cấp của bạn. +
  • +
  • + Các cơ sở dữ liệu {@link android.database.sqlite.SQLiteDatabase} mà bạn tạo là dữ liệu riêng tư + đối với ứng dụng và trình cung cấp của bạn. +
  • +
  • + Theo mặc định, các tệp dữ liệu mà bạn lưu vào bộ nhớ ngoài là dữ liệu công khai và + đọc được công khai. Bạn không thể sử dụng một trình cung cấp nội dung để hạn chế truy cập vào các tệp trong + bộ nhớ ngoài vì các ứng dụng khác có thể sử dụng lệnh gọi API khác để đọc và ghi chúng. +
  • +
  • + Các lệnh gọi phương pháp để mở hoặc tạo tệp hoặc cơ sở dữ liệu SQLite trên bộ nhớ trong + của thiết bị của bạn có thể cấp quyền truy cập đọc và ghi cho tất cả ứng dụng khác. Nếu bạn + sử dụng một tệp hoặc cơ sở dữ liệu nội bộ làm kho lưu giữ của trình cung cấp của mình, và bạn cấp quyền truy cập + "đọc được công khai" hoặc "ghi được công khai", quyền mà bạn đặt cho trình cung cấp của mình trong + bản kê khai của nó sẽ không bảo vệ dữ liệu của bạn. Quyền truy cập mặc định cho các tệp và cơ sở dữ liệu trong + bộ nhớ trong là "riêng tư", và đối với kho lưu giữ của trình cung cấp của mình, bạn không nên thay đổi điều này. +
  • +
+

+ Nếu bạn muốn sử dụng các quyền của trình cung cấp nội dung để kiểm soát truy cập vào dữ liệu của mình, khi đó bạn nên + lưu trữ dữ liệu của mình trong các tệp nội bộ, cơ sở dữ liệu SQLite, hoặc "đám mây" (ví dụ, + trên một máy chủ từ xa), và bạn nên giữ các tệp và cơ sở dữ liệu riêng tư cho ứng dụng của mình. +

+

Triển khai quyền

+

+ Tất cả ứng dụng đều có thể đọc từ hoặc ghi vào trình cung cấp của bạn, ngay cả khi dữ liệu liên quan + là dữ liệu riêng tư, vì theo mặc định, trình cung cấp của bạn không được đặt quyền. Để thay đổi điều này, + hãy đặt quyền cho trình cung cấp của bạn trong tệp bản kê khai của bạn bằng cách sử dụng các thuộc tính hoặc phần tử + con của phần tử + <provider>. Bạn có thể đặt quyền áp dụng cho toàn bộ trình cung cấp, + hoặc cho một số bảng, hoặc thậm chí cho một số bản ghi, hoặc cả ba. +

+

+ Bạn định nghĩa các quyền cho trình cung cấp của bạn bằng một hoặc nhiều phần tử + + <permission> trong tệp bản kê khai của bạn. Để + quyền là duy nhất cho trình cung cấp của bạn, hãy sử dụng phạm vi kiểu Java cho thuộc tính + + android:name. Ví dụ, đặt tên quyền đọc + com.example.app.provider.permission.READ_PROVIDER. + +

+

+ Danh sách sau liệt kê phạm vi các quyền của trình cung cấp, bắt đầu với các quyền + áp dụng cho toàn bộ trình cung cấp rồi mới đến các quyền chi tiết hơn. + Các quyền chi tiết hơn được ưu tiên so với các quyền có phạm vi rộng hơn: +

+
+
+ Quyền đọc-ghi đơn lẻ ở cấp trình cung cấp +
+
+ Một quyền kiểm soát cả quyền truy cập đọc và ghi cho toàn bộ trình cung cấp, được quy định + bằng thuộc tính + android:permission của phần tử + + <provider>. +
+
+ Quyền đọc ghi tách riêng ở cấp độ trình cung cấp +
+
+ Một quyền đọc và một quyền ghi cho toàn bộ trình cung cấp. Bạn chỉ định chúng + bằng các thuộc tính + android:readPermission và + + android:writePermission của phần tử + + <provider>. Chúng được ưu tiên so với quyền được yêu cầu bởi + + android:permission. +
+
+ Quyền ở cấp đường dẫn +
+
+ Quyền đọc, ghi, hoặc đọc/ghi cho một URI nội dung trong trình cung cấp của bạn. Bạn chỉ định + từng URI mà bạn muốn kiểm soát bằng một phần tử con + + <path-permission> của phần tử + + <provider>. Với mỗi một URI nội dung mà bạn chỉ định, bạn có thể chỉ định một + quyền đọc/ghi, quyền đọc, hoặc quyền ghi, hoặc cả ba. Quyền đọc và + quyền ghi được ưu tiên so với quyền đọc/ghi. Đồng thời, quyền ở cấp độ đường dẫn + sẽ được ưu tiên so với quyền ở cấp độ trình cung cấp. +
+
+ Quyền tạm thời +
+
+ Là cấp độ quyền cho phép truy cập tạm thời vào một ứng dụng, ngay cả khi ứng dụng + không có các quyền thường được yêu cầu. Tính năng truy cập + tạm thời làm giảm số quyền mà một ứng dụng phải yêu cầu trong + bản kê khai của mình. Khi bạn dùng đến các quyền tạm thời, những ứng dụng duy nhất mà cần + quyền "lâu dài" cho trình cung cấp của bạn là những ứng dụng liên tục truy cập tất cả + dữ liệu của bạn. +

+ Xét các quyền bạn cần để triển khai một trình cung cấp và ứng dụng e-mail khi bạn + muốn cho phép một ứng dụng trình xem ảnh bên ngoài hiển thị các tài liệu đính kèm dạng ảnh từ trình cung cấp + của bạn. Để cấp cho trình xem ảnh quyền truy cập cần thiết mà không cần yêu cầu quyền, + hãy thiết lập các quyền tạm thời cho URI nội dung đối với ảnh. Thiết kế ứng dụng e-mail của bạn sao cho + khi người dùng muốn hiển thị một ảnh, ứng dụng sẽ gửi một ý định chứa URI nội dung + của ảnh và cờ cho phép tới trình xem ảnh. Trình xem ảnh khi đó có thể + truy vấn trình cung cấp e-mail của bạn để truy xuất ảnh, ngay cả khi trình xem không + có quyền đọc bình thường cho trình cung cấp của bạn. +

+

+ Để sử dụng các quyền tạm thời, hoặc đặt thuộc tính + + android:grantUriPermissions của phần tử + + <provider> hoặc thêm một hoặc nhiều phần tử con + + <grant-uri-permission> vào phần tử + + <provider> của bạn. Nếu bạn sử dụng các quyền tạm thời, bạn phải gọi + {@link android.content.Context#revokeUriPermission(Uri, int) + Context.revokeUriPermission()} bất cứ khi nào bạn gỡ bỏ hỗ trợ cho một URI nội dung khỏi + trình cung cấp của mình, và URI nội dung đó sẽ được liên kết với một quyền tạm thời. +

+

+ Giá trị của thuộc tính sẽ xác định trình cung cấp của bạn được cho phép truy cập bao nhiêu. + Nếu thuộc tính được đặt thành true, khi đó hệ thống sẽ cấp quyền tạm thời + cho toàn bộ trình cung cấp của bạn, khống chế mọi quyền khác mà được yêu cầu bởi + quyền ở cấp độ trình cung cấp hoặc cấp độ đường dẫn của bạn. +

+

+ Nếu cờ này được đặt thành false, khi đó bạn phải thêm các phần tử con + + <grant-uri-permission> vào phần tử + + <provider> của mình. Mỗi phần tử con lại quy định URI nội dung hoặc + các URI mà truy cập tạm thời được cấp cho. +

+

+ Để ủy quyền truy cập tạm thời cho một ứng dụng, ý định phải chứa + cờ {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} hoặc cờ + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, hoặc cả hai. Những quyền + này được đặt bằng phương pháp {@link android.content.Intent#setFlags(int) setFlags()}. +

+

+ Nếu thuộc tính + android:grantUriPermissions không có mặt, giả sử rằng nó là + false. +

+
+
+ + + + +

Phần tử <provider>

+

+ Như các thành phần {@link android.app.Activity} và {@link android.app.Service}, + một lớp con của {@link android.content.ContentProvider} + phải được định nghĩa trong tệp bản kê khai cho ứng dụng của nó bằng cách sử dụng phần tử + + <provider>. Hệ thống Android nhận thông tin sau từ + phần tử: +

+
+ Thẩm quyền + ({@code + android:authorities}) +
+
+ Các tên biểu tượng nhận biết toàn bộ trình cung cấp trong hệ thống. Thuộc tính + này được mô tả chi tiết hơn trong phần + Thiết kế URI Nội dung. +
+
+ Tên lớp của trình cung cấp + ( +android:name + ) +
+
+ Lớp triển khai {@link android.content.ContentProvider}. Lớp này + được mô tả chi tiết hơn trong phần + Triển khai Lớp Trình cung cấp Nội dung. +
+
+ Quyền +
+
+ Những thuộc tính quy định quyền mà các ứng dụng khác phải có để truy cập + dữ liệu của trình cung cấp: + +

+ Các quyền và thuộc tính tương ứng của chúng được mô tả chi tiết hơn trong + phần + Triển khai Quyền của Trình cung cấp Nội dung. +

+
+
+ Thuộc tính khởi động và kiểm soát +
+
+ Những thuộc tính này xác định cách và thời điểm hệ thống Android khởi động trình cung cấp, các + đặc tính tiến trình của trình cung cấp, và các thiết đặt về thời gian chạy: +
    +
  • + + android:enabled: Cờ cho phép hệ thống khởi động trình cung cấp. +
  • +
  • + + android:exported: Cờ cho phép các ứng dụng sử dụng trình cung cấp này. +
  • +
  • + + android:initOrder: Thứ tự mà trình cung cấp nên được khởi động, + so với các trình cung cấp khác trong cùng tiến trình. +
  • +
  • + + android:multiProcess: Cờ cho phép hệ thống khởi động trình cung cấp + trong cùng tiến trình như máy khách gọi. +
  • +
  • + + android:process: Tên của tiến trình mà trình cung cấp + nên chạy trong đó. +
  • +
  • + + android:syncable: Cờ cho biết rằng dữ liệu của trình cung cấp sẽ được + đồng bộ với dữ liệu trên một máy chủ. +
  • +
+

+ Các thuộc tính được lập tài liệu theo dõi đầy đủ trong chủ đề hướng dẫn nhà phát triển đối với phần tử + + <provider> +. +

+
+
+ Các thuộc tính thông tin +
+
+ Một biểu tượng tùy chọn và nhãn cho trình cung cấp: +
    +
  • + + android:icon: Một tài nguyên có thể vẽ chứa một biểu tượng cho trình cung cấp. + Biểu tượng xuất hiện bên cạnh nhãn của trình cung cấp trong danh sách ứng dụng trong + Settings > Apps > All. +
  • +
  • + + android:label: Một nhãn thông tin mô tả trình cung cấp hoặc dữ liệu + của nó, hoặc cả hai. Nhãn xuất hiện trong danh sách ứng dụng trong + Settings > Apps > All. +
  • +
+

+ Các thuộc tính được lập tài liệu theo dõi đầy đủ trong chủ đề hướng dẫn nhà phát triển đối với phần tử + + <provider>. +

+
+
+ + +

Ý định và Truy cập Dữ liệu

+

+ Các ứng dụng có thể gián tiếp truy cập một trình cung cấp nội dung bằng một {@link android.content.Intent}. + Ứng dụng không gọi bất kỳ phương pháp nào của {@link android.content.ContentResolver} hoặc + {@link android.content.ContentProvider}. Thay vào đó, nó sẽ gửi một ý định để bắt đầu một hoạt động, + đây thường là một bộ phận trong ứng dụng của chính trình cung cấp. Hoạt động đích phụ trách + truy xuất và hiển thị dữ liệu trong UI của nó. Tùy vào hành động trong ý định, hoạt động + đích cũng có thể nhắc người dùng thực hiện sửa đổi dữ liệu của trình cung cấp. + Một ý định cũng có thể chứa dữ liệu "phụ thêm" mà hoạt động đích hiển thị + trong UI; khi đó người dùng có tùy chọn thay đổi dữ liệu này trước khi sử dụng nó để sửa đổi + dữ liệu trong trình cung cấp. +

+

+ +

+

+ Bạn có thể muốn sử dụng truy cập ý định để giúp đảm bảo toàn vẹn dữ liệu. Trình cung cấp của bạn có thể phụ thuộc vào + việc chèn, cập nhật và xóa dữ liệu theo lô-gic nghiệp vụ được quy định chặt chẽ. Trong + trường hợp như vậy, việc cho phép các ứng dụng khác trực tiếp sửa đổi dữ liệu của bạn có thể dẫn đến dữ liệu + không hợp lệ. Nếu bạn muốn các nhà phát triển sử dụng truy cập ý định, hãy đảm bảo lập tài liệu theo dõi nó thật kỹ. + Giải thích với họ tại sao truy cập ý định sử dụng UI ứng dụng của chính bạn lại tốt hơn là cố gắng sửa đổi + dữ liệu bằng mã của họ. +

+

+ Việc xử lý một ý định đến nhằm sửa đổi dữ liệu của trình cung cấp của bạn không khác với + việc xử lý các ý định khác. Bạn có thể tìm hiểu về việc sử dụng ý định bằng cách đọc chủ đề + Ý định và Bộ lọc Ý định. +

diff --git a/docs/html-intl/intl/vi/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/vi/guide/topics/providers/content-providers.jd new file mode 100644 index 0000000000000000000000000000000000000000..0b0233705d879bc9aa9a9deee542240bee124499 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=Trình cung cấp Nội dung +@jd:body + +

+ Trình cung cấp nội dung quản lý truy cập vào một tập dữ liệu cấu trúc. Chúng gói gọn + dữ liệu và cung cấp các cơ chế để định nghĩa bảo mật dữ liệu. Trình cung cấp nội dung là + giao diện tiêu chuẩn kết nối dữ liệu trong một tiến trình với mã đang chạy trong một tiến trình khác. +

+

+ Khi bạn truy cập dữ liệu trong một trình cung cấp nội dung, bạn sử dụng đối tượng + {@link android.content.ContentResolver} trong + {@link android.content.Context} của ứng dụng của bạn để giao tiếp với trình cung cấp như một máy khách. + Đối tượng {@link android.content.ContentResolver} giao tiếp với đối tượng trình cung cấp, một + thực thể của lớp triển khai {@link android.content.ContentProvider}. Đối tượng + trình cung cấp nhận các yêu cầu dữ liệu từ máy khách, thực hiện hành động được yêu cầu, và + trả về kết quả. +

+

+ Bạn không cần phát triển trình cung cấp của chính mình nếu không có ý định chia sẻ dữ liệu của bạn với + các ứng dụng khác. Tuy nhiên, bạn cần phải có trình cung cấp của chính mình để cung cấp các gợi ý tìm kiếm tùy chỉnh + trong ứng dụng của chính bạn. Bạn cũng cần phải có trình cung cấp của chính mình nếu muốn sao chép và + dán dữ liệu hoặc tệp phức tạp từ ứng dụng của bạn sang các ứng dụng khác. +

+

+ Bản thân Android bao gồm các trình cung cấp nội dung chuyên quản lý dữ liệu như âm thanh, video, hình ảnh và + thông tin liên lạc cá nhân. Bạn có thể thấy một số được liệt kê trong tài liệu + tham khảo cho gói + android.provider + . Với một số hạn chế, những trình cung cấp này có thể truy cập vào bất kỳ ứng dụng + Android nào. +

+ Các chủ đề sau mô tả chi tiết hơn về các trình cung cấp nội dung: +

+
+
+ + Nội dung Cơ bản về Trình cung cấp Nội dung +
+
+ Cách truy cập dữ liệu trong một trình cung cấp nội dung khi dữ liệu được tổ chức dưới dạng bảng. +
+
+ + Tạo một Trình cung cấp Nội dung +
+
+ Cách tạo trình cung cấp nội dung của chính bạn. +
+
+ + Trình cung cấp Lịch +
+
+ Cách truy cập Trình cung cấp Lịch mà là một bộ phận của nền tảng Android. +
+
+ + Trình cung cấp Danh bạ +
+
+ Cách truy cập Trình cung cấp Danh bạ mà là một bộ phận của nền tảng Android. +
+
diff --git a/docs/html-intl/intl/vi/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/vi/guide/topics/providers/document-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..30844d7c277e2639b6107ff5b2f3af0ebbd2e2f5 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=Khuôn khổ Truy cập Kho lưu trữ +@jd:body + + + +

Android 4.4 (API mức 19) giới thiệu Khuôn khổ Truy cập Kho lưu trữ (SAF). SAF + giúp người dùng đơn giản hóa việc duyệt và mở tài liệu, hình ảnh và các tệp khác +giữa tất cả trình cung cấp lưu trữ tài liệu mà họ thích. UI tiêu chuẩn, dễ sử dụng +cho phép người dùng duyệt tệp và truy cập hoạt động gần đây một cách nhất quán giữa các ứng dụng và trình cung cấp.

+ +

Dịch vụ lưu trữ đám mây hoặc cục bộ có thể tham gia vào hệ sinh thái này bằng cách triển khai một +{@link android.provider.DocumentsProvider} để gói gọn các dịch vụ của mình. Những ứng dụng +máy khách cần truy cập vào tài liệu của một trình cung cấp có thể tích hợp với SAF chỉ bằng một vài +dòng mã.

+ +

SAF bao gồm:

+ +
    +
  • Trình cung cấp tài liệu—Một trình cung cấp nội dung cho phép một +dịch vụ lưu trữ (chẳng hạn như Google Drive) phát hiện các tệp mà nó quản lý. Trình cung cấp tài liệu được +triển khai thành một lớp con của lớp {@link android.provider.DocumentsProvider}. +Sơ đồ tài liệu-trình cung cấp sẽ được dựa trên một phân cấp tệp truyền thống, +cho dù cách thức trình cung cấp tài liệu của bạn trực tiếp lưu trữ dữ liệu là hoàn toàn do bạn. +Nền tảng Android bao gồm một vài trình cung cấp tài liệu tích hợp, chẳng hạn như +Downloads, Images, và Videos.
  • + +
  • Ứng dụng máy khách—Một ứng dụng tùy chỉnh có chức năng gọi ra ý định +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} và/hoặc +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} và nhận các tệp +được trả về bởi trình cung cấp tài liệu.
  • + +
  • Bộ chọn—Một UI hệ thống cho phép người dùng truy cập tài liệu từ tất cả +trình cung cấp tài liệu mà thỏa mãn các tiêu chí tìm kiếm của ứng dụng máy khách.
  • +
+ +

Một số tính năng được SAF cung cấp bao gồm:

+
    +
  • Cho phép người dùng duyệt nội dung từ tất cả trình cung cấp tài liệu, không chỉ một ứng dụng duy nhất.
  • +
  • Giúp ứng dụng của bạn có thể có quyền truy cập lâu dài, cố định vào + các tài liệu được sở hữu bởi một trình cung cấp tài liệu. Thông qua truy cập này, người dùng có thể thêm, chỉnh sửa, + lưu và xóa tệp trên trình cung cấp.
  • +
  • Hỗ trợ nhiều tài khoản người dùng và các phần gốc tạm thời chẳng hạn như trình cung cấp +bộ nhớ USB, nó chỉ xuất hiện nếu ổ đĩa được cắm vào.
  • +
+ +

Tổng quan

+ +

SAF tập trung xoay quanh một trình cung cấp nội dung là một lớp con +của lớp {@link android.provider.DocumentsProvider}. Trong một trình cung cấp tài liệu, dữ liệu được +cấu trúc thành một phân cấp tệp truyền thống:

+

data model

+

Hình 1. Mô hình dữ liệu của trình cung cấp tài liệu. Một Phần gốc chỉ đến một Tài liệu duy nhất, +sau đó nó bắt đầu xòe ra toàn bộ cây.

+ +

Lưu ý điều sau đây:

+
    + +
  • Mỗi một trình cung cấp tài liệu sẽ báo cáo một hoặc nhiều +"phần gốc" là điểm bắt đầu khám phá cây tài liệu. +Mỗi phần gốc có một {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID} duy nhất, +và nó trỏ đến một tài liệu (thư mục) +biểu diễn nội dung bên dưới phần gốc đó. +Phần gốc có thể linh hoạt theo thiết kế để hỗ trợ các trường hợp sử dụng như nhiều tài khoản, +thiết bị lưu trữ USB tạm thời, hoặc đăng nhập/đăng xuất người dùng.
  • + +
  • Dưới mỗi phần gốc là một tài liệu đơn lẻ. Tài liệu đó sẽ trỏ tới 1 đến N tài liệu, +mỗi tài liệu lại có thể trỏ tới 1 đến N tài liệu khác.
  • + +
  • Mỗi bộ nhớ phụ trợ phủ bề mặt +các tệp và thư mục riêng lẻ bằng cách tham chiếu chúng bằng một +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} duy nhất. +ID của tài liệu phải là duy nhất và không thay đổi sau khi được phát hành, do chúng được sử dụng để cấp URI +không thay đổi giữa các lần khởi động lại thiết bị.
  • + + +
  • Tài liệu có thể là một tệp mở được (có một kiểu MIME cụ thể), hoặc một +thư mục chứa các tài liệu bổ sung (có kiểu MIME +{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR}).
  • + +
  • Mỗi tài liệu có thể có các khả năng khác nhau như được mô tả bởi +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS}. +Ví dụ, {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}, +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE}, và +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}. +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} cũng có thể +có trong nhiều thư mục.
  • +
+ +

Dòng Điều khiển

+

Như nêu trên, mô hình dữ liệu của trình cung cấp tài liệu được dựa trên một phân cấp +tệp truyền thống. Tuy nhiên, bạn có thể thực tế lưu trữ dữ liệu của mình bằng bất kỳ cách nào mà mình thích, miễn +là nó có thể được truy cập thông qua API {@link android.provider.DocumentsProvider}. Ví dụ, bạn +có thể sử dụng kho lưu trữ đám mây dựa trên tag cho dữ liệu của mình.

+ +

Hình 2 minh họa một ví dụ về cách mà một ứng dụng ảnh có thể sử dụng SAF +để truy cập dữ liệu được lưu trữ:

+

app

+ +

Hình 2. Dòng Khuôn khổ Truy cập Kho lưu trữ

+ +

Lưu ý điều sau đây:

+
    + +
  • Trong SAF, trình cung cấp và máy khách không tương tác +trực tiếp với nhau. Một máy khách yêu cầu quyền để tương tác +với tệp (cụ thể là quyền đọc, chỉnh sửa, tạo hoặc xóa tệp).
  • + +
  • Tương tác bắt đầu khi một ứng dụng (trong ví dụ này này một ứng dụng ảnh) thể hiện ý định +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} hoặc {@link android.content.Intent#ACTION_CREATE_DOCUMENT}. Ý định có thể bao gồm các bộ lọc +để cụ thể hơn các tiêu chí—ví dụ, "cấp cho tôi tất cả tệp mở được +có kiểu MIME là 'image'."
  • + +
  • Sau khi ý định thể hiện, bộ chọn của hệ thống sẽ đi đến từng trình cung cấp được đăng ký +và hiển thị cho người dùng xem các phần gốc nội dung khớp với tiêu chí.
  • + +
  • Bộ chọn cấp cho người dùng một giao diện tiêu chuẩn để truy cập tài liệu, mặc +dù các trình cung cấp tài liệu liên quan có thể rất khác nhau. Ví dụ, hình 2 +minh họa một trình cung cấp Google Drive, một trình cung cấp USB, và một trình cung cấp đám mây.
  • +
+ +

Hình 3 minh họa một bộ chọn mà trong đó một người dùng đang tìm kiếm hình ảnh đã chọn một +tài khoản Google Drive:

+ +

picker

+ +

Hình 3. Bộ chọn

+ +

Khi người dùng chọn Google Drive, hình ảnh được hiển thị như minh họa trong +hình 4. Từ điểm đó trở đi, người dùng có thể tương tác với chúng theo bất kỳ cách nào +được hỗ trợ bởi trình cung cấp và ứng dụng máy khách. + +

picker

+ +

Hình 4. Hình ảnh

+ +

Ghi một Ứng dụng Máy khách

+ +

Trên phiên bản Android 4.3 và thấp hơn, nếu bạn muốn ứng dụng của mình truy xuất một tệp từ một ứng dụng +khác, nó phải gọi ra một ý định chẳng hạn như {@link android.content.Intent#ACTION_PICK} +hay {@link android.content.Intent#ACTION_GET_CONTENT}. Khi đó, người dùng phải chọn +một ứng dụng duy nhất mà từ đó họ chọn một tệp và ứng dụng được chọn phải cung cấp một +giao diện người dùng để người dùng duyệt và chọn từ các tệp có sẵn.

+ +

Trên phiên bản Android 4.4 trở lên, bạn có thêm một tùy chọn là sử dụng ý định +{@link android.content.Intent#ACTION_OPEN_DOCUMENT}, +nó hiển thị một UI bộ chọn được điều khiển bởi hệ thống, cho phép người dùng +duyệt tất cả tệp mà các ứng dụng khác đã cung cấp. Từ UI duy nhất này, người dùng +có thể chọn một tệp từ bất kỳ ứng dụng nào được hỗ trợ.

+ +

{@link android.content.Intent#ACTION_OPEN_DOCUMENT} không +nhằm mục đích thay thế cho {@link android.content.Intent#ACTION_GET_CONTENT}. +Bạn nên sử dụng cái nào sẽ phụ thuộc vào nhu cầu của ứng dụng của bạn:

+ +
    +
  • Sử dụng {@link android.content.Intent#ACTION_GET_CONTENT} nếu bạn muốn ứng dụng của mình +chỉ đơn thuần đọc/nhập dữ liệu. Bằng cách này, ứng dụng nhập một bản sao dữ liệu, +chẳng hạn như một tệp hình ảnh.
  • + +
  • Sử dụng {@link android.content.Intent#ACTION_OPEN_DOCUMENT} nếu bạn muốn ứng dụng +của mình có quyền truy cập lâu dài, cố định vào các tài liệu được sở hữu bởi một +trình cung cấp tài liệu. Ví dụ như trường hợp một ứng dụng chỉnh sửa ảnh cho phép người dùng chỉnh sửa +các hình ảnh được lưu trữ trong một trình cung cấp tài liệu.
  • + +
+ + +

Phần này mô tả cách ghi các ứng dụng máy khách dựa trên +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} và +các ý định {@link android.content.Intent#ACTION_CREATE_DOCUMENT}.

+ + + + +

+Đoạn mã HTML sau sử dụng {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +để tìm kiếm các trình cung cấp tài liệu mà +chứa tệp hình ảnh:

+ +
private static final int READ_REQUEST_CODE = 42;
+...
+/**
+ * Fires an intent to spin up the "file chooser" UI and select an image.
+ */
+public void performFileSearch() {
+
+    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
+    // browser.
+    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as a
+    // file (as opposed to a list of contacts or timezones)
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Filter to show only images, using the image MIME data type.
+    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
+    // To search for all documents available via installed storage providers,
+    // it would be "*/*".
+    intent.setType("image/*");
+
+    startActivityForResult(intent, READ_REQUEST_CODE);
+}
+ +

Lưu ý điều sau đây:

+
    +
  • Khi ứng dụng thể hiện ý định {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +, nó sẽ khởi chạy một bộ chọn để hiển thị tất cả trình cung cấp tài liệu khớp với tiêu chí.
  • + +
  • Thêm thể loại {@link android.content.Intent#CATEGORY_OPENABLE} vào +ý định sẽ lọc kết quả để chỉ hiển thị những tài liệu có thể mở được, chẳng hạn như tệp hình ảnh.
  • + +
  • Câu lệnh {@code intent.setType("image/*")} sẽ lọc thêm để +chỉ hiển thị những tài liệu có kiểu dữ liệu MIME hình ảnh.
  • +
+ +

Kết quả Tiến trình

+ +

Sau khi người dùng chọn một tài liệu trong bộ chọn, +{@link android.app.Activity#onActivityResult onActivityResult()} sẽ được gọi. +URI tro tới tài liệu được chọn sẽ nằm trong tham số {@code resultData} +. Trích xuất UI bằng cách sử dụng {@link android.content.Intent#getData getData()}. +Sau khi có nó, bạn có thể sử dụng nó để truy xuất tài liệu mà người dùng muốn. Ví +dụ:

+ +
@Override
+public void onActivityResult(int requestCode, int resultCode,
+        Intent resultData) {
+
+    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
+    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
+    // response to some other intent, and the code below shouldn't run at all.
+
+    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+        // The document selected by the user won't be returned in the intent.
+        // Instead, a URI to that document will be contained in the return intent
+        // provided to this method as a parameter.
+        // Pull that URI using resultData.getData().
+        Uri uri = null;
+        if (resultData != null) {
+            uri = resultData.getData();
+            Log.i(TAG, "Uri: " + uri.toString());
+            showImage(uri);
+        }
+    }
+}
+
+ +

Kiểm tra siêu dữ liệu tài liệu

+ +

Sau khi có URI cho một tài liệu, bạn có quyền truy cập siêu dữ liệu của nó. Đoạn mã HTML +này bắt siêu dữ liệu cho một tài liệu được quy định bởi URI, và ghi lại nó:

+ +
public void dumpImageMetaData(Uri uri) {
+
+    // The query, since it only applies to a single document, will only return
+    // one row. There's no need to filter, sort, or select fields, since we want
+    // all fields for one document.
+    Cursor cursor = getActivity().getContentResolver()
+            .query(uri, null, null, null, null, null);
+
+    try {
+    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
+    // "if there's anything to look at, look at it" conditionals.
+        if (cursor != null && cursor.moveToFirst()) {
+
+            // Note it's called "Display Name".  This is
+            // provider-specific, and might not necessarily be the file name.
+            String displayName = cursor.getString(
+                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+            Log.i(TAG, "Display Name: " + displayName);
+
+            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
+            // If the size is unknown, the value stored is null.  But since an
+            // int can't be null in Java, the behavior is implementation-specific,
+            // which is just a fancy term for "unpredictable".  So as
+            // a rule, check if it's null before assigning to an int.  This will
+            // happen often:  The storage API allows for remote files, whose
+            // size might not be locally known.
+            String size = null;
+            if (!cursor.isNull(sizeIndex)) {
+                // Technically the column stores an int, but cursor.getString()
+                // will do the conversion automatically.
+                size = cursor.getString(sizeIndex);
+            } else {
+                size = "Unknown";
+            }
+            Log.i(TAG, "Size: " + size);
+        }
+    } finally {
+        cursor.close();
+    }
+}
+
+ +

Mở một tài liệu

+ +

Sau khi có URI cho một tài liệu, bạn có thể mở nó hoặc làm bất kỳ điều gì +mà bạn muốn.

+ +

Bitmap

+ +

Sau đây là một ví dụ về cách bạn có thể mở một {@link android.graphics.Bitmap}:

+ +
private Bitmap getBitmapFromUri(Uri uri) throws IOException {
+    ParcelFileDescriptor parcelFileDescriptor =
+            getContentResolver().openFileDescriptor(uri, "r");
+    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
+    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
+    parcelFileDescriptor.close();
+    return image;
+}
+
+ +

Lưu ý rằng bạn không nên thực hiện thao tác này trên luồng UI. Thực hiện điều này dưới +nền bằng cách sử dụng {@link android.os.AsyncTask}. Sau khi mở bitmap, bạn có thể +hiển thị nó trong một {@link android.widget.ImageView}. +

+ +

Nhận một InputStream

+ +

Sau đây là một ví dụ về cách mà bạn có thể nhận một {@link java.io.InputStream} từ URI. Trong đoạn mã HTML +này, các dòng tệp đang được đọc thành một xâu:

+ +
private String readTextFromUri(Uri uri) throws IOException {
+    InputStream inputStream = getContentResolver().openInputStream(uri);
+    BufferedReader reader = new BufferedReader(new InputStreamReader(
+            inputStream));
+    StringBuilder stringBuilder = new StringBuilder();
+    String line;
+    while ((line = reader.readLine()) != null) {
+        stringBuilder.append(line);
+    }
+    fileInputStream.close();
+    parcelFileDescriptor.close();
+    return stringBuilder.toString();
+}
+
+ +

Tạo một tài liệu mới

+ +

Ứng dụng của bạn có thể tạo một tài liệu mới trong một trình cung cấp tài liệu bằng cách sử dụng ý định +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} +. Để tạo một tệp, bạn cấp cho ý định của mình một kiểu MIME và tên tệp, và +khởi chạy nó bằng một mã yêu cầu duy nhất. Phần còn lại sẽ được làm hộ bạn:

+ + +
+// Here are some examples of how you might call this method.
+// The first parameter is the MIME type, and the second parameter is the name
+// of the file you are creating:
+//
+// createFile("text/plain", "foobar.txt");
+// createFile("image/png", "mypicture.png");
+
+// Unique request code.
+private static final int WRITE_REQUEST_CODE = 43;
+...
+private void createFile(String mimeType, String fileName) {
+    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as
+    // a file (as opposed to a list of contacts or timezones).
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Create a file with the requested MIME type.
+    intent.setType(mimeType);
+    intent.putExtra(Intent.EXTRA_TITLE, fileName);
+    startActivityForResult(intent, WRITE_REQUEST_CODE);
+}
+
+ +

Sau khi tạo một tài liệu mới, bạn có thể nhận URI của tài liệu trong +{@link android.app.Activity#onActivityResult onActivityResult()}, sao cho bạn +có thể tiếp tục ghi nó.

+ +

Xóa một tài liệu

+ +

Nếu bạn có URI cho một tài liệu và +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} +của tài liệu chứa +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE}, +bạn có thể xóa tài liệu đó. Ví dụ:

+ +
+DocumentsContract.deleteDocument(getContentResolver(), uri);
+
+ +

Chỉnh sửa một tài liệu

+ +

Bạn có thể sử dụng SAF để chỉnh sửa một tài liệu văn bản ngay tại chỗ. +Đoạn mã HTML này thể hiện +ý định {@link android.content.Intent#ACTION_OPEN_DOCUMENT} và sử dụng +thể loại {@link android.content.Intent#CATEGORY_OPENABLE} để chỉ hiển thị +những tài liệu có thể mở được. Nó lọc thêm để chỉ hiển thị những tệp văn bản:

+ +
+private static final int EDIT_REQUEST_CODE = 44;
+/**
+ * Open a file for writing and append some text to it.
+ */
+ private void editDocument() {
+    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
+    // file browser.
+    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+
+    // Filter to only show results that can be "opened", such as a
+    // file (as opposed to a list of contacts or timezones).
+    intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+    // Filter to show only text files.
+    intent.setType("text/plain");
+
+    startActivityForResult(intent, EDIT_REQUEST_CODE);
+}
+
+ +

Tiếp theo, từ {@link android.app.Activity#onActivityResult onActivityResult()} +(xem Kết quả tiến trình) bạn có thể gọi mã để thực hiện chỉnh sửa. +Đoạn mã HTML sau nhận được một {@link java.io.FileOutputStream} +từ {@link android.content.ContentResolver}. Theo mặc định, nó sử dụng chế độ “ghi”. +Cách tốt nhất là yêu cầu lượng quyền truy cập bạn cần ở mức ít nhất, vì thế đừng yêu cầu +quyền đọc/ghi nếu bạn chỉ cần quyền ghi:

+ +
private void alterDocument(Uri uri) {
+    try {
+        ParcelFileDescriptor pfd = getActivity().getContentResolver().
+                openFileDescriptor(uri, "w");
+        FileOutputStream fileOutputStream =
+                new FileOutputStream(pfd.getFileDescriptor());
+        fileOutputStream.write(("Overwritten by MyCloud at " +
+                System.currentTimeMillis() + "\n").getBytes());
+        // Let the document provider know you're done by closing the stream.
+        fileOutputStream.close();
+        pfd.close();
+    } catch (FileNotFoundException e) {
+        e.printStackTrace();
+    } catch (IOException e) {
+        e.printStackTrace();
+    }
+}
+ +

Cố định các quyền

+ +

Khi ứng dụng của bạn mở một tệp để đọc hoặc ghi, hệ thống sẽ cấp cho +ứng dụng của bạn một quyền URI được cấp cho tệp đó. Quyền này sẽ kéo dài tới khi thiết bị của bạn khởi động lại. +Nhưng giả sử ứng dụng của bạn là một ứng dụng chỉnh sửa hình ảnh, và bạn muốn người dùng có thể +truy cập 5 hình ảnh cuối cùng mà họ đã chỉnh sửa, trực tiếp từ ứng dụng của bạn. Nếu thiết bị của người dùng +đã khởi động lại, bạn sẽ phải gửi người dùng trở lại bộ chọn hệ thống để tìm +các tệp đó, đây rõ ràng không phải là cách lý tưởng.

+ +

Để tránh điều này xảy ra, bạn có thể cố định các quyền mà hệ thống +cấp cho ứng dụng của bạn. Ứng dụng của bạn sẽ "nhận" cấp quyền URI có thể cố định +mà hệ thống cung cấp một cách hiệu quả. Điều này cho phép người dùng có quyền liên tục truy cập các tệp đó +thông qua ứng dụng của bạn, ngay cả khi thiết bị đã bị khởi động lại:

+ + +
final int takeFlags = intent.getFlags()
+            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
+            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+// Check for the freshest data.
+getContentResolver().takePersistableUriPermission(uri, takeFlags);
+ +

Còn một bước cuối cùng. Bạn có thể đã lưu các +URI gần đây nhất mà ứng dụng của bạn đã truy cập, nhưng chúng còn thể không còn hợp lệ—một ứng dụng khác +có thể đã xóa hoặc sửa đổi tài liệu. Vì thế, bạn luôn nên gọi +{@code getContentResolver().takePersistableUriPermission()} để kiểm tra +dữ liệu mới nhất.

+ +

Ghi một Trình cung cấp Tài liệu Tùy chỉnh

+ +

+Nếu bạn đang phát triển một ứng dụng cung cấp dịch vụ lưu trữ cho tệp (chẳng hạn như +một dịch vụ lưu trữ đám mây), bạn có thể cung cấp các tệp của mình thông qua +SAF bằng cách ghi một trình cung cấp tài liệu tùy chỉnh. Phần này mô tả cách làm điều +này.

+ + +

Bản kê khai

+ +

Để triển khai một trình cung cấp tài liệu tùy chỉnh, hãy thêm nội dung sau vào bản kê khai +của ứng dụng của bạn:

+
    + +
  • Một mục tiêu API mức 19 hoặc cao hơn.
  • + +
  • Một phần tử <provider> khai báo trình cung cấp lưu trữ +tùy chỉnh của bạn.
  • + +
  • Tên của trình cung cấp của bạn, là tên lớp của nó, bao gồm tên gói. +Ví dụ: com.example.android.storageprovider.MyCloudProvider.
  • + +
  • Tên thẩm quyền của bạn, tức là tên gói của bạn (trong ví dụ này là +com.example.android.storageprovider) cộng với kiểu của trình cung cấp nội dung +(documents). Ví dụ, {@code com.example.android.storageprovider.documents}.
  • + +
  • Thuộc tính android:exported được đặt thành "true". +Bạn phải xuất trình cung cấp của mình để các ứng dụng khác có thể thấy nó.
  • + +
  • Thuộc tính android:grantUriPermissions được đặt thành +"true". Thiết đặt này cho phép hệ thống cấp cho các ứng dụng khác quyền truy cập +vào nội dung trong trình cung cấp của bạn. Để thảo luận về cách cố định quyền được cấp cho +một tài liệu cụ thể, hãy xem phầnCố định các quyền.
  • + +
  • Quyền {@code MANAGE_DOCUMENTS}. Theo mặc định, một trình cung cấp sẽ có sẵn +đối với mọi người. Việc thêm quyền này sẽ hạn chế trình cung cấp của bạn vào hệ thống. +Hạn chế này có ý nghĩa quan trọng đối với vấn đề bảo mật.
  • + +
  • Thuộc tính {@code android:enabled} được đặt thành một giá trị boolean được định nghĩa trong một tệp +tài nguyên. Mục đích của thuộc tính này là để vô hiệu hóa trình cung cấp trên các thiết bị chạy phiên bản Android 4.3 hoặc thấp hơn. +Ví dụ, {@code android:enabled="@bool/atLeastKitKat"}. Bên +cạnh việc nêu thuộc tính này trong bản kê khai, bạn cần làm như sau: +
      +
    • Trong tệp tài nguyên {@code bool.xml} của bạn bên dưới {@code res/values/}, hãy thêm +dòng sau:
      <bool name="atLeastKitKat">false</bool>
    • + +
    • Trong tệp tài nguyên {@code bool.xml} của bạn bên dưới {@code res/values-v19/}, hãy thêm +dòng sau:
      <bool name="atLeastKitKat">true</bool>
    • +
  • + +
  • Một bộ lọc ý định chứa hành động +{@code android.content.action.DOCUMENTS_PROVIDER}, sao cho trình cung cấp của bạn +xuất hiện trong bộ chọn khi hệ thống tìm kiếm trình cung cấp.
  • + +
+

Sau đây là các đoạn trích từ một bản kê khai mẫu chứa một trình cung cấp:

+ +
<manifest... >
+    ...
+    <uses-sdk
+        android:minSdkVersion="19"
+        android:targetSdkVersion="19" />
+        ....
+        <provider
+            android:name="com.example.android.storageprovider.MyCloudProvider"
+            android:authorities="com.example.android.storageprovider.documents"
+            android:grantUriPermissions="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DOCUMENTS"
+            android:enabled="@bool/atLeastKitKat">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+        </provider>
+    </application>
+
+</manifest>
+ +

Hỗ trợ các thiết bị chạy phiên bản Android 4.3 và thấp hơn

+ +

Ý định +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} chỉ có sẵn +trên các thiết bị chạy phiên bản Android 4.4 trở lên. +Nếu bạn muốn ứng dụng của mình hỗ trợ {@link android.content.Intent#ACTION_GET_CONTENT} +để tạo điều kiện cho các thiết bị đang chạy phiên bản Android 4.3 và thấp hơn, bạn nên +vô hiệu hóa bộ lọc ý định {@link android.content.Intent#ACTION_GET_CONTENT} trong +bản kê khai của bạn cho các thiết bị chạy phiên bản Android 4.4 trở lên. Một +trình cung cấp tài liệu và {@link android.content.Intent#ACTION_GET_CONTENT} nên được xem xét + loại trừ lẫn nhau. Nếu bạn hỗ trợ cả hai đồng thời, ứng dụng của bạn sẽ +xuất hiện hai lần trong UI của bộ chọn hệ thống, đưa ra hai cách khác nhau để truy cập +dữ liệu đã lưu của bạn. Điều này có thể khiến người dùng bị nhầm lẫn.

+ +

Sau đây là cách được khuyến cáo để vô hiệu hóa bộ lọc ý định +{@link android.content.Intent#ACTION_GET_CONTENT} đối với các thiết bị +chạy phiên bản Android 4.4 hoặc cao hơn:

+ +
    +
  1. Trong tệp tài nguyên {@code bool.xml} của bạn bên dưới {@code res/values/}, hãy thêm +dòng sau:
    <bool name="atMostJellyBeanMR2">true</bool>
  2. + +
  3. Trong tệp tài nguyên {@code bool.xml} của bạn bên dưới {@code res/values-v19/}, hãy thêm +dòng sau:
    <bool name="atMostJellyBeanMR2">false</bool>
  4. + +
  5. Thêm một +bí danh +hoạt động để vô hiệu hóa bộ lọc ý định {@link android.content.Intent#ACTION_GET_CONTENT} +đối với các phiên bản 4.4 (API mức 19) trở lên. Ví dụ: + +
    +<!-- This activity alias is added so that GET_CONTENT intent-filter
    +     can be disabled for builds on API level 19 and higher. -->
    +<activity-alias android:name="com.android.example.app.MyPicker"
    +        android:targetActivity="com.android.example.app.MyActivity"
    +        ...
    +        android:enabled="@bool/atMostJellyBeanMR2">
    +    <intent-filter>
    +        <action android:name="android.intent.action.GET_CONTENT" />
    +        <category android:name="android.intent.category.OPENABLE" />
    +        <category android:name="android.intent.category.DEFAULT" />
    +        <data android:mimeType="image/*" />
    +        <data android:mimeType="video/*" />
    +    </intent-filter>
    +</activity-alias>
    +
    +
  6. +
+

Hợp đồng

+ +

Thường khi bạn ghi một trình cung cấp nội dung tùy chỉnh, một trong những tác vụ đó là +triển khai các lớp hợp đồng như được mô tả trong hướng dẫn cho nhà phát triển + +Trình cung cấp Nội dung. Lớp hợp đồng là một lớp {@code public final} mà +chứa các định nghĩa hằng số cho URI, tên cột, kiểu MIME và +siêu dữ liệu khác liên quan tới trình cung cấp. SAF +cung cấp những lớp hợp đồng này cho bạn, vì thế bạn không cần tự +ghi:

+ +
    +
  • {@link android.provider.DocumentsContract.Document}
  • +
  • {@link android.provider.DocumentsContract.Root}
  • +
+ +

Ví dụ, sau đây là các cột bạn có thể trả về trong một con chạy khi +trình cung cấp tài liệu của bạn được truy vấn về tài liệu hoặc phần gốc:

+ +
private static final String[] DEFAULT_ROOT_PROJECTION =
+        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
+        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
+        Root.COLUMN_AVAILABLE_BYTES,};
+private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
+        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
+        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
+        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
+
+ +

Phân lớp con DocumentsProvider

+ +

Bước tiếp theo trong khi ghi một trình cung cấp tài liệu tùy chỉnh đó là phân lớp con +cho lớp tóm tắt {@link android.provider.DocumentsProvider}. Tối thiểu, bạn cần triển khai +các phương pháp sau:

+ +
    +
  • {@link android.provider.DocumentsProvider#queryRoots queryRoots()}
  • + +
  • {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
  • + +
  • {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
  • + +
  • {@link android.provider.DocumentsProvider#openDocument openDocument()}
  • +
+ +

Đây là những phương pháp duy nhất mà bạn được yêu cầu phải triển khai, nhưng còn +nhiều phương pháp nữa mà bạn có thể muốn triển khai. Xem {@link android.provider.DocumentsProvider} +để biết chi tiết.

+ +

Triển khai queryRoots

+ +

Việc bạn triển khai {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} phải trả về một {@link android.database.Cursor} trỏ về tất cả +thư mục gốc trong trình cung cấp tài liệu của bạn, bằng cách sử dụng các cột được định nghĩa trong +{@link android.provider.DocumentsContract.Root}.

+ +

Trong đoạn mã HTML sau, tham số {@code projection} biểu diễn các trường cụ thể +mà hàm gọi muốn nhận về. Đoạn mã HTML tạo một con chạy mới +và thêm một hàng vào nó—một thư mục gốc, mức cao nhất, như +Downloads hoặc Images. Hầu hết các trình cung cấp chỉ có một phần gốc. Bạn có thể có nhiều hơn một, +ví dụ, trong trường hợp nhiều tài khoản người dùng. Trong trường hợp đó, chỉ cần thêm một +hàng thứ hai vào con chạy.

+ +
+@Override
+public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+
+    // Create a cursor with either the requested fields, or the default
+    // projection if "projection" is null.
+    final MatrixCursor result =
+            new MatrixCursor(resolveRootProjection(projection));
+
+    // If user is not logged in, return an empty root cursor.  This removes our
+    // provider from the list entirely.
+    if (!isUserLoggedIn()) {
+        return result;
+    }
+
+    // It's possible to have multiple roots (e.g. for multiple accounts in the
+    // same app) -- just add multiple cursor rows.
+    // Construct one row for a root called "MyCloud".
+    final MatrixCursor.RowBuilder row = result.newRow();
+    row.add(Root.COLUMN_ROOT_ID, ROOT);
+    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
+
+    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
+    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
+    // recently used documents will show up in the "Recents" category.
+    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
+    // shares.
+    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
+            Root.FLAG_SUPPORTS_RECENTS |
+            Root.FLAG_SUPPORTS_SEARCH);
+
+    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
+    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
+
+    // This document id cannot change once it's shared.
+    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
+
+    // The child MIME types are used to filter the roots and only present to the
+    //  user roots that contain the desired type somewhere in their file hierarchy.
+    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
+    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
+    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
+
+    return result;
+}
+ +

Triển khai queryChildDocuments

+ +

Việc bạn triển khai +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} +phải trả về một {@link android.database.Cursor} mà chỉ đến tất cả tệp trong +thư mục được chỉ định, bằng cách sử dụng các cột được định nghĩa trong +{@link android.provider.DocumentsContract.Document}.

+ +

Phương pháp này được gọi khi bạn chọn một thư mục gốc ứng dụng trong UI bộ chọn. +Nó nhận được tài liệu con của một thư mục nằm dưới phần gốc. Nó có thể được gọi ở bất kỳ mức nào trong phân cấp tệp +, không chỉ phần gốc. Đoạn mã HTML +này tạo một con chạy mới bằng các cột được yêu cầu, sau đó thêm thông tin về +mọi tệp con trực tiếp trong thư mục mẹ vào con chạy. +Tệp con có thể là một hình ảnh, một thư mục khác—bất kỳ tệp nào:

+ +
@Override
+public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+                              String sortOrder) throws FileNotFoundException {
+
+    final MatrixCursor result = new
+            MatrixCursor(resolveDocumentProjection(projection));
+    final File parent = getFileForDocId(parentDocumentId);
+    for (File file : parent.listFiles()) {
+        // Adds the file's display name, MIME type, size, and so on.
+        includeFile(result, null, file);
+    }
+    return result;
+}
+
+ +

Triển khai queryDocument

+ +

Việc bạn triển khai +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} +phải trả về một {@link android.database.Cursor} mà chỉ đến tệp được chỉ định, +bằng cách sử dụng các cột được định nghĩa trong {@link android.provider.DocumentsContract.Document}. +

+ +

Phương pháp {@link android.provider.DocumentsProvider#queryDocument queryDocument()} +trả về cùng thông tin đã được chuyển trong +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}, +nhưng là đối với một tệp cụ thể:

+ + +
@Override
+public Cursor queryDocument(String documentId, String[] projection) throws
+        FileNotFoundException {
+
+    // Create a cursor with the requested projection, or the default projection.
+    final MatrixCursor result = new
+            MatrixCursor(resolveDocumentProjection(projection));
+    includeFile(result, documentId, null);
+    return result;
+}
+
+ +

Triển khai openDocument

+ +

Bạn phải triển khai {@link android.provider.DocumentsProvider#openDocument +openDocument()} để trả về một {@link android.os.ParcelFileDescriptor} biểu diễn +tệp được chỉ định. Các ứng dụng khác có thể sử dụng {@link android.os.ParcelFileDescriptor} +được trả về để truyền phát dữ liệu. Hệ thống gọi phương pháp này sau khi người dùng chọn một tệp +và ứng dụng máy khách yêu cầu truy cập nó bằng cách gọi +{@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()}. +Ví dụ:

+ +
@Override
+public ParcelFileDescriptor openDocument(final String documentId,
+                                         final String mode,
+                                         CancellationSignal signal) throws
+        FileNotFoundException {
+    Log.v(TAG, "openDocument, mode: " + mode);
+    // It's OK to do network operations in this method to download the document,
+    // as long as you periodically check the CancellationSignal. If you have an
+    // extremely large file to transfer from the network, a better solution may
+    // be pipes or sockets (see ParcelFileDescriptor for helper methods).
+
+    final File file = getFileForDocId(documentId);
+
+    final boolean isWrite = (mode.indexOf('w') != -1);
+    if(isWrite) {
+        // Attach a close listener if the document is opened in write mode.
+        try {
+            Handler handler = new Handler(getContext().getMainLooper());
+            return ParcelFileDescriptor.open(file, accessMode, handler,
+                        new ParcelFileDescriptor.OnCloseListener() {
+                @Override
+                public void onClose(IOException e) {
+
+                    // Update the file with the cloud server. The client is done
+                    // writing.
+                    Log.i(TAG, "A file with id " +
+                    documentId + " has been closed!
+                    Time to " +
+                    "update the server.");
+                }
+
+            });
+        } catch (IOException e) {
+            throw new FileNotFoundException("Failed to open document with id "
+            + documentId + " and mode " + mode);
+        }
+    } else {
+        return ParcelFileDescriptor.open(file, accessMode);
+    }
+}
+
+ +

Bảo mật

+ +

Giả sử trình cung cấp tài liệu của bạn là một dịch vụ lưu trữ đám mây được bảo vệ bằng mật khẩu +và bạn muốn đảm bảo rằng người dùng được đăng nhập trước khi bạn bắt đầu chia sẻ tệp của họ. +Ứng dụng của bạn nên làm gì nếu người dùng không đăng nhập? Giải pháp là trả về +phần gốc 0 trong triển khai {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} của bạn. Cụ thể là một con chạy gốc trống:

+ +
+public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+...
+    // If user is not logged in, return an empty root cursor.  This removes our
+    // provider from the list entirely.
+    if (!isUserLoggedIn()) {
+        return result;
+}
+
+ +

Bước còn lại là gọi {@code getContentResolver().notifyChange()}. +Bạn còn nhớ {@link android.provider.DocumentsContract} chứ? Chúng ta đang sử dụng nó để tạo +URI này. Đoạn mã HTML sau báo cho hệ thống truy vấn các phần gốc trong +trình cung cấp tài liệu của bạn bất cứ khi nào trạng thái đăng nhập của người dùng thay đổi. Nếu người dùng không được +đăng nhập, lệnh gọi tới {@link android.provider.DocumentsProvider#queryRoots queryRoots()} sẽ trả về một +con chạy trống như minh họa bên trên. Điều này đảm bảo rằng tài liệu của một trình cung cấp chỉ +có sẵn nếu người dùng đăng nhập vào trình cung cấp đó.

+ +
private void onLoginButtonClick() {
+    loginOrLogout();
+    getContentResolver().notifyChange(DocumentsContract
+            .buildRootsUri(AUTHORITY), null);
+}
+
\ No newline at end of file diff --git a/docs/html-intl/intl/vi/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/vi/guide/topics/resources/accessing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..b5491dcdee2c8d6fca3e1c96f0c7d3dc5d6bd81f --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=Truy cập Tài nguyên +parent.title=Tài nguyên Ứng dụng +parent.link=index.html +@jd:body + +
+
+

Xem nhanh

+
    +
  • Tài nguyên có thể được tham chiếu từ mã bằng các số nguyên từ {@code R.java}, chẳng hạn như +{@code R.drawable.myimage}
  • +
  • Tài nguyên có thể được tham chiếu từ các tài nguyên bằng cách sử dụng một cú pháp XML đặc biệt, ví dụ như {@code +@drawable/myimage}
  • +
  • Bạn cũng có thể truy cập tài nguyên ứng dụng của mình bằng các phương pháp trong +{@link android.content.res.Resources}
  • +
+ +

Lớp khóa

+
    +
  1. {@link android.content.res.Resources}
  2. +
+ +

Trong tài liệu này

+
    +
  1. Truy cập Tài nguyên từ Mã
  2. +
  3. Truy cập Tài nguyên từ XML +
      +
    1. Tham chiếu các thuộc tính kiểu
    2. +
    +
  4. +
  5. Truy cập Tài nguyên Nền tảng
  6. +
+ +

Xem thêm

+
    +
  1. Cung cấp Tài nguyên
  2. +
  3. Loại Tài nguyên
  4. +
+
+
+ + + + +

Sau khi cung cấp một tài nguyên trong ứng dụng của mình (đề cập trong Cung cấp Tài nguyên), bạn có thể áp dụng nó bằng cách +tham chiếu ID tài nguyên đó. Tất cả ID tài nguyên được định nghĩa trong lớp {@code R} dự án của bạn, do +công cụ {@code aapt} tự động khởi tạo.

+ +

Khi ứng dụng của bạn được biên dịch, {@code aapt} khởi tạo lớp {@code R}, trong đó chứa +ID tài nguyên cho tất cả tài nguyên trong thư mục {@code +res/} của bạn. Với mỗi loại tài nguyên, có một lớp con {@code R} (ví dụ, +{@code R.drawable} cho tất cả tài nguyên có thể vẽ), và với mỗi tài nguyên loại đó, có một số nguyên +tĩnh (ví dụ, {@code R.drawable.icon}). Số nguyên này là ID tài nguyên mà bạn có thể sử dụng +để truy xuất tài nguyên của mình.

+ +

Mặc dù lớp {@code R} là nơi các ID tài nguyên được quy định, bạn sẽ không cần +tìm ở đó để khám phá một ID tài nguyên. Một ID tài nguyên luôn bao gồm:

+
    +
  • Loại tài nguyên: Mỗi tài nguyên được nhóm vào một "loại," chẳng hạn như {@code +string}, {@code drawable}, và {@code layout}. Để biết thêm về các loại khác nhau, hãy xem phần Loại Tài nguyên. +
  • +
  • Tên tài nguyên, là, hoặc: tên tệp, +không bao gồm phần mở rộng; hoặc giá trị trong thuộc tính XML {@code android:name}, nếu tài nguyên +đó là một giá trị đơn giản (chẳng hạn như một xâu).
  • +
+ +

Có hai cách để bạn có thể truy cập một tài nguyên:

+
    +
  • Trong mã: Sử dụng một số nguyên tĩnh từ một lớp con của lớp {@code R} +của bạn, chẳng hạn như: +
    R.string.hello
    +

    {@code string} là loại tài nguyên và {@code hello} là tên tài nguyên. Có nhiều +API Android mà có thể truy cập các tài nguyên của bạn khi bạn cung cấp một ID tài nguyên theo định dạng này. Xem +Truy cập Tài nguyên trong Mã.

    +
  • +
  • Trong XML: Sử dụng một cú pháp XML đặc biệt mà cũng tương ứng với +ID tài nguyên được định nghĩa trong lớp {@code R} của bạn, chẳng hạn như: +
    @string/hello
    +

    {@code string} là loại tài nguyên và {@code hello} là tên tài nguyên. Bạn có thể sử dụng cú pháp +này trong một tài nguyên XML ở bất kỳ nơi nào có kỳ vọng một giá trị mà bạn cung cấp trong một tài nguyên. Xem phần Truy cập Tài nguyên từ XML.

    +
  • +
+ + + +

Truy cập Tài nguyên trong Mã

+ +

Bạn có thể sử dụng một tài nguyên trong mã bằng cách chuyển ID tài nguyên như một tham số phương pháp. Ví +dụ, bạn có thể đặt một {@link android.widget.ImageView} để sử dụng tài nguyên {@code res/drawable/myimage.png} +bằng cách sử dụng {@link android.widget.ImageView#setImageResource(int) setImageResource()}:

+
+ImageView imageView = (ImageView) findViewById(R.id.myimageview);
+imageView.setImageResource(R.drawable.myimage);
+
+ +

Bạn cũng có thể truy xuất các tài nguyên riêng lẻ bằng các phương pháp trong {@link +android.content.res.Resources}, theo đó bạn có thể nhận được một thực thể +bằng {@link android.content.Context#getResources()}.

+ + + + +

Cú pháp

+ +

Sau đây là cú pháp để tham chiếu một tài nguyên trong mã:

+ +
+[<package_name>.]R.<resource_type>.<resource_name>
+
+ +
    +
  • {@code <package_name>} là tên của gói mà tài nguyên nằm trong đó (không +bắt buộc khi tham chiếu các tài nguyên từ gói của chính bạn).
  • +
  • {@code <resource_type>} là lớp con {@code R} cho loại tài nguyên.
  • +
  • {@code <resource_name>} hoặc là tên tệp tài nguyên +không có phần mở rộng hoặc là giá trị thuộc tính {@code android:name} trong phần tử XML (đối với các giá trị +đơn giản).
  • +
+

Xem phần Loại Tài nguyên để +biết thêm thông tin về mỗi loại tài nguyên và cách tham chiếu chúng.

+ + +

Trường hợp sử dụng

+ +

Có nhiều phương pháp chấp nhận một tham số ID tài nguyên và bạn có thể truy xuất tài nguyên bằng cách sử dụng +các phương pháp trong {@link android.content.res.Resources}. Bạn có thể lấy một thực thể {@link +android.content.res.Resources} bằng {@link android.content.Context#getResources +Context.getResources()}.

+ + +

Sau đây là một số ví dụ về truy cập tài nguyên trong mã:

+ +
+// Load a background for the current screen from a drawable resource
+{@link android.app.Activity#getWindow()}.{@link
+android.view.Window#setBackgroundDrawableResource(int)
+setBackgroundDrawableResource}(R.drawable.my_background_image) ;
+
+// Set the Activity title by getting a string from the Resources object, because
+//  this method requires a CharSequence rather than a resource ID
+{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence)
+setTitle}(getResources().{@link android.content.res.Resources#getText(int)
+getText}(R.string.main_title));
+
+// Load a custom layout for the current screen
+{@link android.app.Activity#setContentView(int)
+setContentView}(R.layout.main_screen);
+
+// Set a slide in animation by getting an Animation from the Resources object
+mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation)
+setInAnimation}(AnimationUtils.loadAnimation(this,
+        R.anim.hyperspace_in));
+
+// Set the text on a TextView object using a resource ID
+TextView msgTextView = (TextView) findViewById(R.id.msg);
+msgTextView.{@link android.widget.TextView#setText(int)
+setText}(R.string.hello_message);
+
+ + +

Chú ý: Bạn không nên sửa đổi tệp {@code +R.java} bằng cách thủ công—nó được khởi tạo bởi công cụ {@code aapt} khi dự án của bạn được +biên dịch. Mọi thay đổi đều bị ghi đè vào lần biên dịch tới của bạn.

+ + + +

Truy cập Tài nguyên từ XML

+ +

Bạn có thể định nghĩa các giá trị cho một số thuộc tính và phần tử XML bằng cách sử dụng một +tham chiếu tới một tài nguyên hiện có. Bạn sẽ thường làm điều này khi tạo các tệp bố trí, để +cung cấp các xâu và hình ảnh cho widget của mình.

+ +

Ví dụ, nếu thêm một {@link android.widget.Button} vào bố trí của mình, bạn nên sử dụng +một tài nguyên xâu cho văn bản nút:

+ +
+<Button
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="@string/submit" />
+
+ + +

Cú pháp

+ +

Sau đây là cú pháp để tham chiếu một tài nguyên trong một tài nguyên XML:

+ +
+@[<package_name>:]<resource_type>/<resource_name>
+
+ +
    +
  • {@code <package_name>} là tên của gói mà tài nguyên nằm trong đó (không +bắt buộc khi tham chiếu các tài nguyên từ cùng gói đó)
  • +
  • {@code <resource_type>} là lớp con +{@code R} cho loại tài nguyên.
  • +
  • {@code <resource_name>} hoặc là tên tệp tài nguyên +không có phần mở rộng hoặc là giá trị thuộc tính {@code android:name} trong phần tử XML (đối với các giá trị +đơn giản).
  • +
+ +

Xem phần Loại Tài nguyên để +biết thêm thông tin về mỗi loại tài nguyên và cách tham chiếu chúng.

+ + +

Trường hợp sử dụng

+ +

Trong một số trường hợp bạn phải sử dụng một tài nguyên cho một giá trị trong XML (ví dụ, để áp dụng một hình ảnh có thể vẽ +cho một widget), nhưng bạn cũng có thể sử dụng một tài nguyên trong XML ở bất kỳ nơi nào chấp nhận một giá trị đơn giản. Ví +dụ, nếu bạn có tệp tài nguyên sau bao gồm một tài nguyên màu và một tài nguyên xâu:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+   <color name="opaque_red">#f00</color>
+   <string name="hello">Hello!</string>
+</resources>
+
+ +

Bạn có thể sử dụng những tài nguyên này trong tệp bố trí sau để đặt màu văn bản và +xâu văn bản:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:textColor="@color/opaque_red"
+    android:text="@string/hello" />
+
+ +

Trong trường hợp này, bạn không cần quy định tên gói trong tham chiếu tài nguyên đó vì tài nguyên +xuất phát từ gói của chính bạn. Để +tham chiếu một tài nguyên hệ thống, bạn sẽ cần đưa vào tên gói. Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:textColor="@android:color/secondary_text_dark"
+    android:text="@string/hello" />
+
+ +

Lưu ý: Bạn nên sử dụng các tài nguyên xâu +vào mọi lúc, để ứng dụng của bạn có thể được bản địa hóa cho các ngôn ngữ khác. +Để biết thông tin về việc tạo các tài nguyên +thay thế (chẳng hạn như xâu được bản địa hóa), hãy xem phần Cung cấp Tài nguyên +Thay thế. Để được hướng dẫn đầy đủ về việc bản địa hóa ứng dụng của bạn cho các ngôn ngữ khác, +hãy xem phần Bản địa hóa.

+ +

Bạn thậm chí có thể sử dụng tài nguyên trong XML để tạo các bí danh. Ví dụ, bạn có thể tạo một tài nguyên có thể vẽ +là một bí danh cho một tài nguyên có thể vẽ khác:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/other_drawable" />
+
+ +

Nghe có vẻ thừa, nhưng có thể rất hữu ích khi sử dụng tài nguyên thay thế. Đọc thêm về +Tạo tài nguyên bí danh.

+ + + +

Tham chiếu các thuộc tính kiểu

+ +

Một tài nguyên thuộc tính kiểu sẽ cho phép bạn tham chiếu giá trị +của một thuộc tính trong chủ đề đang áp dụng. Tham chiếu một thuộc tính kiểu sẽ cho phép bạn +tùy chỉnh diện mạo của các phần tử UI bằng cách tạo kiểu cho chúng để phù hợp với các biến đổi tiêu chuẩn được cung cấp bởi +chủ đề hiện tại, thay vì cung cấp một giá trị được mã hóa cố định. Tham chiếu một thuộc tính kiểu +về cơ bản mà nói, là "sử dụng kiểu được định nghĩa bởi thuộc tính này, trong chủ đề hiện tại."

+ +

Để tham chiếu một thuộc tính kiểu, cú pháp tên gần như tương tự với định dạng tài nguyên thường +, nhưng thay vì biểu tượng @ ({@code @}), hãy sử dụng một dấu hỏi ({@code ?}), và +phần loại tài nguyên là tùy chọn. Ví dụ:

+ +
+?[<package_name>:][<resource_type>/]<resource_name>
+
+ +

Ví dụ, sau đây là cách bạn có thể tham chiếu một thuộc tính để đặt màu văn bản cho phù hợp với màu văn bản +"chính" của chủ đề hệ thống:

+ +
+<EditText id="text"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:textColor="?android:textColorSecondary"
+    android:text="@string/hello_world" />
+
+ +

Ở đây, thuộc tính {@code android:textColor} quy định tên của một thuộc tính kiểu +trong chủ đề hiện tại. Hiện nay, Android sử dụng giá trị được áp dụng cho thuộc tính kiểu {@code android:textColorSecondary} +làm giá trị cho {@code android:textColor} trong widget này. Vì công cụ tài nguyên +hệ thống biết rằng một tài nguyên thuộc tính sẽ được yêu cầu trong ngữ cảnh này, +bạn không cần nêu rõ loại (mà sẽ là +?android:attr/textColorSecondary)—bạn có thể không nêu loại {@code attr}.

+ + + + +

Truy cập Tài nguyên Nền tảng

+ +

Android bao gồm nhiều tài nguyên tiêu chuẩn, chẳng hạn như kiểu, chủ đề và bố trí. Để +truy cập các tài nguyên này, hãy xác định tham chiếu tài nguyên của bạn bằng tên gói +android. Ví dụ, Android cung cấp một tài nguyên bố trí bạn có thể sử dụng cho +các mục danh sách trong một {@link android.widget.ListAdapter}:

+ +
+{@link android.app.ListActivity#setListAdapter(ListAdapter)
+setListAdapter}(new {@link
+android.widget.ArrayAdapter}<String>(this, android.R.layout.simple_list_item_1, myarray));
+
+ +

Trong ví dụ này, {@link android.R.layout#simple_list_item_1} là một tài nguyên bố trí được định nghĩa bởi +nền tảng cho các mục trong một {@link android.widget.ListView}. Bạn có thể sử dụng điều này thay vì tạo +bố trí riêng của mình cho các mục danh sách. Để biết thêm thông tin, hãy xem phần +Dạng xem Danh sách trong hướng dẫn cho nhà phát triển.

+ diff --git a/docs/html-intl/intl/vi/guide/topics/resources/overview.jd b/docs/html-intl/intl/vi/guide/topics/resources/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..7bbd72af96a77d8427d0202d76ee74b621cf773c --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=Tổng quan về Tài nguyên +@jd:body + + + + +

Bạn nên luôn ngoại hiện hóa các tài nguyên chẳng hạn như hình ảnh và xâu từ mã +ứng dụng của mình, sao cho bạn có thể duy trì chúng một cách độc lập. Việc ngoại hiện hóa +tài nguyên cũng cho phép bạn cung cấp các tài nguyên thay thế hỗ trợ những cấu hình +thiết bị cụ thể chẳng hạn như ngôn ngữ hoặc kích cỡ màn hình khác nhau, điều này đang ngày càng trở nên +quan trọng bởi các thiết bị dựa trên nền tảng Android ngày càng sẵn có với các cấu hình khác nhau. Để +đảm bảo tính tương thích với các cấu hình khác nhau, bạn phải tổ chức tài nguyên trong +thư mục {@code res/} dự án của bạn bằng cách sử dụng các thư mục con khác nhau có chức năng nhóm tài nguyên lại theo loại và +cấu hình.

+ +
+ +

+Hình 1. Hai thiết bị khác nhau, mỗi thiết bị sử dụng bố trí mặc định +(ứng dụng không cung cấp bố trí thay thế).

+
+ +
+ +

+Hình 2. Hai thiết bị khác nhau, mỗi thiết bị sử dụng một bố trí khác nhau được cung cấp +cho các kích cỡ màn hình khác nhau.

+
+ +

Đối với mọi loại tài nguyên, bạn có thể quy định tài nguyên mặc định và nhiều tài nguyên +thay thế cho ứng dụng của mình:

+
    +
  • Tài nguyên mặc định là những tài nguyên nên được sử dụng không phụ thuộc vào +cấu hình thiết bị hoặc khi không có tài nguyên thay thế khớp với cấu hình +hiện tại.
  • +
  • Tài nguyên thay thế là những tài nguyên mà bạn đã thiết kế để sử dụng với một cấu hình +cụ thể. Để quy định rằng một nhóm tài nguyên áp dụng cho một cấu hình cụ thể, +hãy nối hình dạng cấu hình phù hợp với tên thư mục.
  • +
+ +

Ví dụ, trong khi bố trí UI mặc định của bạn +được lưu trong thư mục {@code res/layout/}, bạn có thể quy định một bố trí khác sẽ +được sử dụng khi màn hình ở hướng khổ ngang, bằng cách lưu nó trong thư mục {@code res/layout-land/} +. Android tự động áp dụng các tài nguyên phù hợp bằng cách khớp cấu hình hiện tại +của thiết bị với tên thư mục tài nguyên của bạn.

+ +

Hình 1 minh họa cách hệ thống áp dụng cùng bố trí cho +hai thiết bị khác nhau khi không có sẵn tài nguyên thay thế. Hình 2 minh họa +cùng ứng dụng khi nó thêm một tài nguyên bố trí thay thế cho các màn hình lớn hơn.

+ +

Các tài liệu sau trình bày hướng dẫn hoàn chỉnh về cách bạn có thể tổ chức các tài nguyên ứng dụng của mình, +quy định tài nguyên thay thế, truy cập chúng trong ứng dụng của bạn, và nhiều điều khác:

+ +
+
Cung cấp Tài nguyên
+
Những kiểu tài nguyên mà bạn có thể cung cấp trong ứng dụng của mình, nơi lưu chúng, và cách tạo +tài nguyên thay thế cho những cấu hình thiết bị cụ thể.
+
Truy cập Tài nguyên
+
Cách sử dụng tài nguyên mà bạn đã cung cấp hoặc bằng cách tham chiếu chúng từ mã ứng dụng của mình +hoặc từ các tài nguyên XML khác.
+
Xử lý Thay đổi Thời gian chạy
+
Cách quản lý những thay đổi cấu hình mà diễn ra trong khi Hoạt động của bạn đang chạy.
+
Bản địa hóa
+
Một hướng dẫn từ dưới lên về việc bản địa hóa ứng dụng của bạn bằng cách sử dụng các tài nguyên thay thế. Trong khi đây +chỉ là một công dụng cụ thể của tài nguyên thay thế, nó rất quan trọng để tiếp cận với nhiều +người dùng hơn.
+
Loại Tài nguyên
+
Một tham chiếu về các loại tài nguyên khác nhau mà bạn có thể cung cấp, mô tả các phần tử XML, +thuộc tính và cú pháp của chúng. Ví dụ, tham chiếu này cho bạn thấy cách tạo một tài nguyên cho +menu ứng dụng, đối tượng vẽ được, hoạt ảnh, và hơn thế nữa.
+
+ + diff --git a/docs/html-intl/intl/vi/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/vi/guide/topics/resources/providing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..b733643e75cd21fedb069846678809b8eb85e26f --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=Cung cấp Tài nguyên +parent.title=Tài nguyên Ứng dụng +parent.link=index.html +@jd:body + +
+
+

Xem nhanh

+
    +
  • Các loại tài nguyên khác nhau thuộc về các thư mục con khác nhau của {@code res/}
  • +
  • Tài nguyên thay thế cung cấp các tệp tài nguyên theo cấu hình cụ thể
  • +
  • Luôn bao gồm tài nguyên mặc định để ứng dụng của bạn không phụ thuộc vào các +cấu hình thiết bị cụ thể
  • +
+

Trong tài liệu này

+
    +
  1. Nhóm các Loại Tài nguyên lại
  2. +
  3. Cung cấp Tài nguyên Thay thế +
      +
    1. Quy tắc về tên hạn định
    2. +
    3. Tạo tài nguyên bí danh
    4. +
    +
  4. +
  5. Cung cấp Tính tương thích giữa Thiết bị với Tài nguyên Tốt nhất
  6. +
  7. Cách Android tìm Tài nguyên Khớp Tốt nhất
  8. +
+ +

Xem thêm

+
    +
  1. Truy cập Tài nguyên
  2. +
  3. Loại Tài nguyên
  4. +
  5. Hỗ trợ Nhiều +Màn hình
  6. +
+
+
+ +

Bạn nên luôn ngoại hiện hóa các tài nguyên ứng dụng chẳng hạn như hình ảnh và xâu từ mã +của mình, sao cho bạn có thể duy trì chúng một cách độc lập. Bạn cũng nên cung cấp tài nguyên thay thế cho +cấu hình thiết bị cụ thể bằng cách nhóm chúng lại trong những thư mục tài nguyên đích danh. Trong +thời gian chạy, Android sẽ sử dụng tài nguyên phù hợp dựa trên cấu hình hiện tại. Ví +dụ, bạn có thể muốn cung cấp một bố trí UI khác phụ thuộc vào kích cỡ màn hình hoặc các xâu +khác nhau phụ thuộc vào thiết đặt ngôn ngữ.

+ +

Sau khi ngoại hiện hóa các tài nguyên ứng dụng của mình, bạn có thể truy cập chúng +bằng cách sử dụng các ID tài nguyên được khởi tạo trong lớp {@code R} của dự án của bạn. Cách sử dụng +tài nguyên trong ứng dụng của bạn được trình bày trong phần Truy cập +Tài nguyên. Tài liệu này trình bày với bạn cách nhóm các tài nguyên lại trong dự án Android của bạn và +cung cấp tài nguyên thay thế cho những cấu hình thiết bị cụ thể.

+ + +

Nhóm các Loại Tài nguyên lại

+ +

Bạn nên đặt từng loại tài nguyên vào một thư mục con cụ thể trong thư mục +{@code res/} dự án của mình. Ví dụ, sau đây là phân cấp tệp của một dự án đơn giản:

+ +
+MyProject/
+    src/  
+        MyActivity.java  
+    res/
+        drawable/  
+            graphic.png  
+        layout/  
+            main.xml
+            info.xml
+        mipmap/  
+            icon.png 
+        values/  
+            strings.xml  
+
+ +

Như bạn có thể thấy trong ví dụ này, thư mục {@code res/} chứa tất cả tài nguyên (trong +các thư mục con): một tài nguyên hình ảnh, hai tài nguyên bố trí, các thư mục{@code mipmap/} cho biểu tượng của trình khởi chạy +, và một tệp tài nguyên xâu. Tên thư mục +tài nguyên có vai trò quan trọng và được mô tả trong bảng 1.

+ +

Lưu ý: Để biết thêm thông tin về cách sử dụng thư mục mipmap, hãy xem phần +Tổng quan về Quản lý Dự án.

+ +

Bảng 1. Các thư mục tài nguyên +được hỗ trợ bên trong thư mục {@code res/} của dự án.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Thư mụcLoại Tài nguyên
animator/Tệp XML định nghĩa các hoạt hình +tính chất.
anim/Tệp XML định nghĩa các hoạt hình +tween. (Các hoạt hình tính chất cũng có thể được lưu trong thư mục này, nhưng +thư mục {@code animator/} được ưu tiên cho hoạt hình tính chất để phân biệt giữa hai +loại này.)
color/Tệp XML định nghĩa một danh sách trạng thái các màu. Xem phần Tài nguyên +Danh sách Trạng thái Màu
drawable/

Tệp bitmap ({@code .png}, {@code .9.png}, {@code .jpg}, {@code .gif}) hoặc tệp XML +được biên dịch thành các loại tài nguyên con vẽ được sau:

+
    +
  • Tệp bitmap
  • +
  • Nine-Patche (tệp bitmap có thể thay đổi kích cỡ)
  • +
  • Danh sách trạng thái
  • +
  • Hình
  • +
  • Nội dung vẽ được hoạt hình
  • +
  • Nội dung vẽ được khác
  • +
+

Xem phần Tài nguyên Vẽ được.

+
mipmap/Tệp vẽ được cho các mật độ biểu tượng trình khởi chạy khác nhau. Để biết thêm thông tin về việc quản lý + các biểu tượng trình khởi chạy bằng thư mục {@code mipmap/}, xem phần + Tổng quan về Quản lý Dự án.
layout/Tệp XML định nghĩa một bố trí giao diện người dùng. + Xem phần Tài nguyên Bố trí.
menu/Tệp XML định nghĩa các menu ứng dụng, chẳng hạn như Menu Tùy chọn, Menu Ngữ cảnh, hoặc Menu +Con. Xem phần Tài nguyên Menu.
raw/

Tệp tùy ý để lưu trong dạng thô của chúng. Để mở những tài nguyên có một +{@link java.io.InputStream} thô này, hãy gọi {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()} bằng ID tài nguyên, chính là {@code R.raw.filename}.

+

Tuy nhiên, nếu cần truy cập tên tệp gốc và phân cấp tệp, bạn có thể xem xét +lưu một số tài nguyên trong thư mục {@code +assets/} (thay vì {@code res/raw/}). Các tệp trong {@code assets/} không được cấp +ID tài nguyên, vì thế bạn chỉ có thể đọc chúng bằng cách sử dụng {@link android.content.res.AssetManager}.

values/

Tệp XML chứa các giá trị đơn giản, chẳng hạn như xâu, số nguyên, và màu sắc.

+

Trong đó, tệp tài nguyên XML trong các thư mục con {@code res/} khác định nghĩa một tài nguyên đơn lẻ +dựa trên tên tệp XML, tệp trong thư mục {@code values/} sẽ mô tả nhiều nguồn. +Đối với tệp trong thư mục này, mỗi phần tử con của phần tử {@code <resources>} lại định nghĩa một tài nguyên +duy nhất. Ví dụ, phần tử {@code <string>} tạo tài nguyên +{@code R.string} và phần tử {@code <color>} tạo tài nguyên {@code R.color} +.

+

Vì mỗi tài nguyên được định nghĩa bằng phần tử XML của chính nó, bạn có thể đặt tên tệp +theo cách mình muốn và đặt các loại tài nguyên khác nhau vào một tệp. Tuy nhiên, để giải thích rõ, bạn có thể +muốn đặt các loại tài nguyên duy nhất vào những tệp khác nhau. Ví dụ, sau đây là một số quy ước +tên tệp cho các tài nguyên mà bạn có thể tạo trong thư mục này:

+ +

Xem các phần Tài nguyên Xâu, + Tài nguyên Kiểu, và + các Loại Tài nguyên khác.

+
xml/Tệp XML tùy ý mà có thể được đọc vào thời gian chạy bằng cách gọi {@link +android.content.res.Resources#getXml(int) Resources.getXML()}. Các tệp cấu hình XML khác nhau +phải được lưu ở đây, chẳng hạn như một cấu hình có thể tìm kiếm. +
+ +

Chú ý: Không được lưu tệp tài nguyên trực tiếp vào trong thư mục +{@code res/}—nó sẽ gây ra lỗi với trình biên dịch.

+ +

Để biết thêm thông tin về các loại tài nguyên, hãy xem tài liệu Các Loại Tài nguyên.

+ +

Tài nguyên mà bạn lưu trong thư mục con được định nghĩa trong bảng 1 là những tài nguyên "mặc định" +của bạn. Cụ thể, những tài nguyên này định nghĩa thiết kế và nội dung mặc định cho ứng dụng của bạn. +Tuy nhiên, các loại thiết bị dựa trên nền tảng Android khác nhau có thể gọi các loại tài nguyên khác nhau. +Ví dụ, nếu một thiết bị có một màn hình lớn hơn bình thường, khi đó bạn nên cung cấp +các tài nguyên bố trí khác nhau để tận dụng diện tích màn hình tăng thêm. Hoặc, nếu một thiết bị có +thiết đặt ngôn ngữ khác, khi đó bạn nên cung cấp các tài nguyên xâu khác để biên dịch +văn bản trong giao diện người dùng của mình. Để cung cấp những tài nguyên khác nhau này cho các cấu hình +thiết bị khác nhau, bạn cần cung cấp tài nguyên thay thế bên cạnh những tài nguyên +mặc định của mình.

+ + +

Cung cấp Tài nguyên Thay thế

+ + +
+ +

+Hình 1. Hai thiết bị khác nhau, mỗi thiết bị sử dụng các tài nguyên bố trí khác nhau.

+
+ +

Hầu như mọi ứng dụng đều nên cung cấp các tài nguyên thay thế để hỗ trợ những cấu hình +thiết bị cụ thể. Ví dụ, bạn nên bao gồm các tài nguyên vẽ được thay thế cho các mật độ +màn hình khác nhau và tài nguyên xâu thay thế cho các ngôn ngữ khác nhau. Vào thời gian chạy, Android +sẽ phát hiện cấu hình thiết bị hiện tại và tải các tài nguyên +tương ứng cho ứng dụng của bạn.

+ +

Để quy định các phương án thay thế theo cấu hình cụ thể cho một tập hợp tài nguyên:

+
    +
  1. Tạo một thư mục mới trong {@code res/} có tên theo dạng {@code +<resources_name>-<config_qualifier>}. +
      +
    • {@code <resources_name>} là tên thư mục của các tài nguyên mặc định tương ứng +(được định nghĩa trong bảng 1).
    • +
    • {@code <qualifier>} là tên quy định một cấu hình riêng +mà những tài nguyên này sẽ được sử dụng cho nó (được định nghĩa trong bảng 2).
    • +
    +

    Bạn có thể nối nhiều hơn một {@code <qualifier>}. Tách riêng từng cái +bằng một nét gạch.

    +

    Chú ý: Khi nối nhiều hạn định, bạn phải +đặt chúng theo cùng thứ tự liệt kê trong bảng 2. Nếu các hạn định được xếp thứ tự +sai, tài nguyên sẽ bị bỏ qua.

    +
  2. +
  3. Lưu các tài nguyên thay thế tương ứng vào thư mục mới này. Tệp tài nguyên phải được +đặt tên đúng như các tệp tài nguyên mặc định.
  4. +
+ +

Ví dụ, sau đây là một số tài nguyên mặc định và thay thế:

+ +
+res/
+    drawable/   
+        icon.png
+        background.png    
+    drawable-hdpi/  
+        icon.png
+        background.png  
+
+ +

Hạn định {@code hdpi} cho biết rằng các tài nguyên trong thư mục đó áp dụng cho những thiết bị có +màn hình mật độ cao. Hình ảnh trong từng thư mục vẽ được này được định cỡ cho một mật độ +màn hình cụ thể, nhưng tên tệp thì +giống hệt. Bằng cách này, ID tài nguyên mà bạn sử dụng để tham chiếu {@code icon.png} hoặc hình ảnh {@code +background.png} luôn như nhau, nhưng Android sẽ chọn +phiên bản của từng tài nguyên cho khớp tốt nhất với thiết bị hiện tại, bằng cách so sánh thông tin cấu hình thiết bị +với các hạn định về tên thư mục tài nguyên.

+ +

Android hỗ trợ một vài hạn định cấu hình và bạn có thể +thêm nhiều hạn định vào một tên thư mục, bằng cách tách riêng từng hạn định bằng một nét gạch. Bảng 2 +liệt kê các hạn định cấu hình hợp lệ, theo thứ tự ưu tiên—nếu bạn sử dụng nhiều +hạn định cho một thư mục tài nguyên, bạn phải thêm chúng vào tên thư mục theo thứ tự được liệt kê trong +bảng.

+ + +

Bảng 2. Tên của hạn định +cấu hình.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cấu hìnhGiá trị Hạn địnhMô tả
MCC và MNCVí dụ:
+ mcc310
+ mcc310-mnc004
+ mcc208-mnc00
+ v.v. +
+

Mã quốc gia di động (MCC), đằng sau có thể là mã mạng di động (MNC) + từ thẻ SIM trong thiết bị. Ví dụ, mcc310 ở Hoa Kỳ đối với mọi nhà mạng, + mcc310-mnc004 ở Hoa Kỳ đối với Verizon, và mcc208-mnc00 ở Pháp đối với + Orange.

+

Nếu thiết bị sử dụng một kết nối vô tuyến (điện thoại GSM), các giá trị MCC và MNC sẽ lấy + từ thẻ SIM.

+

Bạn cũng có thể sử dụng chỉ MCC (ví dụ, để đưa các tài nguyên +pháp lý theo quốc gia cụ thể vào ứng dụng của bạn). Nếu bạn cần quy định chỉ dựa trên ngôn ngữ, hãy sử dụng hạn định +ngôn ngữ và khu vực để thay thế (được trình bày ở phần tiếp theo). Nếu bạn quyết định sử dụng hạn định MCC và +MNC, bạn nên cẩn thận và kiểm tra xem nó có hoạt động như kỳ vọng không.

+

Ngoài ra, cũng xem các trường cấu hình {@link +android.content.res.Configuration#mcc}, và {@link +android.content.res.Configuration#mnc}, tương ứng cho biết mã quốc gia di động và +mã mạng di động hiện tại.

+
Ngôn ngữ và khu vựcVí dụ:
+ en
+ fr
+ en-rUS
+ fr-rFR
+ fr-rCA
+ v.v. +

Ngôn ngữ được định nghĩa bằng một mã ngôn ngữ ISO + 639-1 gồm hai chữ cái, có thể theo sau là một mã khu vực + ISO + 3166-1-alpha-2 dài hai chữ cái (đằng trước là "{@code r}" chữ thường). +

+ Các mã không phân biệt chữ hoa/thường; tiền tố {@code r} được sử dụng để + phân biệt phần khu vực. + Bạn không thể chỉ quy định một khu vực.

+

Điều này có thể thay đổi trong suốt vòng đời +ứng dụng của bạn nếu người dùng thay đổi ngôn ngữ của mình trong cài đặt hệ thống. Xem phần Xử lý Thay đổi Thời gian chạy để biết thông tin về +ảnh hưởng có thể có của thay đổi này tới ứng dụng của bạn trong thời gian chạy.

+

Xem phần Bản địa hóa để biết hướng dẫn đầy đủ về việc bản địa hóa +ứng dụng của bạn cho các ngôn ngữ khác.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#locale}, trong đó +cho biết địa phương hiện tại.

+
Chỉ hướng Bố tríldrtl
+ ldltr
+

Chỉ hướng bố trí của ứng dụng của bạn. {@code ldrtl} có nghĩa là "chỉ-hướng-bố-trí-phải-qua-trái". + {@code ldltr} có nghĩa là "chỉ-hướng-bố-trí-trái-qua-phải" và là giá trị không biểu thị mặc định. +

+

Điều này có thể áp dụng cho bất kỳ tài nguyên nào, chẳng hạn như bố trí, nội dung vẽ được hoặc giá trị. +

+

Ví dụ, nếu bạn muốn cung cấp một bố trí cụ thể cho ngôn ngữ Ả-rập và một + bố trí chung nào đó cho bất kỳ ngôn ngữ “phải-qua-trái" nào khác (như chữ Ba Tư hoặc Do Thái), vậy bạn sẽ phải: +

+
+res/
+    layout/   
+        main.xml  (Default layout)
+    layout-ar/  
+        main.xml  (Specific layout for Arabic)
+    layout-ldrtl/  
+        main.xml  (Any "right-to-left" language, except
+                  for Arabic, because the "ar" language qualifier
+                  has a higher precedence.)
+
+

Lưu ý: Để kích hoạt các tính năng bố trí phải-qua-trái + cho ứng dụng của mình, bạn phải đặt {@code + supportsRtl} thành {@code "true"} và đặt {@code targetSdkVersion} thành 17 trở lên.

+

Được thêm trong API mức 17.

+
smallestWidthsw<N>dp

+ Ví dụ:
+ sw320dp
+ sw600dp
+ sw720dp
+ v.v. +
+

Kích cỡ cơ bản của một màn hình, thể hiện bằng kích thước ngắn nhất của khu vực màn hình +khả dụng. Cụ thể, smallestWidth của thiết bị bằng khoảng ngắn nhất giữa chiều cao và chiều rộng +khả dụng của màn hình (bạn cũng có thể gọi là "chiều rộng nhỏ nhất có thể" cho màn hình). Bạn có thể +sử dụng hạn định này để đảm bảo rằng, không phụ thuộc vào hướng hiện tại của màn hình, ứng dụng +của bạn có ít nhất {@code <N>} dp chiều rộng khả dụng cho UI của mình.

+

Ví dụ, nếu bố trí của bạn yêu cầu rằng kích thước nhỏ nhất của khu vực màn hình tối thiểu +phải luôn bằng 600 dp, vậy bạn có thể sử dụng hạn định này để tạo các tài nguyên bố trí, {@code +res/layout-sw600dp/}. Hệ thống sẽ chỉ sử dụng những tài nguyên này khi kích thước nhỏ nhất của +màn hình khả dụng tối thiểu bằng 600dp, không phụ thuộc vào cạnh 600dp là chiều cao hay chiều rộng +theo nhận thức của người dùng. SmallestWidth là đặc trưng kích cỡ màn hình cố định của thiết bị; smallestWidth của +thiết bị không thay đổi khi hướng của màn hình thay đổi.

+

SmallestWidth của một thiết bị sẽ xem xét cả trang trí màn hình và UI hệ thống. Ví +dụ, nếu thiết bị có một số phần tử UI cố định trên màn hình mà chiếm mất khoảng trống dọc +theo trục smallestWidth, hệ thống sẽ khai báo smallestWidth nhỏ hơn kích cỡ màn hình +thực tế, bởi chúng là những điểm ảnh màn hình không khả dụng cho UI của bạn. Vì thế, giá trị mà bạn sử dụng +nên là kích thước nhỏ nhất thực tế mà bố trí của bạn yêu cầu (thông thường, giá trị này bằng +"chiều rộng nhỏ nhất" mà bố trí của bạn hỗ trợ, không phụ thuộc vào hướng hiện tại của màn hình).

+

Một số giá trị mà bạn có thể sử dụng ở đây đối với các kích cỡ màn hình phổ biến:

+
    +
  • 320, cho các thiết bị có cấu hình màn hình như: +
      +
    • 240x320 ldpi (thiết bị cầm tay QVGA)
    • +
    • 320x480 mdpi (thiết bị cầm tay)
    • +
    • 480x800 hdpi (thiết bị cầm tay mật độ cao)
    • +
    +
  • +
  • 480, đối với những màn hình như 480x800 mdpi (máy tính bảng/thiết bị cầm tay).
  • +
  • 600, đối với những màn hình như 600x1024 mdpi (máy tính bảng 7").
  • +
  • 720, đối với những màn hình như 720x1280 mdpi (máy tính bảng 10").
  • +
+

Khi ứng dụng của bạn cung cấp nhiều thư mục tài nguyên với những giá trị khác nhau cho + hạn định smallestWidth, hệ thống sẽ sử dụng hạn định gần nhất với (không vượt quá) +smallestWidth của thiết bị.

+

Được thêm trong API mức 13.

+

Xem thêm thuộc tính {@code +android:requiresSmallestWidthDp}, trong đó khai báo smallestWidth tối thiểu mà ứng dụng của bạn +tương thích với, và trường cấu hình {@link +android.content.res.Configuration#smallestScreenWidthDp}, trong đó lưu trữ giá trị +smallestWidth của thiết bị.

+

Để biết thêm thông tin về việc thiết kế cho các màn hình khác nhau và sử dụng hạn định +này, hãy xem hướng dẫn dành cho nhà phát triển Hỗ trợ +Nhiều Màn hình.

+
Chiều rộng khả dụngw<N>dp

+ Ví dụ:
+ w720dp
+ w1024dp
+ v.v. +
+

Quy định một chiều rộng màn hình khả dụng tối thiểu theo đơn vị {@code dp} mà tại đó, tài nguyên + nên được sử dụng—được định nghĩa bởi giá trị <N>. Giá trị + cấu hình này sẽ thay đổi khi hướng + thay đổi giữa khổ ngang và dọc để khớp với chiều rộng thực tế hiện tại.

+

Khi ứng dụng của bạn cung cấp nhiều thư mục tài nguyên với những giá trị khác nhau + cho cấu hình này, hệ thống sẽ sử dụng giá trị gần nhất với (không vượt quá) + chiều rộng hiện tại của màn hình. Giá trị + ở đây xét cả trang trí trên màn hình, vì thế nếu thiết bị có một số + phần tử UI cố định ở cạnh trái hoặc phải của màn hình, nó + sẽ sử dụng một giá trị cho chiều rộng nhỏ hơn kích cỡ màn hình thực sự, dùng + cho những phần tử UI này và làm giảm khoảng trống khả dụng của ứng dụng.

+

Được thêm trong API mức 13.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#screenWidthDp} + mà chứa chiều rộng màn hình hiện tại.

+

Để biết thêm thông tin về việc thiết kế cho các màn hình khác nhau và sử dụng hạn định +này, hãy xem hướng dẫn dành cho nhà phát triển Hỗ trợ +Nhiều Màn hình.

+
Chiều cao khả dụngh<N>dp

+ Ví dụ:
+ h720dp
+ h1024dp
+ v.v. +
+

Quy định chiều cao màn hình khả dụng tối thiểu theo đơn vị "dp" mà tại đó tài nguyên + nên được sử dụng—được định nghĩa bởi giá trị <N>. Giá trị + cấu hình này sẽ thay đổi khi hướng + thay đổi giữa khổ ngang và dọc để khớp với chiều cao thực tế hiện tại.

+

Khi ứng dụng của bạn cung cấp nhiều thư mục tài nguyên với những giá trị khác nhau + cho cấu hình này, hệ thống sẽ sử dụng giá trị gần nhất với (không vượt quá) + chiều cao hiện tại của màn hình. Giá trị + ở đây xét cả trang trí trên màn hình, vì thế nếu thiết bị có một số + phần tử UI cố định trên cạnh trên hoặc dưới của màn hình, nó sẽ sử dụng + một giá trị cho chiều cao nhỏ hơn kích cỡ màn hình thực sự, dùng + cho những phần tử UI này và làm giảm khoảng trống khả dụng của ứng dụng. Trang trí + trên màn hình mà không cố định (chẳng hạn như thanh trạng thái của điện thoại mà có thể được + ẩn khi ở toàn màn hình) không được xét ở đây, cả + những trang trí trên cửa sổ như thanh tiêu đề hay thanh hành động cũng vậy, vì thế ứng dụng phải được chuẩn bị để + xử lý một khoảng trống nhỏ hơn mức mà chúng quy định. +

Được thêm trong API mức 13.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#screenHeightDp} + mà chứa chiều rộng màn hình hiện tại.

+

Để biết thêm thông tin về việc thiết kế cho các màn hình khác nhau và sử dụng hạn định +này, hãy xem hướng dẫn dành cho nhà phát triển Hỗ trợ +Nhiều Màn hình.

+
Kích cỡ màn hình + small
+ normal
+ large
+ xlarge +
+
    +
  • {@code small}: Các màn hình có kích cỡ tương tự như màn hình + QVGA mật độ thấp. Kích cỡ bố trí tối thiểu đối với một màn hình nhỏ + bằng xấp xỉ 320x426 đơn vị dp. Các ví dụ như QVGA mật độ thấp và VGA mật độ + cao.
  • +
  • {@code normal}: Các màn hình có kích cỡ tương tự như màn hình + HVGA mật độ trung bình. Kích cỡ bố trí tối thiểu + đối với một màn hình bình thường bằng xấp xỉ 320x470 đơn vị dp. Ví dụ + về những màn hình như vậy là WQVGA mật độ thấp, HVGA mật độ trung bình, WVGA + mật độ cao.
  • +
  • {@code large}: Các màn hình có kích cỡ tương tự như màn hình + VGA mật độ trung bình. + Kích cỡ bố trí tối thiểu đối với một màn hình lớn bằng xấp xỉ 480x640 đơn vị dp. + Ví dụ như các màn hình mật độ trung bình VGA và WVGA.
  • +
  • {@code xlarge}: Các màn hình lớn hơn đáng kể so với màn hình + HVGA mật độ trung bình truyền thống. Kích cỡ bố trí tối thiểu đối với một màn hình siêu lớn + bằng xấp xỉ 720x960 đơn vị dp. Trong hầu hết trường hợp, những thiết bị có màn hình + siêu lớn sẽ quá lớn để mang trong túi và gần như là + thiết bị kiểu máy tính bảng. Được thêm trong API mức 9.
  • +
+

Lưu ý: Việc sử dụng một hạn định kích cỡ không hàm ý rằng các +tài nguyên chỉ áp dụng cho màn hình có kích cỡ đó. Nếu bạn không cung cấp cho các tài nguyên +thay thế với các hạn định khớp tốt hơn với cấu hình thiết bị hiện tại, hệ thống có thể sử dụng +bất kỳ tài nguyên nào phù hợp nhất.

+

Chú ý: Nếu tất cả tài nguyên của bạn sử dụng một hạn định kích cỡ +lớn hơn màn hình hiện tại, hệ thống sẽ không sử dụng chúng và +ứng dụng của bạn sẽ bị lỗi vào thời gian chạy (ví dụ, nếu tất cả tài nguyên bố trí được gắn thẻ hạn định {@code +xlarge} nhưng thiết bị lại có màn hình kích cỡ bình thường).

+

Được thêm trong API mức 4.

+ +

Xem Hỗ trợ Nhiều +Màn hình để biết thêm thông tin.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#screenLayout}, +ở đó cho biết màn hình là màn hình nhỏ, bình thường, +hay lớn.

+
Tỷ lệ màn hình + long
+ notlong +
+
    +
  • {@code long}: Màn hình dài, chẳng hạn như WQVGA, WVGA, FWVGA
  • +
  • {@code notlong}: Màn hình không dài, chẳng hạn như QVGA, HVGA và VGA
  • +
+

Được thêm trong API mức 4.

+

Giá trị này thuần túy được dựa trên tỷ lệ khung ảnh của màn hình (màn hình "dài" sẽ rộng hơn). Nó +không liên quan tới hướng của màn hình.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#screenLayout}, +ở đó cho biết màn hình có dài không.

+
Hướng của màn hình + port
+ land +
+
    +
  • {@code port}: Thiết bị ở hướng đứng (thẳng đứng)
  • +
  • {@code land}: Thiết bị ở khổ ngang (nằm ngang)
  • + +
+

Giá trị này có thể thay đổi trong suốt vòng đời ứng dụng của bạn nếu người dùng xoay +màn hình. Xem phần Xử lý Thay đổi Thời gian chạy để biết thông tin về +ảnh hưởng của điều này tới ứng dụng của bạn trong thời gian chạy.

+

Xem thêm trường cấu hình {@link android.content.res.Configuration#orientation}, trong đó +cho biết hướng thiết bị hiện tại.

+
Chế độ UI + car
+ desk
+ television
+ appliance + watch +
+
    +
  • {@code car}: Thiết bị đang hiển thị trong đế gắn trên ô-tô
  • +
  • {@code desk}: Thiết bị đang hiển thị trong đế gắn trên bàn
  • +
  • {@code television}: Thiết bị đang hiển thị trên một TV, mang đến một + trải nghiệm "10 foot" (3 mét) trong đó UI của nó nằm trên một màn hình lớn + cách xa người dùng, được định hướng chủ yếu quanh DPAD hoặc cách + tương tác không sử dụng con trỏ khác
  • +
  • {@code appliance}: Thiết bị đang đóng vai trò như một dụng cụ không + có màn hình hiển thị
  • +
  • {@code watch}: Thiết bị có một màn hình hiển thị và được đeo trên cổ tay
  • +
+

Được thêm trong API mức 8, TV được thêm trong API 13, đồng hồ được thêm trong API 20.

+

Để biết thông tin về cách ứng dụng của bạn hồi đáp khi thiết bị được cắm vào hoặc + rút khỏi đế, hãy đọc Xác định +và Theo dõi Trạng thái và Loại Đế.

+

Giá trị này có thể thay đổi trong suốt vòng đời ứng dụng của bạn nếu người dùng đặt +thiết bị vào đế. Bạn có thể kích hoạt hoặc vô hiệu hóa một số chế độ này bằng cách sử dụng {@link +android.app.UiModeManager}. Xem phần Xử lý Thay đổi Thời gian chạy để +biết thông tin về ảnh hưởng của điều này tới ứng dụng của bạn trong thời gian chạy.

+
Chế độ ban đêm + night
+ notnight +
+
    +
  • {@code night}: Thời gian ban đêm
  • +
  • {@code notnight}: Thời gian ban ngày
  • +
+

Được thêm trong API mức 8.

+

Giá trị này có thể thay đổi trong suốt vòng đời ứng dụng của bạn nếu chế độ ban đêm được để ở +chế độ tự động (mặc định), trong trường hợp đó chế độ sẽ thay đổi dựa vào thời gian trong ngày. Bạn có thể kích hoạt +hoặc vô hiệu hóa chế độ này bằng cách sử dụng {@link android.app.UiModeManager}. Xem phần Xử lý Thay đổi Thời gian chạy để biết thông tin về ảnh hưởng của điều này tới +ứng dụng của bạn trong thời gian chạy.

+
Mật độ điểm ảnh màn hình (dpi) + ldpi
+ mdpi
+ hdpi
+ xhdpi
+ xxhdpi
+ xxxhdpi
+ nodpi
+ tvdpi +
+
    +
  • {@code ldpi}: Màn hình mật độ thấp; xấp xỉ 120dpi.
  • +
  • {@code mdpi}: Màn hình mật độ trung bình (trên HVGA truyền thống); xấp xỉ +160dpi.
  • +
  • {@code hdpi}: Màn hình mật độ cao; xấp xỉ 240dpi.
  • +
  • {@code xhdpi}: Màn hình mật độ siêu cao; xấp xỉ 320dpi. Được thêm trong API +Mức 8
  • +
  • {@code xxhdpi}: Màn hình mật độ siêu siêu cao; xấp xỉ 480dpi. Được thêm trong API +Mức 16
  • +
  • {@code xxxhdpi}: Mật độ siêu siêu siêu cao sử dụng (chỉ biểu tượng trình khởi chạy, xem + ghi chú + trong Hỗ trợ Nhiều Màn hình); xấp xỉ 640dpi. Được thêm trong API +Mức 18
  • +
  • {@code nodpi}: Loại này có thể được sử dụng cho tài nguyên bitmap mà bạn không muốn được định cỡ +cho khớp với mật độ của thiết bị.
  • +
  • {@code tvdpi}: Màn hình trong khoảng giữa mdpi và hdpi; xấp xỉ 213dpi. Đây +không được coi là nhóm mật độ "cơ bản". Nó được dành chủ yếu cho TV và hầu hết +các ứng dụng không cần nó—với điều kiện các tài nguyên mdpi và hpdi đủ cho hầu hết ứng dụng +và hệ thống sẽ định cỡ chúng cho phù hợp. Hạn định này đã được giới thiệu với API mức 13.
  • +
+

Có tỷ lệ định cỡ 3:4:6:8:12:16 giữa sáu mật độ cơ bản (bỏ qua mật độ +tvdpi). Vì thế, một tệp bimap 9x9 trong ldpi sẽ bằng 12x12 trong mdpi, 18x18 trong hdpi, 24x24 trong xhdpi, v.v. +

+

Nếu bạn quyết định rằng tài nguyên hình ảnh của mình không đủ đẹp trên TV hoặc +một số thiết bị khác và muốn thử tài nguyên tvdpi, hệ số định cỡ sẽ bằng 1,33*mdpi. Ví +dụ, một hình ảnh 100px x 100px đối với màn hình mdpi sẽ bằng 133px x 133px đối với tvdpi.

+

Lưu ý: Việc sử dụng một hạn định mật độ không hàm ý rằng các +tài nguyên chỉ áp dụng cho màn hình có mật độ đó. Nếu bạn không cung cấp cho các tài nguyên +thay thế với các hạn định khớp tốt hơn với cấu hình thiết bị hiện tại, hệ thống có thể sử dụng +bất kỳ tài nguyên nào phù hợp nhất.

+

Xem Hỗ trợ Nhiều +Màn hình để biết thêm thông tin về cách xử lý các mật độ màn hình khác nhau và cách Android +có thể định cỡ bitmap của mình cho vừa với mật độ hiện tại.

+
Loại màn hình cảm ứng + notouch
+ finger +
+
    +
  • {@code notouch}: Thiết bị không có màn hình cảm ứng.
  • +
  • {@code finger}: Thiết bị có màn hình cảm ứng để + được sử dụng thông qua tương tác hướng của ngón tay của người dùng.
  • +
+

Xem thêm trường cấu hình {@link android.content.res.Configuration#touchscreen}, +nó cho biết loại màn hình cảm ứng trên thiết bị.

+
Sự sẵn có của bàn phím + keysexposed
+ keyshidden
+ keyssoft +
+
    +
  • {@code keysexposed}: Thiết bị có sẵn một bàn phím. Nếu thiết bị có một +bàn phím mềm được kích hoạt (có khả năng), giá trị này có thể được sử dụng khi bàn phím cứng +không hiển thị trước người dùng, ngay cả khi thiết bị không có bàn phím cứng. Nếu không có +bàn phím mềm hoặc bàn phím mềm bị vô hiệu hóa, khi đó giá trị này chỉ được sử dụng khi một bàn phím cứng được +hiển thị.
  • +
  • {@code keyshidden}: Thiết bị có sẵn một bàn phím cứng nhưng nó bị +ẩn đi thiết bị không có bàn phím mềm được kích hoạt.
  • +
  • {@code keyssoft}: Thiết bị có một bàn phím mềm được kích hoạt dù nó có +hiển thị hay không.
  • +
+

Nếu bạn cung cấp các tài nguyên keysexposed, nhưng không cung cấp tài nguyên keyssoft +, hệ thống sẽ sử dụng tài nguyên keysexposed mà không phụ thuộc vào việc có hiển thị +bàn phím hay không, miễn là hệ thống có kích hoạt một bàn phím mềm.

+

Giá trị này có thể thay đổi trong vòng đời ứng dụng của bạn nếu người dùng mở một bàn phím +cứng. Xem phần Xử lý Thay đổi Thời gian chạy để biết thông tin về +ảnh hưởng của điều này tới ứng dụng của bạn trong thời gian chạy.

+

Xem thêm các trường cấu hình {@link +android.content.res.Configuration#hardKeyboardHidden} và {@link +android.content.res.Configuration#keyboardHidden}, theo đó tương ứng cho biết mức độ hiển thị của bàn phím +cứng và mức độ hiển thị của bất kỳ loại bàn phím nào (bao gồm bàn phím mềm).

+
Phương pháp nhập liệu văn bản chính + nokeys
+ qwerty
+ 12key +
+
    +
  • {@code nokeys}: Thiết bị không có phím cứng cho việc nhập liệu văn bản.
  • +
  • {@code qwerty}: Thiết bị có một bàn phím qwerty cứng, dù nó có hiển thị với +người dùng +hay không.
  • +
  • {@code 12key}: Thiết bị có một bàn phím 12-phím cứng, dù nó có hiển thị với +người dùng hay không.
  • +
+

Xem thêm trường cấu hình {@link android.content.res.Configuration#keyboard}, trong đó +cho biết phương pháp nhập liệu văn bản chính sẵn có.

+
Phiên bản Nền tảng (Mức API)Ví dụ:
+ v3
+ v4
+ v7
+ v.v.
+

Mức API được hỗ trợ bởi thiết bị. Ví dụ, v1 đối với API mức +1 (thiết bị ở phiên bản Android 1.0 hoặc cao hơn) và v4 đối với API mức 4 (thiết bị ở phiên bản Android +1.6 hoặc cao hơn). Xem tài liệu Mức API của Android để biết thêm thông tin +về những giá trị này.

+
+ + +

Lưu ý: Một số hạn định cấu hình đã được thêm kể từ phiên bản Android +1.0, vì thế không phải tất cả phiên bản Android đều hỗ trợ tất cả hạn định. Việc sử dụng một hạn định mới sẽ hàm ý +thêm hạn định phiên bản nền tảng sao cho các thiết bị cũ hơn chắc chắn sẽ bỏ qua nó. Ví dụ, sử dụng +một hạn định w600dp sẽ tự động bao gồm hạn định v13, vì +hạn định chiều rộng khả dụng mới có trong API mức 13. Để tránh bất kỳ sự cố nào, hãy luôn đưa vào một tập hợp +các tài nguyên mặc định (tập hợp các tài nguyên không có hạn định). Để biết thêm thông tin, hãy xem phần +nói về Cung cấp Tính tương thích giữa Thiết bị với Tài nguyên +Tốt nhất.

+ + + +

Quy tắc về tên hạn định

+ +

Sau đây là một số quy tắc về việc sử dụng tên của hạn định cấu hình:

+ +
    +
  • Bạn có thể quy định nhiều hạn định cho một tập hợp đơn lẻ các tài nguyên, được tách riêng bởi dấu gạch ngang. Ví +dụ, drawable-en-rUS-land sẽ áp dụng cho các thiết bị US-English ở hướng +khổ ngang.
  • +
  • Các hạn định phải theo thứ tự liệt kê trong bảng 2. Ví +dụ: +
      +
    • Sai: drawable-hdpi-port/
    • +
    • Đúng: drawable-port-hdpi/
    • +
    +
  • +
  • Các thư mục tài nguyên thay thế không được lồng nhau. Ví dụ, bạn không được có +res/drawable/drawable-en/.
  • +
  • Các giá trị không phân biệt chữ hoa/thường. Trình biên dịch tài nguyên sẽ chuyển tên thư mục + thành chữ thường trước khi xử lý để tránh các vấn đề xảy ra trên hệ thống tệp + không phân biệt chữ hoa/thường. Bất kỳ việc đổi sang chữ hoa nào trong tên chỉ nhằm mục đích dễ đọc hơn.
  • +
  • Chỉ hỗ trợ một giá trị cho mỗi loại hạn định. Ví dụ, nếu bạn muốn sử dụng +cùng các tệp vẽ được cho tiếng Tây Ban Nha và tiếng Pháp, bạn không thể đặt tên thư mục là +drawable-rES-rFR/. Thay vào đó, bạn cần hai thư mục tài nguyên chẳng hạn như +drawable-rES/drawable-rFR/, trong đó chứa các tệp phù hợp. +Tuy nhiên, bạn không bắt buộc thực sự phải tạo bản sao các tệp như nhau ở cả hai vị trí. Thay vào đó, bạn có thể +tạo một bí danh tới một tài nguyên. Xem phần Tạo +tài nguyên bí danh ở bên dưới.
  • +
+ +

Sau khi bạn lưu tài nguyên thay thế vào các thư mục được đặt tên bằng +những hạn định này, Android sẽ tự động áp dụng các tài nguyên trong ứng dụng của bạn dựa trên +cấu hình thiết bị hiện tại. Cứ mỗi lần yêu cầu một tài nguyên, Android lại kiểm tra các thư mục tài nguyên +thay thế chứa tệp tài nguyên được yêu cầu, rồi tìm tài nguyên +so khớp phù hợp nhất (được trình bày ở bên dưới). Nếu không có tài nguyên thay thế khớp +với một cấu hình thiết bị cụ thể, khi đó Android sẽ sử dụng các tài nguyên mặc định tương ứng ( +tập hợp các tài nguyên cho một loại tài nguyên cụ thể không bao gồm hạn định +cấu hình).

+ + + +

Tạo tài nguyên bí danh

+ +

Khi bạn có một tài nguyên muốn sử dụng cho nhiều hơn một cấu hình +thiết bị (nhưng không muốn cung cấp làm tài nguyên mặc định), bạn không cần đặt +cùng tài nguyên đó vào nhiều hơn một thư mục tài nguyên thay thế. Thay vào đó, bạn có thể (trong một số trường hợp) tạo một tài nguyên +thay thế +đóng vai trò như một bí danh cho tài nguyên được lưu trong thư mục tài nguyên mặc định của bạn.

+ +

Lưu ý: Không phải tất cả tài nguyên đều đưa ra cơ chế mà theo đó bạn có thể +tạo một bí danh tới một tài nguyên khác. Cụ thể, hoạt hình, menu, tài nguyên thô và các tài nguyên +không được quy định khác trong thư mục {@code xml/} không cung cấp tính năng này.

+ +

Ví dụ, hãy tưởng tượng bạn có một biểu tượng ứng dụng, {@code icon.png}, và cần phiên bản duy nhất của +nó cho các bản địa khác nhau. Tuy nhiên, hai bản địa English-Canadian và French-Canadian, cần +sử dụng cùng phiên bản. Bạn có thể giả sử rằng mình cần sao chép cùng hình ảnh +vào thư mục tài nguyên cho cả English-Canadian và French-Canadian, nhưng điều đó +không đúng. Thay vào đó, bạn có thể lưu hình ảnh được sử dụng cho cả hai thành {@code icon_ca.png} (bất kỳ +tên nào khác ngoài {@code icon.png}) và đặt +nó vào thư mục {@code res/drawable/} mặc định. Sau đó, tạo một tệp {@code icon.xml} trong {@code +res/drawable-en-rCA/} và {@code res/drawable-fr-rCA/} tham chiếu tới tài nguyên {@code icon_ca.png} +bằng cách sử dụng phần tử {@code <bitmap>}. Điều này cho phép bạn lưu trữ chỉ một phiên bản của tệp +PNG và hai tệp XML nhỏ trỏ tới nó. (Ví dụ về tệp XML được trình bày ở bên dưới.)

+ + +

Nội dung vẽ được

+ +

Để tạo một bí danh cho một nội dung vẽ được đang tồn tại, hãy sử dụng phần tử {@code <bitmap>}. +Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/icon_ca" />
+
+ +

Nếu bạn lưu tệp này thành {@code icon.xml} (trong một thư mục tài nguyên thay thế chẳng hạn như +{@code res/drawable-en-rCA/}), nó sẽ được biên dịch vào một tài nguyên mà bạn +có thể tham chiếu như là {@code R.drawable.icon}, nhưng thực tế lại là bí danh cho tài nguyên {@code +R.drawable.icon_ca} (được lưu trong {@code res/drawable/}).

+ + +

Bố trí

+ +

Để tạo một bí danh cho một bố trí hiện tại, hãy sử dụng phần tử {@code <include>} +, được bọc trong một {@code <merge>}. Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<merge>
+    <include layout="@layout/main_ltr"/>
+</merge>
+
+ +

Nếu bạn lưu tệp này thành {@code main.xml}, nó sẽ được biên dịch thành một tài nguyên mà bạn có thể tham chiếu +như là {@code R.layout.main}, nhưng thực tế lại là một bí danh cho tài nguyên {@code R.layout.main_ltr} +.

+ + +

Xâu và các giá trị đơn giản khác

+ +

Để tạo một bí danh cho một xâu hiện có, chỉ cần sử dụng ID tài nguyên của xâu +mong muốn làm giá trị cho xâu mới. Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello</string>
+    <string name="hi">@string/hello</string>
+</resources>
+
+ +

Tài nguyên {@code R.string.hi} lúc này là một bí danh cho {@code R.string.hello}.

+ +

Các giá trị đơn giản khác cũng +hoạt động tương tự. Ví dụ, màu sắc:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="yellow">#f00</color>
+    <color name="highlight">@color/red</color>
+</resources>
+
+ + + + +

Cung cấp Tính tương thích giữa Thiết bị với Tài nguyên Tốt nhất

+ +

Để ứng dụng của bạn hỗ trợ nhiều cấu hình thiết bị, một điều rất quan trọng đó là +bạn luôn cung cấp các tài nguyên mặc định cho từng loại tài nguyên mà ứng dụng của bạn sử dụng.

+ +

Ví dụ, nếu ứng dụng của bạn hỗ trợ vài ngôn ngữ, hãy luôn bao gồm một thư mục {@code +values/} (trong đó, xâu của bạn được lưu) mà không cần một hạn định ngôn ngữ và khu vực. Nếu thay vào đó bạn đặt tất cả tệp xâu của mình +vào các thư mục có một hạn định ngôn ngữ và khu vực, khi đó ứng dụng của bạn sẽ bị lỗi khi chạy +trên một thiết bị được đặt ở một ngôn ngữ mà các xâu của bạn không hỗ trợ. Nhưng miễn là bạn cung cấp các tài nguyên +{@code values/} mặc định, khi đó ứng dụng của bạn sẽ chạy bình thường (ngay cả khi người dùng không +hiểu ngôn ngữ đó—vậy còn tốt hơn là bị lỗi).

+ +

Tương tự, nếu bạn cung cấp các tài nguyên bố trí khác nhau dựa trên hướng của màn hình, bạn nên +chọn một hướng làm mặc định của mình. Ví dụ, thay vì cung cấp tài nguyên bố trí trong {@code +layout-land/} cho khổ ngang và {@code layout-port/} cho khổ dọc, hãy để một cái làm mặc định, chẳng hạn như +{@code layout/} đối với khổ ngang và {@code layout-port/} đối với khổ dọc.

+ +

Việc cung cấp tài nguyên mặc định quan trọng không chỉ bởi ứng dụng của bạn có thể chạy trên một +cấu hình mà bạn chưa nghĩ đến, mà còn bởi các phiên bản Android mới đôi khi thêm +hạn định cấu hình mà những phiên bản cũ hơn không hỗ trợ. Nếu bạn sử dụng một hạn định tài nguyên mới, +nhưng vẫn duy trì tính tương thích về mã với các phiên bản cũ hơn của Android thì khi một phiên bản cũ hơn của +Android chạy trên ứng dụng của bạn, nó sẽ bị lỗi nếu bạn không cung cấp tài nguyên mặc định, do nó +không thể sử dụng tài nguyên được đặt tên bằng hạn định mới. Ví dụ, nếu {@code +minSdkVersion} của bạn được đặt bằng 4, và bạn xác định tất cả tài nguyên vẽ được của mình bằng cách sử dụng chế độ ban đêm ({@code night} hoặc {@code notnight}, đã được thêm trong API +Mức 8), khi đó một thiết bị API mức 4 sẽ không thể truy cập tài nguyên vẽ được của bạn và sẽ bị lỗi. Trong trường hợp +này, bạn có thể muốn {@code notnight} làm tài nguyên mặc định của mình, vì thế bạn nên loại trừ hạn định +đó sao cho tài nguyên vẽ được của bạn ở trong {@code drawable/} hoặc {@code drawable-night/}.

+ +

Vì vậy, để mang lại khả năng tương thích với thiết bị tốt nhất, hãy luôn cung cấp tài nguyên +mặc định cho những tài nguyên mà ứng dụng của bạn cần thực hiện đúng cách. Sau đó, hãy tạo tài nguyên +thay thế cho các cấu hình thiết bị cụ thể bằng cách sử dụng hạn định cấu hình.

+ +

Có một ngoại lệ đối với quy tắc này: Nếu {@code minSdkVersion} của ứng dụng của bạn bằng 4 hoặc +lớn hơn, bạn không cần đến tài nguyên vẽ được mặc định khi cung cấp tài nguyên +vẽ được thay thế bằng hạn định mật độ màn hình. Kể cả khi không có +tài nguyên vẽ được mặc định, Android cũng có thể tìm thấy kết quả khớp tốt nhất trong số các mật độ màn hình thay thế và sẽ định cỡ +bitmap nếu cần. Tuy nhiên, để có trải nghiệm tốt nhất trên tất cả thiết bị, bạn nên +cung cấp nội dung vẽ được thay thế cho cả ba loại mật độ.

+ + + +

Cách Android tìm Tài nguyên Khớp Tốt nhất

+ +

Khi bạn yêu cầu một tài nguyên mà bạn cung cấp nội dung thay thế cho nó, Android sẽ lựa chọn +tài nguyên thay thế để sử dụng vào thời gian chạy, tùy vào cấu hình thiết bị hiện tại. Để +diễn tả cách Android lựa chọn một tài nguyên thay thế, giả sử có các thư mục vẽ được sau, +mỗi thư mục lại chứa các phiên bản khác nhau của cùng hình ảnh:

+ +
+drawable/
+drawable-en/
+drawable-fr-rCA/
+drawable-en-port/
+drawable-en-notouch-12key/
+drawable-port-ldpi/
+drawable-port-notouch-12key/
+
+ +

Và giả sử cấu hình thiết bị như sau:

+ +

+Bản địa = en-GB
+Hướng màn hình = port
+Mật độ điểm ảnh màn hình = hdpi
+Loại màn hình cảm ứng = notouch
+Phương pháp nhập liệu văn bản chính = 12key +

+ +

Bằng cách so sánh cấu hình thiết bị với các tài nguyên thay thế sẵn có, Android sẽ lựa chọn +nội dung vẽ được từ {@code drawable-en-port}.

+ +

Hệ thống ra quyết định của mình về các tài nguyên nào sẽ sử dụng bằng lô-gic +sau:

+ + +
+ +

Hình 2. Lưu đồ về cách Android tìm tài nguyên +khớp tốt nhất.

+
+ + +
    +
  1. Loại bỏ các tệp tài nguyên mà trái với cấu hình thiết bị. +

    Thư mục drawable-fr-rCA/ bị loại bỏ vì nó +trái với bản địa en-GB.

    +
    +drawable/
    +drawable-en/
    +drawable-fr-rCA/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    +

    Ngoại lệ: Mật độ điểm ảnh màn hình là một hạn định không +bị loại bỏ do trái ngược. Mặc dù mật độ màn hình của thiết bị là hdpi, +drawable-port-ldpi/ không bị loại bỏ vì mọi mật độ màn hình đều +được coi là một kết quả khớp tại thời điểm này. Bạn có thể tham khảo thêm thông tin trong tài liệu Hỗ trợ Nhiều +Màn hình.

  2. + +
  3. Chọn hạn định có mức ưu tiên cao nhất (tiếp theo) trong danh sách (bảng 2). +(Bắt đầu bằng MCC, sau đó di chuyển xuống.)
  4. +
  5. Có thư mục tài nguyên nào bao gồm hạn định này không?
  6. +
      +
    • Nếu Không, hãy quay lại bước 2 và tìm với hạn định tiếp theo. (Trong ví dụ, + câu trả lời là "không" tới khi đi đến hạn định ngôn ngữ.)
    • +
    • Nếu Có, tiếp tục sang bước 4.
    • +
    + + +
  7. Loại bỏ các thư mục tài nguyên không bao gồm hạn định này. Trong ví dụ, hệ thống +sẽ loại bỏ tất cả thư mục không bao gồm hạn định ngôn ngữ:
  8. +
    +drawable/
    +drawable-en/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    +

    Ngoại lệ: Nếu hạn định đang xét là mật độ điểm ảnh màn hình, +Android sẽ chọn tùy chọn khớp gần nhất với mật độ màn hình của thiết bị. +Nhìn chung, Android ưu tiên giảm kích cỡ một hình ảnh ban đầu lớn hơn thay vì tăng kích cỡ một hình ảnh ban đầu +nhỏ hơn. Xem phần Hỗ trợ Nhiều +Màn hình.

    + + +
  9. Quay lại và lặp lại các bước 2, 3 và 4 tới khi chỉ còn lại một thư mục. Trong ví dụ, hướng +màn hình là hạn định tiếp theo nếu có kết quả khớp. +Vì thế, các tài nguyên không quy định hướng màn hình sẽ bị loại bỏ: +
    +drawable-en/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +
    +

    Thư mục còn lại là {@code drawable-en-port}.

    +
  10. +
+ +

Mặc dù quy trình này được thực thi cho từng tài nguyên được yêu cầu, hệ thống sẽ tối ưu hóa hơn nữa +một số khía cạnh. Một cách tối ưu hóa như vậy đó là sau khi biết cấu hình thiết bị, nó có thể +loại bỏ các tài nguyên thay thế mà không thể khớp được. Ví dụ, nếu ngôn ngữ cấu hình +là English ("en"), khi đó bất kỳ thư mục tài nguyên nào có hạn định ngôn ngữ được đặt thành +ngôn ngữ khác English đều sẽ không được bao gồm trong tập hợp các tài nguyên được kiểm tra (mặc dù +thư mục tài nguyên không có hạn định ngôn ngữ vẫn được bao gồm).

+ +

Khi lựa chọn tài nguyên dựa trên hạn định kích cỡ màn hình, hệ thống sẽ sử dụng các tài nguyên +được thiết kế cho màn hình nhỏ hơn màn hình hiện tại nếu không có tài nguyên nào khớp tốt hơn +(ví dụ, một màn hình kích cỡ lớn sẽ sử dụng các tài nguyên màn hình kích cỡ bình thường nếu cần). Tuy nhiên, nếu +những tài nguyên duy nhất sẵn có lại lớn hơn màn hình hiện tại, hệ thống sẽ +không sử dụng chúng và ứng dụng của bạn sẽ bị lỗi nếu không có tài nguyên nào khác khớp với cấu hình +thiết bị (ví dụ, nếu tất cả tài nguyên bố trí đều được gắn thẻ bằng hạn định {@code xlarge}, +nhưng thiết bị lại có một màn hình kích cỡ bình thường).

+ +

Lưu ý: Mức ưu tiên của hạn định (trong bảng 2) quan trọng +hơn số lượng hạn định khớp chính xác với thiết bị. Ví dụ, trong bước 4 bên trên +lựa chọn trên danh sách bao gồm ba hạn định khớp chính xác với thiết bị (hướng, loại +màn hình cảm ứng, và phương pháp nhập liệu), trong khi drawable-en chỉ có một tham số khớp +(ngôn ngữ). Tuy nhiên, ngôn ngữ có mức ưu tiên cao hơn cả ba hạn định khác này, vì thế +drawable-port-notouch-12key bị loại.

+ +

Để tìm hiểu thêm về cách sử dụng tài nguyên trong ứng dụng của bạn, hãy tiếp tục sang phần Truy cập Tài nguyên.

diff --git a/docs/html-intl/intl/vi/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/vi/guide/topics/resources/runtime-changes.jd new file mode 100644 index 0000000000000000000000000000000000000000..4a9c38ccfe6052614af5136e423ff3fced543153 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=Xử lý Thay đổi Thời gian chạy +page.tags=hoạt động,vòng đời +@jd:body + + + +

Một số cấu hình thiết bị có thể thay đổi trong thời gian chạy +(chẳng hạn như hướng màn hình, sự sẵn có của bàn phím, và ngôn ngữ). Khi sự thay đổi như vậy diễn ra, +Android sẽ khởi động lại việc chạy +{@link android.app.Activity} ({@link android.app.Activity#onDestroy()} sẽ được gọi, sau đó là {@link +android.app.Activity#onCreate(Bundle) onCreate()}). Hành vi khởi động lại được thiết kế để giúp +ứng dụng điều chỉnh phù hợp với cấu hình mới bằng cách tự động tải lại ứng dụng của bạn bằng +các tài nguyên thay thế khớp với cấu hình thiết bị mới.

+ +

Để xử lý khởi động lại cho đúng, điều quan trọng là hoạt động của bạn khôi phục lại trạng thái trước đó +của nó thông qua vòng đời Hoạt động +thông thường, trong đó Android sẽ gọi +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} trước khi nó hủy +hoạt động của bạn sao cho bạn có thể lưu dữ liệu về trạng thái của ứng dụng. Khi đó, bạn có thể khôi phục trạng thái +trong khi {@link android.app.Activity#onCreate(Bundle) onCreate()} hoặc {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.

+ +

Để kiểm tra xem ứng dụng của bạn có tự khởi động lại mà giữ nguyên trạng thái ứng dụng hay không, +bạn cần gọi ra các thay đổi cấu hình (chẳng hạn như thay đổi hướng màn hình) trong khi thực hiện các +tác vụ khác nhau trong ứng dụng của bạn. Ứng dụng của bạn sẽ có thể khởi động lại vào bất cứ lúc nào mà không bị mất +dữ liệu của người dùng hay trạng thái để xử lý các sự kiện như thay đổi cấu hình hoặc khi người dùng nhận được +một cuộc gọi đến rồi quay lại ứng dụng của bạn muộn hơn nhiều sau khi tiến trình +ứng dụng của bạn có thể đã bị hủy. Để tìm hiểu về cách bạn có thể khôi phục trạng thái hoạt động của mình, hãy đọc về Vòng đời của hoạt động.

+ +

Tuy nhiên, bạn có thể gặp phải một tình huống trong đó việc khởi động lại ứng dụng của bạn và +khôi phục phần lớn dữ liệu có thể tốn kém và tạo nên trải nghiệm người dùng kém. Trong +tình huống như vậy, bạn có hai tùy chọn:

+ +
    +
  1. Giữ lại một đối tượng trong khi thay đổi cấu hình +

    Cho phép hoạt động của bạn khởi động lại khi cấu hình thay đổi, nhưng mang theo một +đối tượng có trạng thái tới thực thể mới của hoạt động của bạn.

    + +
  2. +
  3. Tự mình xử lý thay đổi cấu hình +

    Ngăn không cho hệ thống khởi động lại hoạt động của bạn trong những thay đổi +cấu hình nhất định, nhưng nhận một lệnh gọi lại khi cấu hình thay đổi, sao cho bạn có thể cập nhật thủ công +hoạt động của mình nếu cần.

    +
  4. +
+ + +

Giữ lại một Đối tượng trong khi Thay đổi Cấu hình

+ +

Nếu việc khởi động lại hoạt động của bạn yêu cầu bạn phải khôi phục nhiều tập hợp dữ liệu lớn, hãy thiết lập lại kết nối +mạng, hoặc thực hiện các thao tác tăng cường khác, khi đó khởi động lại hoàn toàn do thay đổi cấu hình +có thể gây ra trải nghiệm người dùng chậm chạp. Đồng thời, có thể bạn sẽ không thể hoàn toàn khôi phục được +trạng thái hoạt động của mình với {@link android.os.Bundle} mà hệ thống lưu cho bạn bằng phương pháp gọi lại {@link +android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()}—nó không +được thiết kế để mang các đối tượng lớn (chẳng hạn như bitmap) và dữ liệu trong nó phải được nối tiếp hóa rồi +bỏ nối tiếp hóa, điều này có thể tiêu tốn nhiều bộ nhớ và khiến việc thay đổi cấu hình diễn ra chậm. Trong một +tình huống như vậy, bạn có thể gỡ bỏ gánh nặng khởi tạo lại hoạt động của mình bằng cách giữ lại {@link +android.app.Fragment} khi hoạt động của bạn được khởi động lại do thay đổi cấu hình. Phân đoạn này +có thể chứa các tham chiếu tới đối tượng có trạng thái mà bạn muốn giữ lại.

+ +

Khi hệ thống Android tắt hoạt động của bạn do một thay đổi cấu hình, các phân đoạn +của hoạt động mà bạn đã đánh dấu để giữ lại sẽ không bị hủy. Bạn có thể thêm các phân đoạn này vào +hoạt động của mình để giữ lại các đối tượng có trạng thái.

+ +

Để giữ lại các đối tượng có trạng thái trong một phân đoạn trong khi thay đổi cấu hình thời gian chạy:

+ +
    +
  1. Mở rộng lớp {@link android.app.Fragment} và khai báo các tham chiếu tới đối tượng + có trạng thái của bạn.
  2. +
  3. Gọi {@link android.app.Fragment#setRetainInstance(boolean)} khi phân đoạn được tạo. +
  4. +
  5. Thêm phân đoạn vào hoạt động của bạn.
  6. +
  7. Sử dụng {@link android.app.FragmentManager} để truy xuất phân đoạn khi hoạt động + được khởi động lại.
  8. +
+ +

Ví dụ, định nghĩa phân đoạn của bạn như sau:

+ +
+public class RetainedFragment extends Fragment {
+
+    // data object we want to retain
+    private MyDataObject data;
+
+    // this method is only called once for this fragment
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // retain this fragment
+        setRetainInstance(true);
+    }
+
+    public void setData(MyDataObject data) {
+        this.data = data;
+    }
+
+    public MyDataObject getData() {
+        return data;
+    }
+}
+
+ +

Chú ý: Trong khi bạn có thể lưu trữ bất kỳ đối tượng nào, bạn +không nên chuyển một đối tượng được gắn với {@link android.app.Activity}, chẳng hạn như {@link +android.graphics.drawable.Drawable}, {@link android.widget.Adapter}, {@link android.view.View} +hay bất kỳ đối tượng nào khác đi kèm với một {@link android.content.Context}. Nếu bạn làm vậy, nó sẽ +rò rỉ tất cả dạng xem và tài nguyên của thực thể hoạt động gốc. (Rò rỉ tài nguyên +có nghĩa là ứng dụng của bạn duy trì việc lưu giữ tài nguyên và chúng không thể được thu dọn bộ nhớ rác, vì thế +rất nhiều bộ nhớ có thể bị mất.)

+ +

Khi đó, hãy sử dụng {@link android.app.FragmentManager} để thêm phân đoạn vào hoạt động. +Bạn có thể thu được đối tượng dữ liệu từ phân đoạn khi hoạt động bắt đầu lại trong khi +thay đổi cấu hình thời gian chạy. Ví dụ, định nghĩa hoạt động của bạn như sau:

+ +
+public class MyActivity extends Activity {
+
+    private RetainedFragment dataFragment;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        // find the retained fragment on activity restarts
+        FragmentManager fm = getFragmentManager();
+        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
+
+        // create the fragment and data the first time
+        if (dataFragment == null) {
+            // add the fragment
+            dataFragment = new DataFragment();
+            fm.beginTransaction().add(dataFragment, “data”).commit();
+            // load the data from the web
+            dataFragment.setData(loadMyData());
+        }
+
+        // the data is available in dataFragment.getData()
+        ...
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        // store the data in the fragment
+        dataFragment.setData(collectMyLoadedData());
+    }
+}
+
+ +

Trong ví dụ này, {@link android.app.Activity#onCreate(Bundle) onCreate()} thêm một phân đoạn +hoặc khôi phục một tham chiếu đến nó. {@link android.app.Activity#onCreate(Bundle) onCreate()} cũng +lưu trữ đối tượng có trạng thái bên trong thực thể phân đoạn đó. +{@link android.app.Activity#onDestroy() onDestroy()} cập nhật đối tượng có trạng thái bên trong +thực thể phân đoạn được giữ lại.

+ + + + + +

Tự mình Xử lý Thay đổi Cấu hình

+ +

Nếu ứng dụng của bạn không cần cập nhật các tài nguyên trong một thay đổi +cấu hình cụ thể bạn có giới hạn về hiệu năng yêu cầu bạn phải +tránh khởi động lại hoạt động, khi đó bạn có thể khai báo rằng hoạt động của bạn tự mình xử lý thay đổi cấu hình +, làm vậy sẽ tránh cho hệ thống khởi động lại hoạt động của bạn.

+ +

Lưu ý: Việc tự mình xử lý thay đổi cấu hình có thể khiến việc +sử dụng các tài nguyên thay thế khó khăn hơn nhiều, vì hệ thống không tự động áp dụng chúng +cho bạn. Kỹ thuật này nên được coi là giải pháp cuối cùng khi bạn phải tránh khởi động lại do một +thay đổi cấu hình và không được khuyến cáo đối với hầu hết ứng dụng.

+ +

Để khai báo rằng hoạt động của bạn xử lý một thay đổi cấu hình, hãy chỉnh sửa phần tử {@code <activity>} phù hợp trong +tệp bản kê khai của bạn để bao gồm thuộc tính {@code +android:configChanges} với một giá trị có chức năng biểu diễn cấu hình mà bạn muốn +xử lý. Các giá trị có thể được liệt kê trong tài liệu dành cho thuộc tính {@code +android:configChanges} (các giá trị thường được sử dụng nhất là {@code "orientation"} để +ngăn khởi động lại khi hướng màn hình thay đổi và {@code "keyboardHidden"} để ngăn +khởi động lại khi tính sẵn có của bàn phím thay đổi). Bạn có thể khai báo nhiều giá trị cấu hình trong +thuộc tính bằng cách tách chúng bằng một ký tự {@code |} đường dẫn nối.

+ +

Ví dụ, đoạn mã bản kê khai sau khai báo một hoạt động có chức năng xử lý cả +thay đổi về hướng màn hình và thay đổi về tính sẵn có của bàn phím:

+ +
+<activity android:name=".MyActivity"
+          android:configChanges="orientation|keyboardHidden"
+          android:label="@string/app_name">
+
+ +

Lúc này, khi một trong những cấu hình này thay đổi, {@code MyActivity} không khởi động lại. +Thay vào đó, {@code MyActivity} nhận được một lệnh gọi tới {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Phương pháp này +được chuyển bởi một đối tượng {@link android.content.res.Configuration} mà quy định +cấu hình thiết bị mới. Bằng cách đọc các trường trong {@link android.content.res.Configuration}, +bạn có thể xác định cấu hình mới và thực hiện những thay đổi phù hợp bằng cách cập nhật +tài nguyên được sử dụng trong giao diện của bạn. Tại +thời điểm phương pháp này được gọi, đối tượng {@link android.content.res.Resources} của hoạt động của bạn được cập nhật +để trả về các tài nguyên dựa trên cấu hình mới, sao cho bạn có thể dễ dàng +đặt lại các phần tử trong UI của mình mà không để hệ thống khởi động lại hoạt động của bạn.

+ +

Chú ý: Bắt đầu với Android 3.2 (API mức 13), +"kích cỡ màn hình" cũng thay đổi khi thiết bị chuyển giữa hướng dọc và khổ ngang +. Vì thế, nếu bạn muốn ngăn cản việc khởi động lại vào thời gian chạy do thay đổi hướng khi phát triển +cho API mức 13 hoặc cao hơn (như được khai báo bởi các thuộc tính {@code minSdkVersion}{@code targetSdkVersion} +), bạn phải bao gồm giá trị {@code "screenSize"} bên cạnh giá trị {@code +"orientation"}. Cụ thể, bạn phải khai báo {@code +android:configChanges="orientation|screenSize"}. Tuy nhiên, nếu ứng dụng của bạn nhắm tới API mức +12 hoặc thấp hơn, khi đó hoạt động của bạn luôn tự mình xử lý thay đổi cấu hình này (thay đổi +cấu hình này không khởi động lại hoạt động của bạn, ngay cả khi đang chạy trên một thiết bị phiên bản Android 3.2 hoặc cao hơn).

+ +

Ví dụ, việc triển khai {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} sau +sẽ kiểm tra hướng thiết bị hiện tại:

+ +
+@Override
+public void onConfigurationChanged(Configuration newConfig) {
+    super.onConfigurationChanged(newConfig);
+
+    // Checks the orientation of the screen
+    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
+    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
+        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
+    }
+}
+
+ +

Đối tượng {@link android.content.res.Configuration} biểu diễn tất cả cấu hình +hiện tại, không chỉ những cấu hình đã thay đổi. Trong phần lớn thời gian, bạn sẽ không quan tâm chính xác xem +cấu hình đã thay đổi như thế nào và có thể đơn giản gán lại tất cả tài nguyên của mình với chức năng cung cấp nội dung thay thế +cho cấu hình mà bạn đang xử lý. Ví dụ, do đối tượng {@link +android.content.res.Resources} nay đã được cập nhật, bạn có thể đặt lại +bất kỳ{@link android.widget.ImageView} nào với {@link android.widget.ImageView#setImageResource(int) +setImageResource()} +và tài nguyên phù hợp cho cấu hình mới được sử dụng (như được mô tả trong phần Cung cấp Tài nguyên).

+ +

Lưu ý rằng giá trị từ các trường {@link +android.content.res.Configuration} là những số nguyên khớp với hằng số cụ thể +từ lớp {@link android.content.res.Configuration}. Đối với tài liệu về những hằng số +cần sử dụng với mỗi trường, hãy tham khảo trường phù hợp trong tham chiếu {@link +android.content.res.Configuration}.

+ +

Hãy ghi nhớ: Khi bạn khai báo hoạt động của mình để xử lý một +thay đổi cấu hình, bạn có trách nhiệm đặt lại bất kỳ phần tử nào mà bạn cung cấp nội dung thay thế cho. Nếu bạn +khai báo hoạt động của mình để xử lý thay đổi hướng và có những hình ảnh nên thay đổi +giữa khổ ngang và hướng dọc, bạn phải gán lại từng tài nguyên cho từng phần tử trong khi {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.

+ +

Nếu bạn không cần cập nhật ứng dụng của mình dựa trên những thay đổi +cấu hình này, thay vào đó bạn có thể không triển khai {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Trong +trường hợp đó, tất cả tài nguyên được sử dụng trước khi thay đổi cấu hình sẽ vẫn được sử dụng +và bạn chỉ mới tránh được việc khởi động lại hoạt động của mình. Tuy nhiên, ứng dụng của bạn cần luôn có khả năng +tắt và khởi động lại với trạng thái trước đó của nó được giữ nguyên, vì thế bạn không nên coi +kỹ thuật này như một cách để thoát khỏi việc giữ lại trạng thái của mình trong vòng đời của hoạt động bình thường. Không chỉ bởi +có những thay đổi cấu hình khác mà bạn không thể ngăn không cho khởi động lại ứng dụng của mình, mà +cả bởi vì bạn nên xử lý những sự kiện như là khi người dùng rời khỏi ứng dụng của bạn và nó bị +hủy trước khi người dùng quay lại.

+ +

Để biết thêm về những thay đổi cấu hình nào mà bạn có thể xử lý trong hoạt động của mình, hãy xem tài liệu {@code +android:configChanges} và lớp {@link android.content.res.Configuration} +.

diff --git a/docs/html-intl/intl/vi/guide/topics/ui/controls.jd b/docs/html-intl/intl/vi/guide/topics/ui/controls.jd new file mode 100644 index 0000000000000000000000000000000000000000..37fe81c8c73a89e06789a0fcae950f4b085f1283 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=Điều khiển Nhập liệu +parent.title=Giao diện Người dùng +parent.link=index.html +@jd:body + +
+ +
+ +

Điều khiển nhập liệu là những thành phần tương tác trong giao diện người dùng của ứng dụng của bạn. Android cung cấp +nhiều kiểu điều khiển bạn có thể sử dụng trong UI của mình, chẳng hạn như nút, trường văn bản, thanh tìm kiếm, +hộp kiểm, nút thu phóng, nút bật tắt, và nhiều kiểu khác.

+ +

Thêm một điều khiển nhập liệu vào UI của bạn cũng đơn giản như thêm một phần tử XML vào bố trí XML của bạn. Ví dụ, đây là một bố trí +với một trường văn bản và nút:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="horizontal">
+    <EditText android:id="@+id/edit_message"
+        android:layout_weight="1"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:hint="@string/edit_message" />
+    <Button android:id="@+id/button_send"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/button_send"
+        android:onClick="sendMessage" />
+</LinearLayout>
+
+ +

Mỗi điều khiển nhập liệu hỗ trợ một tập hợp sự kiện nhập liệu cụ thể để bạn có thể xử lý các sự kiện chẳng hạn như khi +người dùng nhập văn bản hoặc chạm vào một nút.

+ + +

Điều khiển Thông dụng

+

Sau đây là danh sách những điều khiển thông dụng mà bạn có thể sử dụng trong ứng dụng của mình. Theo dõi các liên kết để tìm +hiểu thêm về việc sử dụng từng điều khiển.

+ +

Lưu ý: Android cung cấp nhiều điều khiển hơn một chút so với liệt kê ở +đây. Duyệt gói {@link android.widget} để khám phá thêm. Nếu ứng dụng của bạn yêu cầu một +kiểu điều khiển nhập liệu cụ thể, bạn có thể xây dựng các thành phần tùy chỉnh của chính mình.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Kiểu Điều khiểnMô tảLớp Liên quan
NútNút nhấn có thể được nhấn, hoặc nhấp vào, bởi người dùng để thực hiện một hành động.{@link android.widget.Button Button}
Trường văn bảnTrường văn bản có thể chỉnh sửa. Bạn có thể sử dụng widget AutoCompleteTextView để tạo một widget mục nhập văn bản nhằm cung cấp các gợi ý tự động hoàn thành{@link android.widget.EditText EditText}, {@link android.widget.AutoCompleteTextView}
Hộp kiểmMột công tắc bật/tắt mà có thể được chuyển đổi bởi người dùng. Bạn nên sử dụng các hộp kiểm khi trình bày cho người dùng một nhóm các tùy chọn có thể chọn mà không loại trừ lẫn nhau.{@link android.widget.CheckBox CheckBox}
Nút chọn mộtTương tự như hộp kiểm, chỉ khác ở chỗ chỉ có thể chọn một tùy chọn trong nhóm.{@link android.widget.RadioGroup RadioGroup} +
{@link android.widget.RadioButton RadioButton}
Nút bật tắtMột nút bật/tắt có đèn chỉ báo.{@link android.widget.ToggleButton ToggleButton}
Quay trònMột danh sách thả xuống cho phép người dùng chọn một giá trị từ một tập hợp.{@link android.widget.Spinner Spinner}
Bộ chọnMột hộp thoại cho người dùng chọn một giá trị đơn lẻ cho một tập hợp bằng cách sử dụng các nút lên/xuống hoặc thông qua cử chỉ trượt nhanh. Sử dụng một widget DatePicker để nhập giá trị cho ngày (tháng, ngày, năm) hoặc một widget TimePicker để nhập giá trị cho thời gian (giờ, phút, Sáng/Chiều tối) mà sẽ được định dạng tự động theo bản địa của người dùng.{@link android.widget.DatePicker}, {@link android.widget.TimePicker}
diff --git a/docs/html-intl/intl/vi/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/vi/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000000000000000000000000000000..6add812d89f7d5192356da81754c559c877d1e30 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=Bố trí +page.tags=dạng xem, nhóm dạng xem +@jd:body + + + +

Bố trí định nghĩa cấu trúc hiển thị cho một giao diện người dùng, chẳng hạn như UI cho một hoạt động hoặc widget ứng dụng. +Bạn có thể khai báo một bố trí bằng hai cách:

+
    +
  • Khai báo phần tử UI trong XML. Android cung cấp một kho từ vựng XML +đơn giản, tương ứng với các lớp và lớp con Dạng xem, chẳng hạn như dành cho các widget và bố trí.
  • +
  • Khởi tạo các phần tử bố trí vào thời gian chạy. Ứng dụng +của bạn có thể tạo các đối tượng Dạng xem và Nhóm Dạng xem (và thao tác trên các tính chất của nó) theo lập trình.
  • +
+ +

Khuôn khổ Android cho bạn sự linh hoạt trong khi sử dụng một hoặc cả hai phương pháp này để khai báo và quản lý UI ứng dụng của mình. Ví dụ, bạn có thể khai báo các bố trí mặc định cho ứng dụng của mình trong XML, bao gồm các phần tử màn hình mà sẽ xuất hiện trong chúng hoặc tính chất của chúng. Sau đó, bạn có thể thêm mã trong ứng dụng của mình để sửa đổi trạng thái của các đối tượng trên màn hình, bao gồm những đối tượng được khai báo trong XML, vào thời gian chạy.

+ + + +

Ưu điểm của việc khai báo UI của bạn trong XML là cho phép bạn tách việc trình bày ứng dụng của mình với mã điều khiển hành vi của nó hiệu quả hơn. Mô tả UI của bạn nằm ngoài mã ứng dụng của bạn, điều này có nghĩa rằng bạn có thể sửa đổi hoặc điều hợp nó mà không phải sửa đổi mã nguồn của bạn và biên dịch lại. Ví dụ, bạn có thể tạo bố trí XML cho các hướng màn hình khác nhau, kích cỡ màn hình khác nhau, và ngôn ngữ khác nhau. Ngoài ra, việc khai báo bố trí trong XML giúp dễ dàng hơn trong việc hiển thị cấu trúc UI của bạn, vì vậy sẽ dễ gỡ lỗi sự cố hơn. Như vậy, tài liệu này tập trung vào việc hướng dẫn bạn cách khai báo bố trí của mình trong XML. Nếu bạn +quan tâm tới việc khởi tạo các đối tượng Dạng xem vào thời gian chạy, hãy tham khảo {@link android.view.ViewGroup} và +tài liệu tham khảo lớp {@link android.view.View}.

+ +

Nhìn chung, kho từ vựng của XML đối với việc khai báo các phần tử UI tuân thủ chặt chẽ cấu trúc và cách đặt tên các lớp và phương pháp, trong đó các tên phần tử tương ứng với tên lớp và tên thuộc tính tương ứng với phương pháp. Trên thực tế, sự tương ứng thường trực tiếp đến mức bạn có thể đoán thuộc tính XML nào tương ứng với một phương pháp lớp, hoặc đoán xem lớp nào tương ứng với một phần tử XML cho trước. Tuy nhiên, lưu ý rằng không phải tất cả từ vựng đều giống nhau. Trong một số trường hợp, có sự khác biệt nhỏ trong việc đặt tên. Ví +dụ, phần tử EditText có thuộc tính text tương ứng với +EditText.setText().

+ +

Mẹo: Tìm hiểu thêm về các kiểu bố trí trong Đối tượng Bố trí +Thường gặp. Có một tuyển tập các bài hướng dẫn về việc xây dựng các bố trí khác nhau trong hướng dẫn bài học +Dạng xem Hello.

+ +

Ghi XML

+ +

Khi sử dụng từ vựng XML của Android, bạn có thể nhanh chóng thiết kế các bố trí UI và phần tử màn hình mà chúng chứa, giống như cách bạn tạo trang web trong HTML — bằng một loạt các phần tử lồng nhau.

+ +

Mỗi tệp bố trí phải chứa chính xác một phần tử gốc, đó phải là một đối tượng Dạng xem hoặc Nhóm Dạng xem. Sau khi đã định nghĩa phần tử gốc, bạn có thể thêm các đối tượng hoặc widget bố trí bổ sung làm phần tử con để dần dần xây dựng một phân cấp Dạng xem định nghĩa bố trí của bạn. Ví dụ, sau đây là một bố trí XML sử dụng một {@link android.widget.LinearLayout} +thẳng đứng để giữ một {@link android.widget.TextView} và một {@link android.widget.Button}:

+
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical" >
+    <TextView android:id="@+id/text"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="Hello, I am a TextView" />
+    <Button android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Hello, I am a Button" />
+</LinearLayout>
+
+ +

Sau khi bạn đã khai báo bố trí của mình trong XML, hãy lưu tệp với phần mở rộng .xml, +trong thư mục dự án res/layout/ Android của bạn để biên dịch cho phù hợp.

+ +

Thông tin về cú pháp đối với tệp XML bố trí có sẵn trong tài liệu Tài nguyên Bố trí.

+ +

Nạp Tài nguyên XML

+ +

Khi bạn biên dịch ứng dụng của mình, từng tệp bố trí XML được biên dịch thành một tài nguyên +{@link android.view.View}. Bạn nên nạp tài nguyên bố trí từ mã ứng dụng của mình, trong triển khai gọi lại +{@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} của bạn. +Làm vậy bằng cách gọi {@link android.app.Activity#setContentView(int) setContentView()}, +chuyển cho nó tham chiếu tới tài nguyên bố trí của bạn dưới hình thức: +R.layout.layout_file_name. +Ví dụ, nếu bố trí XML của bạn được lưu thành main_layout.xml, bạn sẽ nạp nó +cho Hoạt động của mình như sau:

+
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main_layout);
+}
+
+ +

Phương pháp gọi lại onCreate() trong Hoạt động của bạn được gọi bởi khuôn khổ Android khi +Hoạt động của bạn được khởi chạy (xem phần thảo luận về vòng đời, trong tài liệu +Hoạt động +).

+ + +

Thuộc tính

+ +

Mọi đối tượng Dạng xem và Nhóm Dạng xem đều hỗ trợ các phiên bản thuộc tính XML của chính mình. +Một số thuộc tính áp dụng riêng cho đối tượng Dạng xem (ví dụ, TextView hỗ trợ thuộc tính textSize +), nhưng những thuộc tính này cũng được kế thừa bởi bất kỳ đối tượng Dạng xem nào mà có thể mở rộng lớp này. +Một số được áp dụng chung cho tất cả đối tượng Dạng xem vì chúng được kế thừa từ lớp Dạng xem gốc (như +thuộc tính id). Và những thuộc tính còn lại được coi là "tham số bố trí," đó là những thuộc tính +mô tả một số hướng bố trí nhất định của đối tượng Dạng xem, như được định nghĩa bởi đối tượng Nhóm Dạng xem +mẹ của đối tượng đó.

+ +

ID

+ +

Bất kỳ đối tượng Dạng xem nào cũng có một ID số nguyên được liên kết với nó để nhận biết duy nhất Dạng xem trong cây. +Khi ứng dụng được biên dịch, ID này được tham chiếu như một số nguyên, nhưng ID thường +được gán trong tệp XML bố trí như một xâu, trong thuộc tính id. +Đây là thuộc tính XML chung cho tất cả đối tượng Dạng xem +(được định nghĩa theo lớp {@link android.view.View}) và bạn sẽ rất hay sử dụng nó. +Cú pháp đối với một ID bên trong một tag XML là:

+
android:id="@+id/my_button"
+ +

Biểu tượng "a móc" (@) ở đầu xâu thể hiện rằng trình phân tích XML nên phân tích và mở rộng phần còn lại +của xâu ID và nhận biết nó như một tài nguyên ID. Biểu tượng dấu cộng (+) có nghĩa rằng đây là một tên tài nguyên mới mà phải +được tạo và thêm vào tài nguyên của chúng ta (trong tệp R.java). Có nhiều tài nguyên ID khác +được cung cấp bởi khuôn khổ Android. Khi tham chiếu một ID tài nguyên Android, bạn không cần biểu tượng dấu cộng, +nhưng phải thêm vùng tên gói android, như sau:

+
android:id="@android:id/empty"
+

Khi đã có vùng tên gói android, giờ chúng ta đang tham chiếu một ID từ lớp tài nguyên android.R +, thay vì lớp tài nguyên cục bộ.

+ +

Để tạo các dạng xem và tham chiếu chúng từ ứng dụng, một mô thức thường thấy đó là:

+
    +
  1. Định nghĩa một dạng xem/widget trong tệp bố trí và gán cho nó một ID duy nhất: +
    +<Button android:id="@+id/my_button"
    +        android:layout_width="wrap_content"
    +        android:layout_height="wrap_content"
    +        android:text="@string/my_button_text"/>
    +
    +
  2. +
  3. Sau đó, tạo một thực thể của đối tượng dạng xem và chụp nó từ bố trí +(thường trong phương pháp {@link android.app.Activity#onCreate(Bundle) onCreate()}): +
    +Button myButton = (Button) findViewById(R.id.my_button);
    +
    +
  4. +
+

Định nghĩa các ID cho đối tượng dạng xem là việc quan trọng khi tạo một {@link android.widget.RelativeLayout}. +Trong một bố trí tương đối, các dạng xem đồng hạng có thể định nghĩa bố trí của nó so với dạng xem đồng hạng kia, +dạng xem mà được tham chiếu bởi ID duy nhất.

+

Một ID không cần phải là duy nhất trong toàn bộ cây, nhưng nên là +duy nhất trong bộ phận của cây mà bạn đang tìm kiếm (thường là toàn bộ cây, vì thế tốt nhất là +ID nên hoàn toàn duy nhất khi có thể).

+ + +

Tham số Bố trí

+ +

Các thuộc tính bố trí XML layout_something sẽ định nghĩa +các tham số bố trí cho Dạng xem phù hợp với Nhóm Dạng xem mà nó nằm trong đó.

+ +

Mọi lớp Nhóm Dạng xem đều triển khai một lớp lồng nhau có chức năng mở rộng {@link +android.view.ViewGroup.LayoutParams}. Lớp con này +chứa các kiểu tính chất mà định nghĩa kích cỡ và vị trí của từng dạng xem con cho +phù hợp với nhóm dạng xem. Như bạn có thể thấy trong hình 1, nhóm dạng xem +mẹ sẽ định nghĩa các tham số bố trí cho từng dạng xem con (bao gồm nhóm dạng xem con).

+ + +

Hình 1. Trực quan hóa một phân cấp dạng xem với các tham số +bố trí được liên kết với từng dạng xem.

+ +

Để ý rằng mọi lớp con LayoutParams đều có cú pháp riêng của mình cho các giá trị +thiết đặt. Mỗi phần tử con phải định nghĩa LayoutParams cho phù hợp với phần tử mẹ của nó, +mặc dù cũng có thể định nghĩa LayoutParams khác cho phần tử con của chính nó.

+ +

Tất cả nhóm dạng xem đều có chiều rộng và chiều cao (layout_width và +layout_height), và mỗi dạng xem đều phải định nghĩa chúng. Nhiều +LayoutParams cũng có lề và viền tùy chọn.

+ +

Bạn có thể chỉ định chiều rộng và chiều cao bằng các số đo chính xác, mặc dù có thể +bạn sẽ không muốn làm điều này thường xuyên. Bạn sẽ thường sử dụng một trong những hằng số này để +đặt chiều rộng hoặc chiều cao:

+ +
    +
  • wrap_content cho biết dạng xem của bạn tự định cỡ theo kích thước +mà nội dung của nó yêu cầu.
  • +
  • match_parent (được đặt tên fill_parent trước API Mức 8) +cho biết dạng xem của bạn có thể phóng lớn khi nhóm dạng xem mẹ của nó cho phép.
  • +
+ +

Nhìn chung, việc chỉ định một chiều rộng và chiều cao bố trí bằng cách sử dụng các đơn vị tuyệt đối như +điểm ảnh là điều không được khuyến cáo. Thay vào đó, sử dụng các số đo tương đối như +số đơn vị điểm ảnh độc lập với mật độ (dp), wrap_content, hoặc +match_parent, là một phương pháp tốt hơn, vì nó giúp đảm bảo rằng +ứng dụng của bạn sẽ hiển thị phù hợp giữa nhiều loại kích cỡ màn hình thiết bị khác nhau. +Các kiểu số đo được chấp nhận được định nghĩa trong tài liệu + +Tài nguyên Có sẵn.

+ + +

Vị trí Bố trí

+

+ Hình học của một dạng xem là hình chữ nhật. Dạng xem có một vị trí, + được biểu diễn thành một cặp tọa độ tráitrên và + hai kích thước, được biểu diễn thành chiều rộng và chiều cao. Đơn vị của vị trí + và kích thước là điểm ảnh. +

+ +

+ Có thể truy xuất vị trí của một dạng xem bằng cách gọi ra các phương pháp + {@link android.view.View#getLeft()} và {@link android.view.View#getTop()}. Phương pháp đầu trả về tọa độ trái, hay X, + của hình chữ nhật biểu diễn dạng xem. Phương pháp sau trả về tọa độ trên, hay Y, + của hình chữ nhật biểu diễn dạng xem. Những phương pháp này + đều trả về vị trí của dạng xem so với dạng xem mẹ của nó. Ví dụ, + khi getLeft() trả về 20, điều đó có nghĩa là dạng xem nằm ở 20 điểm ảnh về + bên phải của cạnh trái của dạng xem mẹ trực tiếp của nó. +

+ +

+ Ngoài ra, một vài phương pháp thuận tiện được đưa ra để tránh những tính toán + không cần thiết, cụ thể là {@link android.view.View#getRight()} và {@link android.view.View#getBottom()}. + Những phương pháp này trả về tọa độ của cạnh phải và cạnh đáy của hình chữ nhật + biểu diễn dạng xem. Ví dụ, việc gọi {@link android.view.View#getRight()} + tương tự như tính toán sau: getLeft() + getWidth(). +

+ + +

Kích cỡ, Phần đệm và Lề

+

+ Kích cỡ của một dạng xem được biểu diễn bằng chiều rộng và chiều cao. Thực ra một dạng xem + sẽ có hai cặp giá trị chiều rộng và chiều cao. +

+ +

+ Cặp thứ nhất được gọi là chiều rộng đo được và + chiều cao đo được. Những kích thước này xác định một dạng xem muốn phóng lớn bao nhiêu + trong dạng xem mẹ của nó. Các + kích thước đo được có thể thu được bằng cách gọi {@link android.view.View#getMeasuredWidth()} + và {@link android.view.View#getMeasuredHeight()}. +

+ +

+ Cặp thứ hai đơn thuần là chiều rộngchiều cao, hoặc + đôi khi gọi là chiều rộng vẽchiều cao vẽ. Những + kích thước này xác định kích cỡ thực sự của dạng xem trên màn hình, tại thời điểm vẽ và + sau khi bố trí. Những giá trị này có thể nhưng không nhất thiết phải khác với + chiều rộng và chiều cao đo được. Chiều rộng và chiều cao này có thể lấy được bằng cách gọi + {@link android.view.View#getWidth()} và {@link android.view.View#getHeight()}. +

+ +

+ Để đo các kích thước của nó, dạng xem cần xét tới phần đệm của nó. Phần đệm + được biểu diễn bằng số điểm ảnh của phần bên trái, bên trên, bên phải và bên dưới của dạng xem. + Phần đệm có thể được sử dụng để bù trừ nội dung của dạng xem bằng một số điểm ảnh + cụ thể. Ví dụ, phần đệm bên trái bằng 2 sẽ đẩy nội dung của dạng xem đi + 2 điểm ảnh về bên phải của cạnh bên trái. Phần đệm có thể được đặt bằng cách sử dụng phương pháp + {@link android.view.View#setPadding(int, int, int, int)} và được truy vấn bằng cách gọi + {@link android.view.View#getPaddingLeft()}, {@link android.view.View#getPaddingTop()}, + {@link android.view.View#getPaddingRight()} và {@link android.view.View#getPaddingBottom()}. +

+ +

+ Mặc dù dạng xem có thể xác định phần đệm, nó không có bất kỳ sự hỗ trợ nào cho + lề. Tuy nhiên, các nhóm dạng xem lại cung cấp sự hỗ trợ như vậy. Tham khảo + {@link android.view.ViewGroup} và + {@link android.view.ViewGroup.MarginLayoutParams} để biết thêm thông tin. +

+ +

Để biết thêm thông tin về kích thước, xem + Giá trị Kích thước. +

+ + + + + + + + + + + +

Các Bố trí Thường gặp

+ +

Mỗi lớp con của lớp {@link android.view.ViewGroup} cung cấp một cách duy nhất để hiển thị +các dạng xem mà bạn lồng trong nó. Dưới đây là một số kiểu bố trí phổ biến hơn mà được tích hợp +trong nền tảng Android.

+ +

Lưu ý: Mặc dù bạn có thể lồng một hoặc nhiều bố trí với một +bố trí khác để đạt được thiết kế UI của mình, bạn nên cố gắng duy trì phân cấp bố trí của mình ở mức nông nhất +có thể. Bố trí của bạn sẽ vẽ nhanh hơn nếu nó có ít bố trí lồng nhau hơn (phân cấp dạng xem rộng +sẽ tốt hơn phân cấp dạng xem sâu).

+ + + + +
+

Bố trí Tuyến tính

+ +

Một bố trí có chức năng sắp xếp tổ chức các bố trí con của nó thành một hàng ngang hoặc thẳng đứng. Nó + sẽ tạo một thanh cuộn nếu chiều dài của cửa sổ vượt quá chiều dài của màn hình.

+
+ +
+

Bố trí Tương đối

+ +

Cho phép bạn chỉ định vị trí của các đối tượng con so với nhau (đối tượng con A về phía +bên trái của đối tượng con B) hoặc so với đối tượng mẹ (được căn theo bên trên đối tượng mẹ).

+
+ +
+

Dạng xem Web

+ +

Hiển thị trang web.

+
+ + + + +

Xây dựng Bố trí bằng một Trình điều hợp

+ +

Khi nội dung cho bố trí của bạn động hoặc chưa được xác định trước, bạn có thể sử dụng một bố trí có chức năng +tạo lớp con {@link android.widget.AdapterView} để đưa vào bố trí có dạng xem vào thời gian chạy. Một +lớp con của lớp {@link android.widget.AdapterView} sẽ sử dụng một {@link android.widget.Adapter} để +gắn kết dữ liệu với bố trí của nó. {@link android.widget.Adapter} đóng vai trò trung gian giữa nguồn +dữ liệu và bố trí {@link android.widget.AdapterView} —{@link android.widget.Adapter} +sẽ truy xuất dữ liệu (từ một nguồn chẳng hạn như một mảng hoặc truy vấn cơ sở dữ liệu) và chuyển từng mục nhập +thành một dạng xem có thể thêm vào bố trí {@link android.widget.AdapterView}.

+ +

Các bố trí phổ biến được hỗ trợ bởi trình điều hợp bao gồm:

+ +
+

Dạng xem Danh sách

+ +

Hiển thị một danh sách cột cuộn đơn.

+
+ +
+

Dạng xem Lưới

+ +

Hiển thị một lưới cuộn gồm nhiều hàng và cột.

+
+ + + +

Điền dữ liệu vào một dạng xem trình điều hợp

+ +

Bạn có thể đưa vào một {@link android.widget.AdapterView} chẳng hạn như {@link android.widget.ListView} hoặc +{@link android.widget.GridView} bằng cách gắn kết thực thể {@link android.widget.AdapterView} với một +{@link android.widget.Adapter}, nó truy xuất dữ liệu từ một nguồn bên ngoài và tạo một {@link +android.view.View} để biểu diễn từng mục nhập dữ liệu.

+ +

Android cung cấp một vài lớp con của {@link android.widget.Adapter} rất hữu ích cho việc +truy xuất các kiểu dữ liệu khác nhau và xây dựng dạng xem cho một {@link android.widget.AdapterView}. Hai +trình điều hợp phổ biến nhất là:

+ +
+
{@link android.widget.ArrayAdapter}
+
Sử dụng trình điều hợp này khi nguồn dữ liệu của bạn là một mảng. Theo mặc định, {@link +android.widget.ArrayAdapter} tạo một dạng xem cho mỗi mục mảng bằng cách gọi {@link +java.lang.Object#toString()} trên từng mục và đặt nội dung trong một {@link +android.widget.TextView}. +

Ví dụ, nếu bạn có một mảng xâu mà bạn muốn hiển thị trong một {@link +android.widget.ListView}, hãy khởi tạo một {@link android.widget.ArrayAdapter} mới bằng cách sử dụng +hàm dựng để chỉ định bố trí cho từng xâu và mảng xâu:

+
+ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+        android.R.layout.simple_list_item_1, myStringArray);
+
+

Các tham đối cho hàm dựng này là:

+
    +
  • Ứng dụng của bạn {@link android.content.Context}
  • +
  • Bố trí chứa một {@link android.widget.TextView} cho mỗi xâu trong mảng
  • +
  • Mảng xâu
  • +
+

Sau đó chỉ cần gọi +{@link android.widget.ListView#setAdapter setAdapter()} trên {@link android.widget.ListView} của bạn:

+
+ListView listView = (ListView) findViewById(R.id.listview);
+listView.setAdapter(adapter);
+
+ +

Để tùy chỉnh diện mạo của từng mục, bạn có thể khống chế phương pháp {@link +java.lang.Object#toString()} cho các đối tượng trong mảng của mình. Hoặc, để tạo một dạng xem cho từng +mục không phải là một {@link android.widget.TextView} (ví dụ, nếu bạn muốn một +{@link android.widget.ImageView} cho từng mục mảng), hãy mở rộng lớp {@link +android.widget.ArrayAdapter} và khống chế {@link android.widget.ArrayAdapter#getView +getView()} để trả về kiểu dạng xem mà bạn muốn cho từng mục.

+ +
+ +
{@link android.widget.SimpleCursorAdapter}
+
Sử dụng trình điều hợp này khi dữ liệu của bạn đến từ một {@link android.database.Cursor}. Khi +sử dụng {@link android.widget.SimpleCursorAdapter}, bạn phải chỉ định một bố trí để sử dụng cho từng +hàng trong {@link android.database.Cursor} và những cột nào trong {@link android.database.Cursor} +nên được chèn vào dạng xem nào của bố trí. Ví dụ, nếu bạn muốn tạo một danh sách +tên người và số điện thoại, bạn có thể thực hiện một truy vấn mà trả về một {@link +android.database.Cursor} chứa một hàng cho từng người và nhiều cột cho các tên và +số điện thoại. Sau đó, bạn tạo một mảng xâu chỉ định những cột nào từ {@link +android.database.Cursor} mà bạn muốn trong bố trí cho từng kết quả và một mảng số nguyên chỉ định các +dạng xem tương ứng mà từng cột sẽ được đặt vào:

+
+String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
+                        ContactsContract.CommonDataKinds.Phone.NUMBER};
+int[] toViews = {R.id.display_name, R.id.phone_number};
+
+

Khi bạn khởi tạo {@link android.widget.SimpleCursorAdapter}, hãy chuyển bố trí cần sử dụng cho +từng kết quả, {@link android.database.Cursor} chứa các kết quả, và hai mảng sau:

+
+SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
+        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
+ListView listView = getListView();
+listView.setAdapter(adapter);
+
+

Sau đó, {@link android.widget.SimpleCursorAdapter} tạo một dạng xem cho từng hàng trong +{@link android.database.Cursor} sử dụng bố trí được cung cấp bằng cách chèn từng mục {@code +fromColumns} vào dạng xem {@code toViews} tương ứng.

.
+
+ + +

Trong vòng đời ứng dụng của bạn, nếu bạn thay đổi dữ liệu liên quan được đọc bởi +trình điều hợp của mình, bạn nên gọi {@link android.widget.ArrayAdapter#notifyDataSetChanged()}. Nó sẽ +thông báo với dạng xem đính kèm rằng dữ liệu đã được thay đổi và dạng xem nên tự làm mới.

+ + + +

Xử lý sự kiện nhấp

+ +

Bạn có thể phản hồi các sự kiện nhấp trên từng mục trong một {@link android.widget.AdapterView} bằng cách +triển khai giao diện {@link android.widget.AdapterView.OnItemClickListener}. Ví dụ:

+ +
+// Create a message handling object as an anonymous class.
+private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
+    public void onItemClick(AdapterView parent, View v, int position, long id) {
+        // Do something in response to the click
+    }
+};
+
+listView.setOnItemClickListener(mMessageClickedHandler);
+
+ + + diff --git a/docs/html-intl/intl/vi/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/vi/guide/topics/ui/dialogs.jd new file mode 100644 index 0000000000000000000000000000000000000000..1fa45508ef0c384df4fec3e132a7029ec840992e --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=Hộp thoại +page.tags=alertdialog,dialogfragment + +@jd:body + + + + + +

Hộp thoại là một cửa sổ nhỏ có chức năng nhắc người dùng +đưa ra một quyết định hoặc nhập thông tin bổ sung. Hộp thoại không lấp kín màn hình và +thường được sử dụng cho các sự kiện mô thái yêu cầu người dùng phải thực hiện một hành động trước khi có thể đi tiếp.

+ +
+

Thiết kế Hộp thoại

+

Để biết thông tin về cách thiết kế hộp thoại của bạn, bao gồm các đề xuất + về ngôn ngữ, hãy đọc hướng dẫn thiết kế Hộp thoại.

+
+ + + +

Lớp {@link android.app.Dialog} là lớp cơ sở cho hộp thoại, nhưng bạn +nên tránh khởi tạo {@link android.app.Dialog} một cách trực tiếp. +Thay vào đó, hãy sử dụng một trong các lớp con sau:

+
+
{@link android.app.AlertDialog}
+
Hộp thoại có thể hiển thị một tiêu đề, tối đa ba nút, một danh sách + các mục có thể chọn, hoặc một bố trí tùy chỉnh.
+
{@link android.app.DatePickerDialog} hoặc {@link android.app.TimePickerDialog}
+
Hộp thoại với một UI được xác định trước, cho phép người dùng chọn ngày hoặc giờ.
+
+ + + +

Những lớp này định nghĩa kiểu và cấu trúc cho hộp thoại của bạn, nhưng bạn nên +sử dụng một {@link android.support.v4.app.DialogFragment} làm bộ chứa cho hộp thoại của mình. +Lớp {@link android.support.v4.app.DialogFragment} sẽ cung cấp tất cả điều khiển mà +bạn cần để tạo hộp thoại của mình và quản lý diện mạo của hộp thoại, thay vì gọi ra các phương pháp +trên đối tượng {@link android.app.Dialog}.

+ +

Việc sử dụng {@link android.support.v4.app.DialogFragment} để quản lý hộp thoại +sẽ đảm bảo rằng nó xử lý đúng các sự kiện vòng đời +chẳng hạn như khi người dùng nhấn nút Quay lại hoặc xoay màn hình. Lớp {@link +android.support.v4.app.DialogFragment} cũng cho phép bạn sử dụng lại UI của hộp thoại như một +thành phần có thể nhúng trong một UI rộng hơn, giống như một {@link +android.support.v4.app.Fragment} truyền thống (chẳng hạn như khi bạn muốn UI hộp thoại xuất hiện khác đi +trên các màn hình lớn và nhỏ).

+ +

Các phần sau trong hướng dẫn này mô tả cách sử dụng {@link +android.support.v4.app.DialogFragment} kết hợp với một đối tượng {@link android.app.AlertDialog} +. Nếu muốn tạo một bộ chọn ngày hoặc giờ, thay vào đó, bạn nên đọc hướng dẫn +Bộ chọn.

+ +

Lưu ý: +Vì lớp {@link android.app.DialogFragment} ban đầu được bổ sung cùng với +Android 3.0 (API mức 11), tài liệu này mô tả cách sử dụng lớp {@link +android.support.v4.app.DialogFragment} được cung cấp kèm Thư viện Hỗ trợ. Bằng cách thêm thư viện này +vào ứng dụng của mình, bạn có thể sử dụng {@link android.support.v4.app.DialogFragment} và nhiều loại +API khác trên các thiết bị chạy Android 1.6 hoặc cao hơn. Nếu phiên bản tối thiểu mà ứng dụng của bạn hỗ trợ +là API mức 11 hoặc cao hơn, khi đó bạn có thể sử dụng phiên bản khuôn khổ của {@link +android.app.DialogFragment}, nhưng hãy chú ý rằng các liên kết trong tài liệu này dành cho các API +thư viện hỗ trợ. Khi sử dụng thư viện hỗ trợ, +hãy nhớ rằng bạn nhập lớp android.support.v4.app.DialogFragment +chứ không phải android.app.DialogFragment.

+ + +

Tạo một Phân đoạn Hộp thoại

+ +

Bạn có thể hoàn thành nhiều loại thiết kế hộp thoại—bao gồm +bố trí tùy chỉnh và những bố trí được mô tả trong hướng dẫn thiết kế Hộp thoại +—bằng cách mở rộng +{@link android.support.v4.app.DialogFragment} và tạo một {@link android.app.AlertDialog} +trong phương pháp gọi lại {@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()}.

+ +

Ví dụ, sau đây là một {@link android.app.AlertDialog} cơ bản được quản lý bên trong +một {@link android.support.v4.app.DialogFragment}:

+ +
+public class FireMissilesDialogFragment extends DialogFragment {
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Use the Builder class for convenient dialog construction
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(R.string.dialog_fire_missiles)
+               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // FIRE ZE MISSILES!
+                   }
+               })
+               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // User cancelled the dialog
+                   }
+               });
+        // Create the AlertDialog object and return it
+        return builder.create();
+    }
+}
+
+ +
+ +

Hình 1. +Hộp thoại với một thông báo và hai nút hành động.

+
+ +

Lúc này, khi bạn tạo một thực thể thuộc lớp này và gọi {@link +android.support.v4.app.DialogFragment#show show()} trên đối tượng đó, hộp thoại sẽ xuất hiện +như minh họa trong hình 1.

+ +

Phần tiếp theo mô tả thêm về việc sử dụng các API {@link android.app.AlertDialog.Builder} +để tạo hộp thoại.

+ +

Tùy vào độ phức tạp của hộp thoại của bạn, bạn có thể triển khai nhiều loại phương pháp gọi lại khác +trong {@link android.support.v4.app.DialogFragment}, bao gồm tất cả +phương pháp vòng đời phân đoạn cơ bản. + + + + + +

Xây dựng một Hộp thoại Cảnh báo

+ + +

Lớp {@link android.app.AlertDialog} cho phép bạn xây dựng nhiều loại thiết kế hộp thoại và +thường là lớp hộp thoại duy nhất mà bạn sẽ cần. +Như được minh họa trong hình 2, có ba vùng trên một hộp thoại cảnh báo:

+ +
+ +

Hình 2. Bố trí của một hộp thoại.

+
+ +
    +
  1. Tiêu đề +

    Tiêu đề không bắt buộc và chỉ nên được sử dụng khi vùng nội dung + bị chiếm bởi một thông báo chi tiết, một danh sách, hay một bố trí tùy chỉnh. Nếu bạn cần nêu + một thông báo hoặc câu hỏi đơn giản (chẳng hạn như hộp thoại trong hình 1), bạn không cần tiêu đề.

  2. +
  3. Vùng nội dung +

    Vùng này có thể hiển thị một thông báo, danh sách, hay bố trí tùy chỉnh khác.

  4. +
  5. Nút hành động +

    Sẽ không có quá ba nút hành động trong một hộp thoại.

  6. +
+ +

Lớp {@link android.app.AlertDialog.Builder} +cung cấp các API cho phép bạn tạo một {@link android.app.AlertDialog} +với những kiểu nội dung này, bao gồm một bố trí tùy chỉnh.

+ +

Để xây dựng một {@link android.app.AlertDialog}:

+ +
+// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+// 2. Chain together various setter methods to set the dialog characteristics
+builder.setMessage(R.string.dialog_message)
+       .setTitle(R.string.dialog_title);
+
+// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}
+AlertDialog dialog = builder.create();
+
+ +

Các chủ đề sau cho biết cách định nghĩa các thuộc tính hộp thoại khác nhau bằng cách +sử dụng lớp {@link android.app.AlertDialog.Builder}.

+ + + + +

Thêm nút

+ +

Để thêm các nút hành động như trong hình 2, +hãy gọi các phương pháp {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} và +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()}:

+ +
+AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+// Add the buttons
+builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+           public void onClick(DialogInterface dialog, int id) {
+               // User clicked OK button
+           }
+       });
+builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+           public void onClick(DialogInterface dialog, int id) {
+               // User cancelled the dialog
+           }
+       });
+// Set other dialog properties
+...
+
+// Create the AlertDialog
+AlertDialog dialog = builder.create();
+
+ +

Các phương pháp set...Button() yêu cầu một tiêu đề cho nút (được cung cấp +bởi một tài nguyên xâu) và một +{@link android.content.DialogInterface.OnClickListener} có chức năng định nghĩa hành động sẽ tiến hành +khi người dùng nhấn nút.

+ +

Có ba nút hành động khác nhau mà bạn có thể thêm:

+
+
Tích cực
+
Bạn nên sử dụng nút này để chấp nhận và tiếp tục với hành động (hành động "OK").
+
Tiêu cực
+
Bạn nên sử dụng nút này để hủy bỏ hành động.
+
Trung lập
+
Bạn nên sử dụng nút này khi người dùng có thể không muốn tiếp tục với hành động, + nhưng không hẳn muốn hủy bỏ. Nó nằm ở giữa nút + tích cực và tiêu cực. Ví dụ, hành động có thể là "Nhắc tôi sau."
+
+ +

Bạn chỉ có thể thêm một nút mỗi loại vào một {@link +android.app.AlertDialog}. Nghĩa là, bạn không thể có nhiều hơn một nút "tích cực".

+ + + +
+ +

Hình 3. +Hộp thoại có tiêu đề và danh sách.

+
+ +

Thêm một danh sách

+ +

Có ba loại danh sách có sẵn với các API {@link android.app.AlertDialog}:

+
    +
  • Danh sách một lựa chọn truyền thống
  • +
  • Danh sách một lựa chọn cố định (nút chọn một)
  • +
  • Danh sách nhiều lựa chọn cố định (hộp kiểm)
  • +
+ +

Để tạo danh sách một lựa chọn như danh sách trong hình 3, +hãy sử dụng phương pháp {@link android.app.AlertDialog.Builder#setItems setItems()}:

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    builder.setTitle(R.string.pick_color)
+           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
+               public void onClick(DialogInterface dialog, int which) {
+               // The 'which' argument contains the index position
+               // of the selected item
+           }
+    });
+    return builder.create();
+}
+
+ +

Vì danh sách xuất hiện trong vùng nội dung của hộp thoại, +hộp thoại không thể hiển thị cả thông báo và danh sách và bạn nên đặt một tiêu đề cho hộp thoại +bằng {@link android.app.AlertDialog.Builder#setTitle setTitle()}. +Để chỉ định các mục cho danh sách, hãy gọi {@link +android.app.AlertDialog.Builder#setItems setItems()}, chuyển một mảng. +Hoặc, bạn có thể chỉ định một danh sách bằng cách sử dụng {@link +android.app.AlertDialog.Builder#setAdapter setAdapter()}. Điều này cho phép bạn hỗ trợ danh sách +bằng dữ liệu động (chẳng hạn như từ một cơ sở dữ liệu) bằng cách sử dụng {@link android.widget.ListAdapter}.

+ +

Nếu bạn chọn hỗ trợ danh sách của mình bằng một {@link android.widget.ListAdapter}, +hãy luôn sử dụng {@link android.support.v4.content.Loader} sao cho nội dung tải +không đồng bộ. Điều này được mô tả thêm trong hướng dẫn +Xây dựng Bố trí +bằng một Trình điều hợpTrình tải +.

+ +

Lưu ý: Theo mặc định, chạm vào một mục danh sách sẽ bỏ hộp thoại, +trừ khi bạn đang sử dụng một trong các danh sách lựa chọn cố định sau.

+ +
+ +

Hình 4. +Danh sách nhiều mục lựa chọn.

+
+ + +

Thêm một danh sách nhiều lựa chọn hoặc một lựa chọn cố định

+ +

Để thêm một danh sách nhiều lựa chọn (hộp kiểm) hoặc +một lựa chọn (nút chọn một), hãy sử dụng các phương pháp +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} hoặc +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} tương ứng.

+ +

Ví dụ, sau đây là cách bạn có thể tạo một danh sách nhiều lựa chọn như +danh sách được minh họa trong hình 4 giúp lưu các mục +được chọn trong một {@link java.util.ArrayList}:

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    mSelectedItems = new ArrayList();  // Where we track the selected items
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    // Set the dialog title
+    builder.setTitle(R.string.pick_toppings)
+    // Specify the list array, the items to be selected by default (null for none),
+    // and the listener through which to receive callbacks when items are selected
+           .setMultiChoiceItems(R.array.toppings, null,
+                      new DialogInterface.OnMultiChoiceClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int which,
+                       boolean isChecked) {
+                   if (isChecked) {
+                       // If the user checked the item, add it to the selected items
+                       mSelectedItems.add(which);
+                   } else if (mSelectedItems.contains(which)) {
+                       // Else, if the item is already in the array, remove it 
+                       mSelectedItems.remove(Integer.valueOf(which));
+                   }
+               }
+           })
+    // Set the action buttons
+           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   // User clicked OK, so save the mSelectedItems results somewhere
+                   // or return them to the component that opened the dialog
+                   ...
+               }
+           })
+           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   ...
+               }
+           });
+
+    return builder.create();
+}
+
+ +

Mặc dù cả danh sách truyền thống và danh sách có nút chọn một +đều cung cấp hành động "một lựa chọn", bạn nên sử dụng {@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} nếu bạn muốn cố định lựa chọn của người dùng. +Cụ thể, nếu việc mở hộp thoại lại sau này báo hiệu lựa chọn hiện tại của người dùng, khi đó +bạn hãy tạo một danh sách với các nút chọn một.

+ + + + + +

Tạo một Bố trí Tùy chỉnh

+ +
+ +

Hình 5. Một bố trí hộp thoại tùy chỉnh.

+
+ +

Nếu bạn muốn một bố trí tùy chỉnh trong một hộp thoại, hãy tạo một bố trí và thêm nó vào một +{@link android.app.AlertDialog} bằng cách gọi {@link +android.app.AlertDialog.Builder#setView setView()} trên đối tượng {@link +android.app.AlertDialog.Builder} của bạn.

+ +

Theo mặc định, bố trí tùy chỉnh sẽ lấp đầy cửa sổ hộp thoại, nhưng bạn vẫn có thể +sử dụng các phương pháp {@link android.app.AlertDialog.Builder} để thêm nút và tiêu đề.

+ +

Ví dụ, sau đây là tệp bố trí cho hộp thoại trong Hình 5:

+ +

res/layout/dialog_signin.xml

+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <ImageView
+        android:src="@drawable/header_logo"
+        android:layout_width="match_parent"
+        android:layout_height="64dp"
+        android:scaleType="center"
+        android:background="#FFFFBB33"
+        android:contentDescription="@string/app_name" />
+    <EditText
+        android:id="@+id/username"
+        android:inputType="textEmailAddress"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="16dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="4dp"
+        android:hint="@string/username" />
+    <EditText
+        android:id="@+id/password"
+        android:inputType="textPassword"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:layout_marginLeft="4dp"
+        android:layout_marginRight="4dp"
+        android:layout_marginBottom="16dp"
+        android:fontFamily="sans-serif"
+        android:hint="@string/password"/>
+</LinearLayout>
+
+ +

Mẹo: Theo mặc định, khi bạn đặt một phần tử {@link android.widget.EditText} +để sử dụng kiểu đầu vào {@code "textPassword"}, họ phông được đặt thành đơn cách, vì thế +bạn nên đổi họ phông thành {@code "sans-serif"} sao cho cả hai trường văn bản đều sử dụng +một kiểu phông thống nhất.

+ +

Để bung bố trí ra trong {@link android.support.v4.app.DialogFragment} của bạn, +hãy lấy một {@link android.view.LayoutInflater} với +{@link android.app.Activity#getLayoutInflater()} và gọi +{@link android.view.LayoutInflater#inflate inflate()}, trong đó tham số đầu tiên +là ID tài nguyên bố trí và tham số thứ hai là một dạng xem mẹ cho bố trí. +Khi đó, bạn có thể gọi {@link android.app.AlertDialog#setView setView()} +để đặt bố trí vào một hộp thoại.

+ +
+@Override
+public Dialog onCreateDialog(Bundle savedInstanceState) {
+    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+    // Get the layout inflater
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+
+    // Inflate and set the layout for the dialog
+    // Pass null as the parent view because its going in the dialog layout
+    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
+    // Add action buttons
+           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
+               @Override
+               public void onClick(DialogInterface dialog, int id) {
+                   // sign in the user ...
+               }
+           })
+           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+               public void onClick(DialogInterface dialog, int id) {
+                   LoginDialogFragment.this.getDialog().cancel();
+               }
+           });      
+    return builder.create();
+}
+
+ +
+

Mẹo: Nếu bạn muốn một hộp thoại tùy chỉnh, +thay vào đó, bạn có thể hiển thị {@link android.app.Activity} như là một hộp thoại +thay vì sử dụng các API {@link android.app.Dialog}. Chỉ cần tạo một hoạt động và đặt chủ đề của nó thành +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog} +trong phần tử bản kê khai {@code +<activity>}:

+ +
+<activity android:theme="@android:style/Theme.Holo.Dialog" >
+
+

Vậy là xong. Lúc này, hoạt động sẽ hiển thị một cửa sổ hộp thoại thay vì toàn màn hình.

+
+ + + +

Chuyển Sự kiện lại Máy chủ của Hộp thoại

+ +

Khi người dùng chạm vào một trong các nút hành động của hộp thoại hoặc chọn một mục từ danh sách của hộp thoại, +{@link android.support.v4.app.DialogFragment} của bạn có thể tự thực hiện hành động +cần thiết, nhưng thường thì bạn sẽ muốn chuyển sự kiện tới hoạt động hoặc phân đoạn +đã mở hộp thoại. Để làm điều này, hãy định nghĩa một giao diện bằng một phương pháp cho mỗi loại sự kiện nhấp. +Sau đó, triển khai giao diện đó trong thành phần chủ mà sẽ +nhận sự kiện hành động từ hộp thoại.

+ +

Ví dụ, sau đây là một {@link android.support.v4.app.DialogFragment} có chức năng định nghĩa một +giao diện mà thông qua đó, nó sẽ chuyển các sự kiện lại cho hoạt động chủ:

+ +
+public class NoticeDialogFragment extends DialogFragment {
+    
+    /* The activity that creates an instance of this dialog fragment must
+     * implement this interface in order to receive event callbacks.
+     * Each method passes the DialogFragment in case the host needs to query it. */
+    public interface NoticeDialogListener {
+        public void onDialogPositiveClick(DialogFragment dialog);
+        public void onDialogNegativeClick(DialogFragment dialog);
+    }
+    
+    // Use this instance of the interface to deliver action events
+    NoticeDialogListener mListener;
+    
+    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        // Verify that the host activity implements the callback interface
+        try {
+            // Instantiate the NoticeDialogListener so we can send events to the host
+            mListener = (NoticeDialogListener) activity;
+        } catch (ClassCastException e) {
+            // The activity doesn't implement the interface, throw exception
+            throw new ClassCastException(activity.toString()
+                    + " must implement NoticeDialogListener");
+        }
+    }
+    ...
+}
+
+ +

Hoạt động lưu giữ hộp thoại sẽ tạo một thực thể của hộp thoại +bằng hàm dựng của phân đoạn hộp thoại và nhận sự kiện +của hộp thoại thông qua triển khai giao diện {@code NoticeDialogListener}:

+ +
+public class MainActivity extends FragmentActivity
+                          implements NoticeDialogFragment.NoticeDialogListener{
+    ...
+    
+    public void showNoticeDialog() {
+        // Create an instance of the dialog fragment and show it
+        DialogFragment dialog = new NoticeDialogFragment();
+        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
+    }
+
+    // The dialog fragment receives a reference to this Activity through the
+    // Fragment.onAttach() callback, which it uses to call the following methods
+    // defined by the NoticeDialogFragment.NoticeDialogListener interface
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // User touched the dialog's positive button
+        ...
+    }
+
+    @Override
+    public void onDialogNegativeClick(DialogFragment dialog) {
+        // User touched the dialog's negative button
+        ...
+    }
+}
+
+ +

Vì hoạt động chủ sẽ triển khai {@code NoticeDialogListener}—, được +thực thi bởi phương pháp gọi lại {@link android.support.v4.app.Fragment#onAttach onAttach()} +minh họa bên trên,—phân đoạn hộp thoại có thể sử dụng các phương pháp gọi lại +giao diện để chuyển các sự kiện nhấp cho hoạt động:

+ +
+public class NoticeDialogFragment extends DialogFragment {
+    ...
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // Build the dialog and set up the button click handlers
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(R.string.dialog_fire_missiles)
+               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // Send the positive button event back to the host activity
+                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
+                   }
+               })
+               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                   public void onClick(DialogInterface dialog, int id) {
+                       // Send the negative button event back to the host activity
+                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
+                   }
+               });
+        return builder.create();
+    }
+}
+
+ + + +

Hiển thị một Hộp thoại

+ +

Khi bạn muốn hiển thị hộp thoại của mình, hãy tạo một thực thể {@link +android.support.v4.app.DialogFragment} của bạn và gọi {@link android.support.v4.app.DialogFragment#show +show()}, chuyển {@link android.support.v4.app.FragmentManager} và một tên tag +cho phân đoạn hộp thoại.

+ +

Bạn có thể nhận được {@link android.support.v4.app.FragmentManager} bằng cách gọi +{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} từ +{@link android.support.v4.app.FragmentActivity} hoặc {@link +android.support.v4.app.Fragment#getFragmentManager()} từ một {@link +android.support.v4.app.Fragment}. Ví dụ:

+ +
+public void confirmFireMissiles() {
+    DialogFragment newFragment = new FireMissilesDialogFragment();
+    newFragment.show(getSupportFragmentManager(), "missiles");
+}
+
+ +

Tham đối thứ hai, {@code "missiles"}, là một tên tag duy nhất mà hệ thống sử dụng để lưu +và khôi phục trạng thái của phân đoạn khi cần thiết. Tag cũng cho phép bạn nhận một điều khiển (handle) cho +phân đoạn bằng cách gọi {@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.

+ + + + +

Hiển thị một Hộp thoại Toàn màn hình hoặc dạng một Phân đoạn Nhúng

+ +

Bạn có thể có một thiết kế UI mà trong đó bạn muốn một phần UI xuất hiện như một hộp thoại trong một số +tình huống, nhưng ở dưới dạng toàn màn hình hoặc phân đoạn nhúng trong trường hợp khác (có thể phụ thuộc +vào thiết bị là màn hình lớn hay nhỏ). Lớp {@link android.support.v4.app.DialogFragment} +cung cấp cho bạn sự linh hoạt này vì nó vẫn có thể đóng vai trò như một {@link +android.support.v4.app.Fragment} nhúng được.

+ +

Tuy nhiên, bạn không thể sử dụng {@link android.app.AlertDialog.Builder AlertDialog.Builder} +hay các đối tượng {@link android.app.Dialog} khác để xây dựng hộp thoại trong trường hợp này. Nếu +bạn muốn {@link android.support.v4.app.DialogFragment} có thể +nhúng được, bạn phải định nghĩa UI của hộp thoại trong một bố trí, rồi tải bố trí đó trong lệnh gọi lại +{@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()}.

+ +

Sau đây là một ví dụ {@link android.support.v4.app.DialogFragment} có thể xuất hiện như một +hộp thoại hoặc phân đoạn nhúng được (sử dụng một bố trí có tên gọi purchase_items.xml):

+ +
+public class CustomDialogFragment extends DialogFragment {
+    /** The system calls this to get the DialogFragment's layout, regardless
+        of whether it's being displayed as a dialog or an embedded fragment. */
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate the layout to use as dialog or embedded fragment
+        return inflater.inflate(R.layout.purchase_items, container, false);
+    }
+  
+    /** The system calls this only when creating the layout in a dialog. */
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        // The only reason you might override this method when using onCreateView() is
+        // to modify any dialog characteristics. For example, the dialog includes a
+        // title by default, but your custom layout might not need it. So here you can
+        // remove the dialog title, but you must call the superclass to get the Dialog.
+        Dialog dialog = super.onCreateDialog(savedInstanceState);
+        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+        return dialog;
+    }
+}
+
+ +

Và sau đây là một số mã quyết định xem hiển thị phân đoạn như một hộp thoại +hay UI toàn màn hình, dựa vào kích cỡ màn hình:

+ +
+public void showDialog() {
+    FragmentManager fragmentManager = getSupportFragmentManager();
+    CustomDialogFragment newFragment = new CustomDialogFragment();
+    
+    if (mIsLargeLayout) {
+        // The device is using a large layout, so show the fragment as a dialog
+        newFragment.show(fragmentManager, "dialog");
+    } else {
+        // The device is smaller, so show the fragment fullscreen
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        // For a little polish, specify a transition animation
+        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
+        // To make it fullscreen, use the 'content' root view as the container
+        // for the fragment, which is always the root view for the activity
+        transaction.add(android.R.id.content, newFragment)
+                   .addToBackStack(null).commit();
+    }
+}
+
+ +

Để biết thêm thông tin về việc thực hiện các giao tác phân đoạn, hãy xem hướng dẫn +Phân đoạn.

+ +

Trong ví dụ này, boolean mIsLargeLayout chỉ định liệu thiết bị hiện tại +có nên sử dụng thiết kế bố trí lớn của ứng dụng (và vì thế, nó hiển thị phân đoạn này như một hộp thoại thay vì +toàn màn hình) hay không. Cách tốt nhất để đặt loại boolean này đó là khai báo một +giá trị tài nguyên bool +bằng một giá trị tài nguyên thay thế cho các kích cỡ màn hình khác nhau. Ví dụ, sau đây là hai +phiên bản của tài nguyên bool cho các kích cỡ màn hình khác nhau:

+ +

res/values/bools.xml

+
+<!-- Default boolean values -->
+<resources>
+    <bool name="large_layout">false</bool>
+</resources>
+
+ +

res/values-large/bools.xml

+
+<!-- Large screen boolean values -->
+<resources>
+    <bool name="large_layout">true</bool>
+</resources>
+
+ +

Khi đó, bạn có thể khởi tạo giá trị {@code mIsLargeLayout} trong phương pháp +{@link android.app.Activity#onCreate onCreate()} của hoạt động:

+ +
+boolean mIsLargeLayout;
+
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.activity_main);
+
+    mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
+}
+
+ + + +

Hiển thị một hoạt động dưới dạng một hộp thoại trên màn hình lớn

+ +

Thay vì hiển thị một hộp thoại thành UI toàn màn hình trên các màn hình nhỏ, bạn có thể đạt được +kết quả tương tự bằng cách hiển thị một {@link android.app.Activity} thành một hộp thoại trên +màn hình lớn. Phương pháp mà bạn chọn phụ thuộc vào thiết kế ứng dụng của bạn, nhưng +việc hiển thị một hoạt động thành một hộp thoại thường có ích khi ứng dụng của bạn đã được thiết kế cho màn hình +nhỏ và bạn muốn cải thiện trải nghiệm trên máy tính bảng bằng cách hiển thị một hoạt động có vòng đời ngắn +thành một hộp thoại.

+ +

Để hiển thị một hoạt động thành một hộp thoại chỉ khi trên màn hình lớn, +hãy áp dụng chủ đề {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} +cho phần tử bản kê khai {@code +<activity>}:

+ +
+<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
+
+ +

Để biết thêm thông tin về việc tạo kiểu cho các hoạt động của bạn bằng chủ đề, hãy xem hướng dẫn Kiểu và Chủ đề.

+ + + +

Bỏ một Hộp thoại

+ +

Khi người dùng chạm vào bất kỳ nút hành động nào được tạo bằng +{@link android.app.AlertDialog.Builder}, hệ thống sẽ bỏ hộp thoại cho bạn.

+ +

Hệ thống cũng bỏ hộp thoại khi người dùng chạm vào một mục trong một danh sách hộp thoại, trừ +khi danh sách sử dụng nút chọn một hoặc hộp kiểm. Nếu không, bạn có thể bỏ thủ công hộp thoại của mình +bằng cách gọi {@link android.support.v4.app.DialogFragment#dismiss()} trên {@link +android.support.v4.app.DialogFragment} của bạn.

+ +

Trong trường hợp bạn cần thực hiện các +hành động nhất định khi hộp thoại biến mất, bạn có thể triển khai phương pháp {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} trong {@link +android.support.v4.app.DialogFragment} của mình.

+ +

Bạn cũng có thể hủy bỏ một hộp thoại. Đây là một sự kiện đặc biệt chỉ báo người dùng +chủ ý rời khỏi hộp thoại mà không hoàn thành tác vụ. Điều này xảy ra nếu người dùng nhấn nút +Quay lại, chạm vào màn hình ngoài vùng hộp thoại, +hoặc nếu bạn công khai gọi {@link android.app.Dialog#cancel()} trên {@link +android.app.Dialog} (chẳng hạn như khi hồi đáp lại một nút "Hủy bỏ" trong hộp thoại).

+ +

Như nêu trong ví dụ bên trên, bạn có thể hồi đáp lại sự kiện hủy bỏ này bằng cách triển khai +{@link android.support.v4.app.DialogFragment#onCancel onCancel()} trong lớp {@link +android.support.v4.app.DialogFragment} của mình.

+ +

Lưu ý: Hệ thống sẽ gọi +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} trên mỗi sự kiện mà +gọi ra lệnh gọi lại {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Tuy nhiên, +nếu bạn gọi {@link android.app.Dialog#dismiss Dialog.dismiss()} hoặc {@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()}, +hệ thống sẽ gọi {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()} chứ +không phải {@link android.support.v4.app.DialogFragment#onCancel onCancel()}. Vì thế, nhìn chung bạn nên +gọi {@link android.support.v4.app.DialogFragment#dismiss dismiss()} khi người dùng nhấn nút +tích cực trong hộp thoại của bạn để xóa hộp thoại khỏi dạng xem.

+ + diff --git a/docs/html-intl/intl/vi/guide/topics/ui/menus.jd b/docs/html-intl/intl/vi/guide/topics/ui/menus.jd new file mode 100644 index 0000000000000000000000000000000000000000..8e9e1c412c5a9061bd348abf807d06d0e3702216 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=Menu +parent.title=Giao diện Người dùng +parent.link=index.html +@jd:body + + + +

Menu là một thành phần giao diện người dùng phổ biến trong nhiều loại ứng dụng. Để cung cấp một +trải nghiệm người dùng quen thuộc và nhất quán, bạn nên sử dụng các API {@link android.view.Menu} để trình bày +hành động người dùng và các tùy chọn khác trong hoạt động của mình.

+ +

Bắt đầu với Android 3.0 (API mức 11), các thiết bị dựa trên nền tảng Android không còn phải +cung cấp một nút Menu chuyên dụng nữa. Với sự thay đổi này, các ứng dụng Android cần tránh khỏi +sự phụ thuộc vào bảng điều khiển menu 6 mục truyền thống này mà thay vào đó cung cấp một thanh hành động để trình bày +các hành động người dùng thông dụng.

+ +

Mặc dù thiết kế và trải nghiệm người dùng đối với một số mục menu đã thay đổi, ngữ nghĩa để định nghĩa +tập hợp hành động và tùy chọn thì vẫn dựa trên các API {@link android.view.Menu}. Hướng dẫn +này trình bày cách tạo ba loại menu hay trình bày hành động cơ bản trên tất cả +phiên bản Android:

+ +
+
Menu tùy chọn và thanh hành động
+
Menu tùy chọn là tập hợp các mục menu cơ bản cho một +hoạt động. Đó là nơi bạn nên đặt các hành động có tác động chung tới ứng dụng, chẳng hạn như +"Tìm kiếm," "Soạn e-mail" và "Cài đặt." +

Nếu bạn đang phát triển cho phiên bản Android 2.3 hoặc thấp hơn, người dùng có thể +hiện bảng điều khiển menu tùy chọn bằng cách nhấn nút Menu.

+

Trên phiên bản Android 3.0 trở lên, các mục từ menu tùy chọn được trình bày bởi thanh hành động, là sự kết hợp giữa các mục hành động +trên màn hình và các tùy chọn tràn. Bắt đầu với phiên bản Android 3.0, nút Menu bị bỏ đi (một số +thiết bị +không có), vì thế bạn nên chuyển sang sử dụng thanh hành động để cho phép truy cập vào hành động và +các tùy chọn khác.

+

Xem phần về Tạo một Menu Tùy chọn.

+
+ +
Menu ngữ cảnh và chế độ hành động theo ngữ cảnh
+ +
Menu ngữ cảnh là một menu nổi xuất hiện khi +người dùng thực hiện nhấp giữ trên một phần tử. Nó cung cấp các hành động ảnh hưởng tới nội dung hoặc +khung ngữ cảnh được chọn. +

Khi phát triển cho phiên bản Android 3.0 trở lên, thay vào đó, bạn nên sử dụng chế độ hành động theo ngữ cảnh để kích hoạt các hành động trên nội dung được chọn. Chế độ này hiển thị +các mục hành động ảnh hưởng tới nội dung được chọn trong một thanh ở trên cùng của màn hình và cho phép người dùng +chọn nhiều mục.

+

Xem phần nói về Tạo Menu Ngữ cảnh.

+
+ +
Menu bật lên
+
Menu bật lên sẽ hiển thị danh sách các mục trong một danh sách thẳng đứng được neo vào dạng xem +đã gọi ra menu. Nên cung cấp một phần tràn gồm các hành động liên quan tới nội dung cụ thể hoặc +nhằm cung cấp các tùy chọn cho phần thứ hai của một lệnh. Các hành động trong một menu bật lên +không nên trực tiếp ảnh hưởng tới nội dung tương ứng—đó là việc của hành động ngữ cảnh +. Thay vào đó, menu bật lên áp dụng cho các hành động mở rộng liên quan tới các vùng nội dung trong hoạt động +của bạn. +

Xem phần về Tạo một Menu Bật lên.

+
+
+ + + +

Định nghĩa một Menu trong XML

+ +

Đối với tất cả các loại menu, Android cung cấp một định dạng XML chuẩn để định nghĩa các mục menu. +Thay vì xây dựng một menu trong mã của hoạt động của bạn, bạn nên định nghĩa một menu và tất cả các mục của nó trong một +tài nguyên menu XML. Khi đó, bạn có thể +bung tài nguyên menu (tải nó như một đối tượng {@link android.view.Menu}) trong hoạt động hoặc +phân đoạn của mình.

+ +

Sử dụng một tài nguyên menu là một cách làm hay vì một vài lý do:

+
    +
  • Nó dễ trực quan hóa cấu trúc menu trong XML hơn.
  • +
  • Nó tách riêng nội dung cho menu với mã hành vi của ứng dụng của bạn.
  • +
  • Nó cho phép bạn tạo các cấu hình menu phái sinh cho các phiên bản nền tảng, +kích cỡ màn hình khác nhau và các cấu hình khác bằng cách tận dụng khuôn khổ tài nguyên ứng dụng.
  • +
+ +

Để định nghĩa menu, hãy tạo một tệp XML bên trong thư mục res/menu/ +dự án của bạn và xây dựng menu với các phần tử sau:

+
+
<menu>
+
Định nghĩa một {@link android.view.Menu}, đó là một bộ chứa các mục menu. Phần tử +<menu> phải là một nút gốc cho tệp và có thể giữ một hoặc nhiều phần tử +<item><group>.
+ +
<item>
+
Tạo một {@link android.view.MenuItem}, nó biểu diễn một mục đơn trong một menu. Phần tử +này có thể chứa một phần tử <menu> được lồng nhau để tạo một menu con.
+ +
<group>
+
Một bộ chứa tùy chọn, vô hình cho các phần tử {@code <item>}. Nó cho phép bạn +phân loại các mục menu sao cho chúng chia sẻ các tính chất như trạng thái hiện hoạt và khả năng hiển thị. Để biết thêm +thông tin, hãy xem phần nói về Tạo Nhóm Menu.
+
+ + +

Sau đây là một menu ví dụ có tên là game_menu.xml:

+
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/new_game"
+          android:icon="@drawable/ic_new_game"
+          android:title="@string/new_game"
+          android:showAsAction="ifRoom"/>
+    <item android:id="@+id/help"
+          android:icon="@drawable/ic_help"
+          android:title="@string/help" />
+</menu>
+
+ +

Phần tử <item> hỗ trợ một vài thuộc tính bạn có thể sử dụng để định nghĩa biểu hiện bên ngoài +và hành vi của một mục. Các mục trong menu trên bao gồm những thuộc tính sau:

+ +
+
{@code android:id}
+
Một ID tài nguyên duy nhất đối với mục, nó cho phép ứng dụng có thể nhận ra mục đó +khi người dùng chọn nó.
+
{@code android:icon}
+
Một tham chiếu tới một nội dung vẽ được để dùng làm biểu tượng của mục.
+
{@code android:title}
+
Một tham chiếu tới một xâu để dùng làm tiêu đề của mục.
+
{@code android:showAsAction}
+
Quy định thời điểm và cách thức mục này nên xuất hiện như một mục hành động trong thanh hành động.
+
+ +

Đây là những thuộc tính quan trọng nhất bạn nên sử dụng, nhưng còn nhiều thuộc tính sẵn có khác. +Để biết thông tin về tất cả thuộc tính được hỗ trợ, hãy xem tài liệu Tài nguyên Menu.

+ +

Bạn có thể thêm một menu con vào một mục trong bất kỳ menu nào (ngoại trừ menu con) bằng cách thêm một phần tử {@code <menu>} +làm con của {@code <item>}. Các menu con thường hữu ích khi ứng dụng của bạn có nhiều +chức năng mà có thể được tổ chức thành các chủ đề, như các mục trong thanh menu của một ứng dụng PC (Tệp, +Chỉnh sửa, Dạng xem, v.v.). Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/file"
+          android:title="@string/file" >
+        <!-- "file" submenu -->
+        <menu>
+            <item android:id="@+id/create_new"
+                  android:title="@string/create_new" />
+            <item android:id="@+id/open"
+                  android:title="@string/open" />
+        </menu>
+    </item>
+</menu>
+
+ +

Để sử dụng menu trong hoạt động của mình, bạn cần bung tài nguyên menu (chuyển tài nguyên XML +thành một đối tượng có thể lập trình) bằng cách sử dụng {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()}. Trong những phần sau, bạn sẽ biết cách bung một menu đối với mỗi +loại menu.

+ + + +

Tạo một Menu Tùy chọn

+ +
+ +

Hình 1. Các menu tùy chọn trong +Trình duyệt, trên Android 2.3.

+
+ +

Menu tùy chọn là nơi bạn nên đưa vào hành động và các tùy chọn khác liên quan tới +ngữ cảnh hoạt động hiện tại, chẳng hạn như "Tìm kiếm," "Soạn e-mail," và "Cài đặt."

+ +

Nơi mà các mục trong menu tùy chọn của bạn xuất hiện trên màn hình sẽ phụ thuộc vào phiên bản mà bạn +phát triển ứng dụng của mình cho:

+ +
    +
  • Nếu bạn phát triển ứng dụng của mình cho phiên bản Android 2.3.x (API mức 10) hoặc +thấp hơn, nội dung của menu tùy chọn sẽ xuất hiện ở dưới cùng màn hình khi người dùng +nhấn nút Menu như minh họa trong hình 1. Khi được mở, phần hiển thị đầu tiên là +menu biểu tượng +với tối đa sáu mục menu. Nếu menu của bạn bao gồm nhiều hơn sáu mục, Android sẽ đặt +mục thứ sáu và phần còn lại vào một menu tràn mà người dùng có thể mở bằng cách chọn +Thêm nữa.
  • + +
  • Nếu bạn phát triển ứng dụng của mình cho phiên bản Android 3.0 (API mức 11) và +cao hơn, các mục từ menu tùy chọn sẵn ở trong thanh hành động. Theo mặc định, hệ thống +đặt tất cả các mục trong phần tràn hành động mà người dùng có thể hiện bằng biểu tượng tràn hành động phía +bên phải của thanh hành động (hoặc bằng cách nhấn nút Menu của thiết bị nếu có). Để +kích hoạt +truy cập nhanh vào các hành động quan trọng, bạn có thể đưa lên một vài mục xuất hiện trong thanh hành động bằng cách thêm +{@code android:showAsAction="ifRoom"} vào phần tử {@code <item>} tương ứng (xem hình +2).

    Để biết thêm thông tin về các mục hành động và hành vi khác của thanh hành động, hãy xem hướng dẫn Thanh Hành động.

    +

    Lưu ý: Ngay cả khi bạn không đang phát triển cho phiên bản Android 3.0 hoặc +cao hơn, bạn có thể xây dựng bố trí thanh hành động của chính mình cho hiệu ứng tương tự. Để xem ví dụ về cách bạn có thể hỗ trợ các phiên bản cao hơn +của Android bằng một thanh hành động, hãy xem mẫu Tương thích với Thanh Hành động +.

    +
  • +
+ + +

Hình 2. Thanh hành động từ ứng dụng Honeycomb Gallery, hiển thị +các tab điều hướng và một mục hành động máy ảnh (cộng với nút tràn hành động).

+ +

Bạn có thể khai báo các mục cho menu tùy chọn từ lớp con {@link android.app.Activity} +của bạn hoặc một lớp con {@link android.app.Fragment}. Nếu cả hoạt động của bạn và (các) phân đoạn +đều khai báo các mục cho menu tùy chọn, chúng sẽ được kết hợp lại trong UI. Các mục của hoạt động xuất hiện +trước, sau đó là các mục của từng phân đoạn theo thứ tự phân đoạn được thêm vào +hoạt động. Nếu cần, bạn có thể sắp xếp lại các mục menu bằng thuộc tính {@code android:orderInCategory} +trong mỗi {@code <item>} mà bạn cần di chuyển.

+ +

Để quy định menu tùy chọn cho một hoạt động, hãy khống chế {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} (các phân đoạn cung cấp +phương pháp gọi lại {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} của chính mình). Trong +phương pháp này, bạn có thể bung tài nguyên menu của mình (được định nghĩa trong XML) vào {@link +android.view.Menu} được cung cấp trong phương pháp gọi lại. Ví dụ:

+ +
+@Override
+public boolean onCreateOptionsMenu(Menu menu) {
+    MenuInflater inflater = {@link android.app.Activity#getMenuInflater()};
+    inflater.inflate(R.menu.game_menu, menu);
+    return true;
+}
+
+ +

Bạn cũng có thể thêm các mục menu bằng cách sử dụng {@link android.view.Menu#add(int,int,int,int) +add()} và truy xuất các mục bằng {@link android.view.Menu#findItem findItem()} để xem lại +tính chất của chúng bằng các API {@link android.view.MenuItem}.

+ +

Nếu bạn phát triển ứng dụng của mình cho phiên bản Android 2.3.x và thấp hơn, hệ thống gọi {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} để tạo menu tùy chọn +khi người dùng mở menu lần đầu tiên. Nếu bạn phát triển cho phiên bản Android 3.0 vào cao hơn, +hệ thống sẽ gọi {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} khi +bắt đầu hoạt động để hiển thị các mục cho thanh hành động.

+ + + +

Xử lý sự kiện nhấp

+ +

Khi người dùng chọn một mục từ menu tùy chọn (bao gồm các mục hành động trong thanh hành động), +hệ thống sẽ gọi phương pháp {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} của hoạt động của bạn. Phương pháp này thông qua {@link android.view.MenuItem} được chọn. Bạn +có thể nhận biết mục bằng cách gọi {@link android.view.MenuItem#getItemId()}, nó trả về ID duy nhất +cho mục menu (được định nghĩa bởi thuộc tính {@code android:id} trong tài nguyên menu hoặc bằng một +số nguyên được cấp cho phương pháp {@link android.view.Menu#add(int,int,int,int) add()}). Bạn có thể khớp +ID này với các mục menu đã biết để thực hiện hành động phù hợp. Ví dụ:

+ +
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    // Handle item selection
+    switch (item.getItemId()) {
+        case R.id.new_game:
+            newGame();
+            return true;
+        case R.id.help:
+            showHelp();
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+    }
+}
+
+ +

Khi bạn xử lý thành công một mục menu, trả về {@code true}. Nếu không xử lý được +mục menu, bạn nên gọi triển khai siêu lớp của {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} (triển khai +mặc định trả về sai).

+ +

Nếu hoạt động của bạn bao gồm các phân đoạn, trước tiên hệ thống sẽ gọi {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} cho hoạt động, rồi mới +cho từng phân đoạn (theo thứ tự thêm phân đoạn) tới khi trả về +{@code true} hoặc tất cả phân đoạn đều được gọi.

+ +

Mẹo: Android 3.0 thêm khả năng cho phép bạn định nghĩa hành vi +khi nhấp đối với một mục menu trong XML, bằng cách sử dụng thuộc tính {@code android:onClick}. Giá trị cho +thuộc tính phải là tên của một phương pháp được định nghĩa bởi hoạt động sử dụng menu. Phương pháp +phải công khai và chấp nhận một tham số {@link android.view.MenuItem} đơn—khi hệ thống +gọi phương pháp này, nó thông qua mục menu được chọn. Để biết thêm thông tin và ví dụ, hãy xem tài liệu Tài nguyên Menu.

+ +

Mẹo: Nếu ứng dụng của bạn chứa nhiều hoạt động và +một số chúng cung cấp menu tùy chọn tương tự, hãy xem xét tạo + một hoạt động chỉ triển khai các phương pháp {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} và {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()}. Sau đó, mở rộng lớp này đối với mỗi hoạt động cần chia sẻ +menu tùy chọn tương tự. Bằng cách này, bạn có thể quản lý một bộ mã để xử lý các hành động +menu và từng lớp hậu duệ kế thừa các hành vi menu. +Nếu bạn muốn thêm các mục menu vào một trong các hoạt động hậu duệ, +hãy khống chế {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} trong hoạt động đó. Gọi {@code super.onCreateOptionsMenu(menu)} sao cho +các mục menu gốc được tạo, sau đó thêm các mục menu mới bằng {@link +android.view.Menu#add(int,int,int,int) menu.add()}. Bạn cũng có thể khống chế hành vi +của siêu lớp đối với các mục menu riêng lẻ.

+ + +

Thay đổi các mục menu vào thời gian chạy

+ +

Sau khi hệ thống gọi {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}, nó sẽ giữ lại một thực thể của {@link android.view.Menu} mà bạn đưa vào và +sẽ không gọi lại {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} +trừ khi menu bị vô hiệu hóa vì lý do nào đó. Tuy nhiên, bạn chỉ nên sử dụng {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} để tạo trạng thái menu +ban đầu chứ không phải để thực hiện thay đổi trong vòng đời của hoạt động.

+ +

Nếu bạn muốn sửa đổi menu tùy chọn dựa trên +các sự kiện xảy ra trong vòng đời của hoạt động, bạn có thể làm vậy trong phương pháp + {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}. Phương pháp +này chuyển cho bạn đối tượng {@link android.view.Menu} như hiện đang có để bạn có thể sửa đổi nó, +chẳng hạn như thêm, xóa bỏ, hoặc vô hiệu hóa các mục. (Phân đoạn cũng cung cấp lệnh gọi lại {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()}.)

+ +

Trên phiên bản Android 2.3.x và thấp hơn, hệ thống gọi {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()} mỗi lần người dùng mở menu tùy chọn (nhấn nút Menu +).

+ +

Trên phiên bản Android 3.0 trở lên, menu tùy chọn được coi như luôn mở khi các mục menu được +trình bày trong thanh hành động. Khi một sự kiện xảy ra và bạn muốn thực hiện một cập nhật menu, bạn phải +gọi {@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()} để yêu cầu +hệ thống gọi {@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}.

+ +

Lưu ý: +Bạn không nên thay đổi các mục trong menu tùy chọn dựa trên {@link android.view.View} đang +trong tiêu điểm. Khi ở chế độ cảm ứng (khi người dùng không sử dụng bi xoay hay d-pad), các dạng xem +không thể lấy tiêu điểm, vì thế bạn không nên sử dụng tiêu điểm làm cơ sở để sửa đổi +các mục trong menu tùy chọn. Nếu bạn muốn cung cấp các mục menu nhạy cảm với ngữ cảnh cho một {@link +android.view.View}, hãy sử dụng một Menu Ngữ cảnh.

+ + + + +

Tạo một Menu Ngữ cảnh

+ +
+ +

Hình 3. Ảnh chụp màn hình một menu ngữ cảnh nổi (trái) +và thanh hành động ngữ cảnh (phải).

+
+ +

Menu ngữ cảnh sẽ đưa ra các hành động ảnh hưởng tới một mục hoặc khung ngữ cảnh cụ thể trong UI. Bạn +có thể cung cấp một menu ngữ cảnh cho bất kỳ dạng xem nào, nhưng chúng thường được sử dụng nhiều nhất cho các mục trong một {@link +android.widget.ListView}, {@link android.widget.GridView}, hoặc các bộ sưu tập dạng xem khác mà +người dùng có thể thực hiện hành động trực tiếp trên mỗi mục.

+ +

Có hai cách để cung cấp các hành động ngữ cảnh:

+
    +
  • Trong một menu ngữ cảnh nổi. Menu xuất hiện như một +danh sách nổi gồm nhiều mục menu (tương tự như một hộp thoại) khi người dùng thực hiện nhấp giữ (nhấn và +giữ) trên một dạng xem có khai báo hỗ trợ menu ngữ cảnh. Người dùng có thể thực hiện hành động +ngữ cảnh trên một mục vào một thời điểm.
  • + +
  • Trong chế độ hành động theo ngữ cảnh. Chế độ này là một hệ thống triển khai +{@link android.view.ActionMode} có chức năng hiển thị một thanh hành động ngữ cảnh ở bên trên +màn hình với các mục hành động ảnh hưởng tới (các) mục được chọn. Khi chế độ này hiện hoạt, người dùng +có thể thực hiện một hành động trên nhiều mục ngay lập tức (nếu ứng dụng của bạn cho phép).
  • +
+ +

Lưu ý: Chế độ hành động theo ngữ cảnh sẵn có trên phiên bản Android 3.0 (API +mức 11) và cao hơn và là kỹ thuật được ưu tiên cho việc hiển thị các hành động theo ngữ cảnh khi +sẵn có. Nếu ứng dụng của bạn hỗ trợ các phiên bản thấp hơn 3.0, vậy bạn nên quay lại menu ngữ cảnh +nổi trên những thiết bị đó.

+ + +

Tạo một menu ngữ cảnh nổi

+ +

Để cung cấp một menu ngữ cảnh nổi:

+
    +
  1. Đăng ký {@link android.view.View} mà menu ngữ cảnh nên được liên kết với bằng cách +gọi {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} và chuyển +cho nó {@link android.view.View}. +

    Nếu hoạt động của bạn sử dụng một {@link android.widget.ListView} hoặc {@link android.widget.GridView} và +bạn muốn từng mục cung cấp cùng menu ngữ cảnh, hãy đăng ký tất cả mục cho một menu ngữ cảnh bằng cách +chuyển {@link android.widget.ListView} hoặc {@link android.widget.GridView} cho {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()}.

    +
  2. + +
  3. Triển khai phương pháp {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +trong {@link android.app.Activity} hoặc {@link android.app.Fragment} của bạn. +

    Khi dạng xem được đăng ký nhận được một sự kiện nhấp giữ, hệ thống sẽ gọi phương pháp {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +của bạn. Đây là nơi bạn định nghĩa các mục menu, thường bằng cách bung một tài nguyên menu. Ví +dụ:

    +
    +@Override
    +public void onCreateContextMenu(ContextMenu menu, View v,
    +                                ContextMenuInfo menuInfo) {
    +    super.onCreateContextMenu(menu, v, menuInfo);
    +    MenuInflater inflater = getMenuInflater();
    +    inflater.inflate(R.menu.context_menu, menu);
    +}
    +
    + +

    {@link android.view.MenuInflater} cho phép bạn bung menu ngữ cảnh từ một tài nguyên menu. Các tham số của phương pháp gọi lại +bao gồm {@link android.view.View} +mà người dùng đã chọn và một đối tượng {@link android.view.ContextMenu.ContextMenuInfo} cung cấp +thông tin bổ sung về mục được chọn. Nếu hoạt động của bạn có một vài dạng xem mà mỗi dạng cung cấp +một menu ngữ cảnh khác nhau, bạn có thể sử dụng những tham số này để xác định menu ngữ cảnh nào cần +bung.

    +
  4. + +
  5. Triển khai {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}. +

    Khi người dùng chọn một mục menu, hệ thống sẽ gọi phương pháp này để bạn có thể thực hiện +hành động phù hợp. Ví dụ:

    + +
    +@Override
    +public boolean onContextItemSelected(MenuItem item) {
    +    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
    +    switch (item.getItemId()) {
    +        case R.id.edit:
    +            editNote(info.id);
    +            return true;
    +        case R.id.delete:
    +            deleteNote(info.id);
    +            return true;
    +        default:
    +            return super.onContextItemSelected(item);
    +    }
    +}
    +
    + +

    Phương pháp {@link android.view.MenuItem#getItemId()} sẽ truy vấn ID cho +mục menu được chọn, bạn nên gán mục này cho từng mục menu trong XML bằng cách sử dụng thuộc tính {@code +android:id} như trình bày trong phần về Định nghĩa một Menu trong +XML.

    + +

    Khi bạn xử lý thành công một mục menu, trả về {@code true}. Nếu bạn không xử lý mục menu, +bạn nên chuyển mục menu đó tới triển khai siêu lớp. Nếu hoạt động của bạn bao gồm nhiều phân đoạn, +hoạt động sẽ nhận được lệnh gọi lại này trước. Bằng cách gọi siêu lớp khi chưa được xử lý, hệ thống +sẽ chuyển sự kiện tới phương pháp gọi lại tương ứng trong từng phân đoạn, lần lượt (theo thứ tự +thêm phân đoạn) tới khi {@code true} hoặc {@code false} được trả về. (Triển khai +mặc định cho {@link android.app.Activity} và {@code android.app.Fragment} sẽ trả về {@code +false}, vì thế bạn nên luôn gọi siêu lớp khi chưa được xử lý.)

    +
  6. +
+ + +

Sử dụng chế độ hành động theo ngữ cảnh

+ +

Chế độ hành động theo ngữ cảnh là một triển khai hệ thống {@link android.view.ActionMode} +tập trung vào tương tác người dùng hướng tới việc thực hiện các hành động theo ngữ cảnh. Khi một +người dùng kích hoạt chế độ này bằng cách chọn một mục, một thanh hành động ngữ cảnh sẽ xuất hiện bên trên +màn hình để trình bày các hành động mà người dùng có thể thực hiện trên (các) mục đang được chọn. Trong khi +chế độ này được kích hoạt, người dùng có thể chọn nhiều mục (nếu bạn cho phép), bỏ chọn mục, và tiếp tục +điều hướng trong hoạt động (miễn là bạn sẵn lòng cho phép). Chế độ hành động bị vô hiệu hóa +và thanh hành động ngữ cảnh biến mất khi người dùng bỏ chọn tất cả các mục, nhấn nút QUAY LẠI, +hoặc chọn hành động Xong ở phía bên trái của thanh.

+ +

Lưu ý: Thanh hành động ngữ cảnh không nhất thiết +phải được liên kết với thanh hành động. Chúng vận hành +độc lập, mặc dù thanh hành động ngữ cảnh đè lên vị trí của thanh hành động +về mặt hiển thị.

+ +

Nếu bạn đang phát triển cho phiên bản Android 3.0 (API mức 11) hoặc cao hơn, bạn +nên sử dụng chế độ hành động theo ngữ cảnh để trình bày các hành động ngữ cảnh, thay vì sử dụng menu ngữ cảnh nổi.

+ +

Đối với các dạng xem cung cấp hành động ngữ cảnh, bạn nên thường xuyên gọi ra chế độ hành động theo ngữ cảnh +khi xảy ra một trong hai sự kiện sau (hoặc cả hai):

+
    +
  • Người dùng thực hiện nhấp giữ trên dạng xem.
  • +
  • Người dùng chọn một hộp kiểm hoặc một thành phần UI tương tự trong dạng xem.
  • +
+ +

Cách ứng dụng của bạn gọi ra chế độ hành động theo ngữ cảnh và định nghĩa hành vi cho từng +hành động phụ thuộc vào thiết kế của bạn. Cơ bản có hai thiết kế:

+
    +
  • Đối với các hành động ngữ cảnh trên các dạng xem riêng lẻ, tùy ý.
  • +
  • Đối với các hành động ngữ cảnh hàng loạt trên các nhóm mục trong một {@link +android.widget.ListView} hoặc {@link android.widget.GridView} (cho phép người dùng chọn nhiều +mục và thực hiện một hành động trên tất cả).
  • +
+ +

Các phần sau mô tả phần thiết lập cần thiết đối với từng kịch bản.

+ + +

Kích hoạt chế độ hành động theo ngữ cảnh cho các dạng xem riêng lẻ

+ +

Nếu muốn gọi ra chế độ hành động theo ngữ cảnh chỉ khi người dùng chọn các dạng xem +cụ thể, bạn nên:

+
    +
  1. Triển khai giao diện {@link android.view.ActionMode.Callback}. Trong các phương pháp gọi lại của giao diện, bạn +có thể quy định các hành động cho thanh hành động ngữ cảnh, hồi đáp các sự kiện nhấp trên mục hành động, và +xử lý các sự kiện vòng đời khác đối với chế độ hành động.
  2. +
  3. Gọi {@link android.app.Activity#startActionMode startActionMode()} khi bạn muốn hiển thị +thanh (chẳng hạn như khi người dùng nhấp giữ dạng xem).
  4. +
+ +

Ví dụ:

+ +
    +
  1. Triển khai giao diện {@link android.view.ActionMode.Callback ActionMode.Callback}: +
    +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
    +
    +    // Called when the action mode is created; startActionMode() was called
    +    @Override
    +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    +        // Inflate a menu resource providing context menu items
    +        MenuInflater inflater = mode.getMenuInflater();
    +        inflater.inflate(R.menu.context_menu, menu);
    +        return true;
    +    }
    +
    +    // Called each time the action mode is shown. Always called after onCreateActionMode, but
    +    // may be called multiple times if the mode is invalidated.
    +    @Override
    +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    +        return false; // Return false if nothing is done
    +    }
    +
    +    // Called when the user selects a contextual menu item
    +    @Override
    +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    +        switch (item.getItemId()) {
    +            case R.id.menu_share:
    +                shareCurrentItem();
    +                mode.finish(); // Action picked, so close the CAB
    +                return true;
    +            default:
    +                return false;
    +        }
    +    }
    +
    +    // Called when the user exits the action mode
    +    @Override
    +    public void onDestroyActionMode(ActionMode mode) {
    +        mActionMode = null;
    +    }
    +};
    +
    + +

    Lưu ý rằng những phương pháp gọi lại sự kiện này hầu như giống với các phương pháp gọi lại đối với menu tùy chọn, khác ở chỗ từng phương pháp cũng chuyển đối tượng {@link +android.view.ActionMode} được liên kết với sự kiện đó. Bạn có thể sử dụng các API {@link +android.view.ActionMode} để thực hiện những thay đổi khác nhau với CAB, chẳng hạn như sửa đổi tiêu đề và +phụ đề bằng {@link android.view.ActionMode#setTitle setTitle()} và {@link +android.view.ActionMode#setSubtitle setSubtitle()} (hữu ích khi muốn cho biết có bao nhiêu mục +được chọn).

    + +

    Cũng lưu ý rằng các bộ mẫu trên sẽ đặt biến {@code mActionMode} là rỗng khi +chế độ hành động bị hủy. Ở bước tiếp theo, bạn sẽ thấy cách nó được khởi tạo và việc lưu +biến thành viên trong hoạt động hoặc phân đoạn của bạn có thể hữu ích như thế nào.

    +
  2. + +
  3. Gọi {@link android.app.Activity#startActionMode startActionMode()} để kích hoạt chế độ hành động theo ngữ cảnh +khi phù hợp, chẳng hạn như để hồi đáp lại một sự kiện nhấp giữ trên một {@link +android.view.View}:

    + +
    +someView.setOnLongClickListener(new View.OnLongClickListener() {
    +    // Called when the user long-clicks on someView
    +    public boolean onLongClick(View view) {
    +        if (mActionMode != null) {
    +            return false;
    +        }
    +
    +        // Start the CAB using the ActionMode.Callback defined above
    +        mActionMode = getActivity().startActionMode(mActionModeCallback);
    +        view.setSelected(true);
    +        return true;
    +    }
    +});
    +
    + +

    Khi bạn gọi {@link android.app.Activity#startActionMode startActionMode()}, hệ thống sẽ trả về +{@link android.view.ActionMode} được tạo. Bằng cách lưu điều này trong một biến thành viên, bạn có thể +thực hiện thay đổi thanh hành động theo ngữ cảnh để hồi đáp những sự kiện khác. Trong mẫu trên, +{@link android.view.ActionMode} được sử dụng để đảm bảo rằng thực thể {@link android.view.ActionMode} không +được tạo lại nếu nó đã hiện hoạt, bằng cách kiểm tra xem thành viên có rỗng không trước khi khởi động +chế độ hành động.

    +
  4. +
+ + + +

Kích hoạt hành động theo ngữ cảnh hàng loạt trong ListView hoặc GridView

+ +

Nếu bạn có một bộ sưu tập các mục trong một {@link android.widget.ListView} hoặc {@link +android.widget.GridView} (hoặc một phần mở rộng khác của {@link android.widget.AbsListView}) và muốn +cho phép người dùng thực hiện các hành động hàng loạt, bạn nên:

+ +
    +
  • Triển khai giao diện {@link android.widget.AbsListView.MultiChoiceModeListener} và đặt nó +cho nhóm dạng xem bằng {@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()}. Trong các phương pháp gọi lại của trình nghe, bạn có thể quy định các hành động +cho thanh hành động theo ngữ cảnh, hồi đáp các sự kiện nhấp trên các mục hành động, và xử lý các phương pháp gọi lại khác +được kế thừa từ giao diện {@link android.view.ActionMode.Callback}.
  • + +
  • Gọi {@link android.widget.AbsListView#setChoiceMode setChoiceMode()} bằng tham đối {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL}.
  • +
+ +

Ví dụ:

+ +
+ListView listView = getListView();
+listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+    @Override
+    public void onItemCheckedStateChanged(ActionMode mode, int position,
+                                          long id, boolean checked) {
+        // Here you can do something when items are selected/de-selected,
+        // such as update the title in the CAB
+    }
+
+    @Override
+    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+        // Respond to clicks on the actions in the CAB
+        switch (item.getItemId()) {
+            case R.id.menu_delete:
+                deleteSelectedItems();
+                mode.finish(); // Action picked, so close the CAB
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+        // Inflate the menu for the CAB
+        MenuInflater inflater = mode.getMenuInflater();
+        inflater.inflate(R.menu.context, menu);
+        return true;
+    }
+
+    @Override
+    public void onDestroyActionMode(ActionMode mode) {
+        // Here you can make any necessary updates to the activity when
+        // the CAB is removed. By default, selected items are deselected/unchecked.
+    }
+
+    @Override
+    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+        // Here you can perform updates to the CAB due to
+        // an {@link android.view.ActionMode#invalidate} request
+        return false;
+    }
+});
+
+ +

Vậy là xong. Lúc này, khi người dùng chọn một mục bằng nhấp giữ, hệ thống sẽ gọi phương pháp {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +và hiển thị thanh hành động theo ngữ cảnh với các hành động được quy định. Trong khi thanh hành động theo ngữ cảnh +hiển thị, người dùng có thể chọn thêm mục.

+ +

Trong một số trường hợp mà các hành động ngữ cảnh cung cấp các mục hành động chung, bạn có thể muốn +thêm một hộp kiểm hoặc một phần tử UI tương tự để cho phép người dùng chọn các mục, vì +họ có thể không phát hiện được hành vi nhấp giữ. Khi một người dùng chọn hộp kiểm, bạn +có thể gọi ra chế độ hành động theo ngữ cảnh bằng cách thiết đặt mục danh sách tương ứng về trạng thái +đã chọn bằng {@link android.widget.AbsListView#setItemChecked setItemChecked()}.

+ + + + +

Tạo một Menu Bật lên

+ +
+ +

Hình 4. Menu bật lên trong ứng dụng Gmail, được neo vào nút tràn +ở trên cùng bên phải.

+
+ +

{@link android.widget.PopupMenu} là một menu mô thái được neo vào một {@link android.view.View}. +Nó xuất hiện bên dưới dạng xem dấu neo nếu có khoảng trống, hoặc bên trên dạng xem nếu không. Nó có ích cho việc:

+
    +
  • Cung cấp một menu kiểu tràn cho các hành động mà liên quan tới nội dung cụ thể (chẳng hạn như tiêu đề e-mail +của Gmail như minh họa trong hình 4). +

    Lưu ý: Nó không giống như một menu ngữ cảnh, vốn thường +áp dụng cho các hành động mà ảnh hưởng tới nội dung được chọn. Đối với những hành động ảnh hưởng tới nội dung +được chọn, hãy sử dụng chế độ hành động theo ngữ cảnh hoặc menu ngữ cảnh nổi.

  • +
  • Cung cấp một phần thứ hai của câu lệnh (chẳng hạn như một nút được đánh dấu "Thêm" +có chức năng tạo ra một menu bật lên với các tùy chọn "Thêm" khác nhau).
  • +
  • Cung cấp một danh sách thả xuống tương tự như {@link android.widget.Spinner}, nó không giữ lại một +lựa chọn liên tục.
  • +
+ + +

Lưu ý: {@link android.widget.PopupMenu} sẵn có với API +mức 11 trở lên.

+ +

Nếu bạn định nghĩa menu của mình trong XML, sau đây là cách bạn có thể hiển thị menu bật lên:

+
    +
  1. Khởi tạo một {@link android.widget.PopupMenu} bằng hàm dựng của nó, có chức năng đưa +ứng dụng hiện tại {@link android.content.Context} và {@link android.view.View} tới menu +mà sẽ được neo.
  2. +
  3. Sử dụng {@link android.view.MenuInflater} để bung tài nguyên menu của bạn vào đối tượng {@link +android.view.Menu} được trả về bởi {@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()}. Trên API mức 14 trở lên, bạn có thể sử dụng +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()} thay thế.
  4. +
  5. Gọi {@link android.widget.PopupMenu#show() PopupMenu.show()}.
  6. +
+ +

Ví dụ, sau đây là một nút với thuộc tính {@link android.R.attr#onClick android:onClick} có chức năng +hiển thị một menu bật lên:

+ +
+<ImageButton
+    android:layout_width="wrap_content" 
+    android:layout_height="wrap_content" 
+    android:src="@drawable/ic_overflow_holo_dark"
+    android:contentDescription="@string/descr_overflow_button"
+    android:onClick="showPopup" />
+
+ +

Khi đó, hoạt động có thể hiển thị menu bật lên như sau:

+ +
+public void showPopup(View v) {
+    PopupMenu popup = new PopupMenu(this, v);
+    MenuInflater inflater = popup.getMenuInflater();
+    inflater.inflate(R.menu.actions, popup.getMenu());
+    popup.show();
+}
+
+ +

Trong API mức 14 trở lên, bạn có thể kết hợp hai dòng có chức năng bung menu bằng {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()}.

+ +

Menu bị bỏ qua khi người dùng chọn một mục hoặc chạm vào bên ngoài vùng +menu. Bạn có thể lắng nghe báo hiệu sự kiện bỏ bằng cách sử dụng {@link +android.widget.PopupMenu.OnDismissListener}.

+ +

Xử lý sự kiện nhấp

+ +

Để thực hiện một +hành động khi người dùng chọn một mục menu, bạn phải triển khai giao diện {@link +android.widget.PopupMenu.OnMenuItemClickListener} và đăng ký nó với {@link +android.widget.PopupMenu} của mình bằng cách gọi {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()}. Khi người dùng chọn một mục, hệ thống sẽ gọi lệnh gọi lại {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} trong +giao diện của bạn.

+ +

Ví dụ:

+ +
+public void showMenu(View v) {
+    PopupMenu popup = new PopupMenu(this, v);
+
+    // This activity implements OnMenuItemClickListener
+    popup.setOnMenuItemClickListener(this);
+    popup.inflate(R.menu.actions);
+    popup.show();
+}
+
+@Override
+public boolean onMenuItemClick(MenuItem item) {
+    switch (item.getItemId()) {
+        case R.id.archive:
+            archive(item);
+            return true;
+        case R.id.delete:
+            delete(item);
+            return true;
+        default:
+            return false;
+    }
+}
+
+ + +

Tạo Nhóm Menu

+ +

Nhóm menu là một tập hợp các mục menu chia sẻ những đặc điểm nhất định. Với một nhóm, bạn có thể +:

+
    +
  • Hiển thị hoặc ẩn tất cả các mục bằng {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()}
  • +
  • Kích hoạt hoặc vô hiệu hóa tất cả các mục bằng {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()}
  • +
  • Quy định xem tất cả các mục có thể chọn hay không bằng {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}
  • +
+ +

Bạn có thể tạo một nhóm bằng cách lồng các phần tử {@code <item>} bên trong một phần tử {@code <group>} +vào tài nguyên menu của bạn hoặc bằng cách quy định một ID nhóm bằng phương pháp {@link +android.view.Menu#add(int,int,int,int) add()}.

+ +

Sau đây là một ví dụ về tài nguyên menu bao gồm một nhóm:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/menu_save"
+          android:icon="@drawable/menu_save"
+          android:title="@string/menu_save" />
+    <!-- menu group -->
+    <group android:id="@+id/group_delete">
+        <item android:id="@+id/menu_archive"
+              android:title="@string/menu_archive" />
+        <item android:id="@+id/menu_delete"
+              android:title="@string/menu_delete" />
+    </group>
+</menu>
+
+ +

Các mục nằm trong nhóm xuất hiện ở cùng cấp như mục đầu tiên—tất cả ba mục +trong menu đều là các mục đồng cấp. Tuy nhiên, bạn có thể sửa đổi các đặc điểm của hai +mục trong nhóm bằng cách tham chiếu ID nhóm và sử dụng các phương pháp được liệt kê bên trên. Hệ thống cũng sẽ +không bao giờ tách riêng các mục đã ghép nhóm. Ví dụ, nếu bạn khai báo {@code +android:showAsAction="ifRoom"} cho từng mục, chúng sẽ hoặc đều xuất hiện trong thanh hành động +hoặc đều xuất hiện trong phần tràn hành động.

+ + +

Sử dụng mục menu có thể chọn

+ +
+ +

Hình 5. Ảnh chụp màn hình một menu con với các mục +có thể chọn.

+
+ +

Một mục có thể có ích như một giao diện để bật và tắt các tùy chọn, bằng cách sử dụng một hộp kiểm cho +các tùy chọn độc lập, hoặc nút chọn một cho các nhóm +tùy chọn loại trừ lẫn nhau. Hình 5 minh họa một menu con với các mục có thể chọn bằng các nút + chọn một.

+ +

Lưu ý: Các mục menu trong Menu Biểu tượng (từ menu tùy chọn) không thể +hiển thị một hộp kiểm hay nút chọn một. Nếu bạn chọn đặt các mục trong Menu Biểu tượng là có thể chọn, +bạn phải chỉ định trạng thái được chọn bằng cách tráo đổi biểu tượng và/hoặc văn bản +mỗi lần trạng thái thay đổi một cách thủ công.

+ +

Bạn có thể định nghĩa hành vi có thể chọn cho các mục menu riêng lẻ bằng cách sử dụng thuộc tính {@code +android:checkable} trong phần tử {@code <item>}, hoặc cho toàn bộ nhóm với +thuộc tính {@code android:checkableBehavior} trong phần tử {@code <group>}. Ví +dụ, tất cả các mục trong nhóm menu này có thể chọn bằng một nút chọn một:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <group android:checkableBehavior="single">
+        <item android:id="@+id/red"
+              android:title="@string/red" />
+        <item android:id="@+id/blue"
+              android:title="@string/blue" />
+    </group>
+</menu>
+
+ +

Thuộc tính {@code android:checkableBehavior} chấp nhận hoặc: +

+
{@code single}
+
Chỉ chọn được một mục từ nhóm (nút chọn một)
+
{@code all}
+
Có thể chọn được tất cả các mục (hộp kiểm)
+
{@code none}
+
Không chọn được mục nào
+
+ +

Bạn có thể áp dụng một trạng thái được chọn mặc định cho một mục bằng cách sử dụng thuộc tính {@code android:checked} trong +phần tử {@code <item>} và thay đổi nó trong mã bằng phương pháp {@link +android.view.MenuItem#setChecked(boolean) setChecked()}.

+ +

Khi chọn một mục có thể chọn, hệ thống sẽ gọi phương pháp gọi lại mục được chọn tương ứng +(chẳng hạn như {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). Chính +ở đây bạn phải đặt trạng thái của hộp kiểm, vì hộp kiểm hay nút chọn một đều không +tự động thay đổi trạng thái của nó. Bạn có thể truy vấn trạng thái hiện tại của mục (như trước khi +người dùng chọn) bằng {@link android.view.MenuItem#isChecked()} và sau đó đặt trạng thái được chọn bằng +{@link android.view.MenuItem#setChecked(boolean) setChecked()}. Ví dụ:

+ +
+@Override
+public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+        case R.id.vibrate:
+        case R.id.dont_vibrate:
+            if (item.isChecked()) item.setChecked(false);
+            else item.setChecked(true);
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+    }
+}
+
+ +

Nếu bạn không đặt trạng thái được chọn bằng cách này, khi đó trạng thái hiển thị của mục (hộp kiểm hoặc +nút chọn một) sẽ không +thay đổi khi người dùng chọn nó. Khi bạn đặt trạng thái, hoạt động sẽ giữ nguyên trạng thái được chọn +của mục đó để khi người dùng mở menu sau, trạng thái được chọn mà bạn đặt +sẽ được hiển thị.

+ +

Lưu ý: +Các mục menu có thể chọn được chỉ định sử dụng trên mỗi phiên và không được lưu sau khi +ứng dụng bị hủy. Nếu bạn có các cài đặt ứng dụng mà bạn muốn lưu cho người dùng, +bạn nên lưu trữ dữ liệu bằng cách sử dụng Tùy chọn dùng chung.

+ + + +

Thêm Mục Menu dựa trên Ý định

+ +

Đôi khi bạn sẽ muốn một mục menu khởi chạy một hoạt động bằng cách sử dụng một {@link android.content.Intent} +(dù đó là một hoạt động trong ứng dụng của bạn hay một ứng dụng khác). Khi bạn biết ý định mà mình +muốn sử dụng và có một mục menu cụ thể sẽ khởi tạo ý định, bạn có thể thực thi ý định +bằng {@link android.app.Activity#startActivity(Intent) startActivity()} trong phương pháp gọi lại +phù hợp theo mục được chọn (chẳng hạn như lệnh gọi lại {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}).

+ +

Tuy nhiên, nếu bạn không chắc chắn rằng thiết bị của người dùng +chứa một ứng dụng xử lý ý định đó thì việc thêm một mục menu gọi nó ra có thể dẫn đến +mục menu không hoạt động, do ý định có thể không phân giải thành một +hoạt động. Để giải quyết điều này, Android cho phép bạn linh hoạt thêm các mục menu vào menu của mình +khi Android tìm các hoạt động trên thiết bị để xử lý ý định của bạn.

+ +

Để thêm các mục menu dựa trên các hoạt động sẵn có mà chấp nhận ý định:

+
    +
  1. Định nghĩa một +ý định bằng thể loại {@link android.content.Intent#CATEGORY_ALTERNATIVE} và/hoặc +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, cộng với bất kỳ yêu cầu nào khác.
  2. +
  3. Gọi {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}. Sau đó, Android tìm kiếm bất kỳ ứng dụng nào có thể thực hiện ý định +và thêm chúng vào menu của bạn.
  4. +
+ +

Nếu không có ứng dụng được cài đặt +mà thỏa mãn ý định thì không có mục menu nào được thêm vào.

+ +

Lưu ý: +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} được sử dụng để xử lý +phần tử đang được chọn trên màn hình. Vì vậy, nó chỉ nên được sử dụng khi tạo một Menu trong {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()}.

+ +

Ví dụ:

+ +
+@Override
+public boolean onCreateOptionsMenu(Menu menu){
+    super.onCreateOptionsMenu(menu);
+
+    // Create an Intent that describes the requirements to fulfill, to be included
+    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
+    Intent intent = new Intent(null, dataUri);
+    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
+
+    // Search and populate the menu with acceptable offering applications.
+    menu.addIntentOptions(
+         R.id.intent_group,  // Menu group to which new items will be added
+         0,      // Unique item ID (none)
+         0,      // Order for the items (none)
+         this.getComponentName(),   // The current activity name
+         null,   // Specific items to place first (none)
+         intent, // Intent created above that describes our requirements
+         0,      // Additional flags to control items (none)
+         null);  // Array of MenuItems that correlate to specific items (none)
+
+    return true;
+}
+ +

Đối với mỗi hoạt động được tìm thấy mà cung cấp một bộ lọc ý định khớp với ý định được định nghĩa, một mục menu +được thêm, bằng cách sử dụng giá trị trong android:label của bộ lọc ý định làm +tiêu đề của mục menu và biểu tượng của ứng dụng làm biểu tượng của mục menu. Phương pháp +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} trả về số mục menu được thêm.

+ +

Lưu ý: Khi bạn gọi {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()}, nó sẽ khống chế bất kỳ và tất cả các mục menu theo nhóm menu được quy định trong tham đối +đầu tiên.

+ + +

Cho phép hoạt động của bạn được thêm vào các menu khác

+ +

Bạn cũng có thể cung cấp các dịch vụ của hoạt động của mình cho các ứng dụng khác, vì vậy ứng dụng của bạn +có thể nằm trong menu của các ứng dụng khác (đảo ngược vai trò nêu trên).

+ +

Để được nằm trong menu của ứng dụng khác, bạn cần định nghĩa một bộ lọc +ý định như bình thường, nhưng đảm bảo thêm các giá trị {@link android.content.Intent#CATEGORY_ALTERNATIVE} +và/hoặc {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} cho thể loại +bộ lọc ý định. Ví dụ:

+
+<intent-filter label="@string/resize_image">
+    ...
+    <category android:name="android.intent.category.ALTERNATIVE" />
+    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
+    ...
+</intent-filter>
+
+ +

Tìm hiểu thêm về việc ghi các bộ lọc ý định trong tài liệu +Ý định và Bộ lọc Ý định.

+ +

Để tham khảo một ứng dụng mẫu sử dụng kỹ thuật này, hãy xem mã mẫu +Note +Pad.

diff --git a/docs/html-intl/intl/vi/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/vi/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 0000000000000000000000000000000000000000..5890cb331b965ddb22e97cb2337a58d98bc6897d --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=Thông báo +@jd:body + + +

+ Thông báo là một thông điệp bạn có thể hiển thị với người dùng bên ngoài + UI bình thường của ứng dụng của bạn. Khi bạn yêu cầu hệ thống phát hành một thông báo, trước tiên nó xuất hiện như một biểu tượng trong + khu vực thông báo. Để xem chi tiết thông báo, người dùng mở + ngăn kéo thông báo. Cả khu vực thông báo và ngăn kéo thông báo đều + là các khu vực do hệ thống kiểm soát mà người dùng có thể xem vào bất cứ lúc nào. +

+ +

+ Hình 1. Thông báo trong khu vực thông báo. +

+ +

+ Hình 2. Thông báo trong ngăn kéo thông báo. +

+ +

Lưu ý: Ngoại trừ phần được lưu ý, hướng dẫn này nhắc đến +lớp {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +trong phiên bản 4 của Thư viện Hỗ trợ. +Lớp {@link android.app.Notification.Builder Notification.Builder} đã được thêm vào trong Android +3.0 (API mức 11).

+ +

Cân nhắc Thiết kế

+ +

Là một phần quan trọng của giao diện người dùng Android, thông báo có các hướng dẫn thiết kế của riêng mình. +Những thay đổi thiết kế cơ bản được giới thiệu trong Android 5.0 (API mức 21) đặc biệt +quan trọng và bạn nên xem phần đào tạo về Thiết kế Material +để biết thêm thông tin. Để tìm hiểu cách thiết kế thông báo và tương tác của chúng, hãy đọc hướng dẫn thiết kế +Thông báo.

+ +

Tạo một Thông báo

+ +

Bạn quy định thông tin UI và các hành động cho một thông báo trong một đối tượng +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. +Để tạo chính thông báo, bạn gọi +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()}, +nó sẽ trả về một đối tượng {@link android.app.Notification} chứa những đặc tả của bạn. Để phát hành +thông báo, bạn chuyển đối tượng {@link android.app.Notification} tới hệ thống bằng cách gọi +{@link android.app.NotificationManager#notify NotificationManager.notify()}.

+ +

Nội dung thông báo được yêu cầu

+

+ Một đối tượng {@link android.app.Notification} phải chứa những điều sau: +

+
    +
  • + Một biểu tượng nhỏ được đặt bởi + {@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} +
  • +
  • + Một tiêu đề được đặt bởi + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} +
  • +
  • + Văn bản chi tiết được đặt bởi + {@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} +
  • +
+

Nội dung và cài đặt thông báo tùy chọn

+

+ Tất cả cài đặt và nội dung thông báo khác đều mang tính tùy chọn. Để tìm hiểu thêm về chúng, + hãy xem tài liệu tham khảo cho {@link android.support.v4.app.NotificationCompat.Builder}. +

+ +

Hành động thông báo

+

+ Mặc dù chúng mang tính tùy chọn, bạn nên thêm ít nhất một hành động vào thông báo của mình. + Một hành động cho phép người dùng đi trực tiếp từ thông báo tới một + {@link android.app.Activity} trong ứng dụng của bạn, nơi mà họ có thể xem thêm một hoặc nhiều sự kiện + hoặc làm việc thêm. +

+

+ Một thông báo có thể cung cấp nhiều hành động. Bạn nên luôn định nghĩa hành động mà được + kích khởi khi người dùng nhấp vào thông báo; thường thì hành động này mở ra một + {@link android.app.Activity} trong ứng dụng của bạn. Bạn cũng có thể thêm các nút vào thông báo + để thực hiện những hành động bổ sung chẳng hạn như báo lại báo thức hay hồi đáp ngay lập tức một tin nhắn + văn bản; tính năng này sẵn có từ phiên bản Android 4.1. Nếu sử dụng các nút hành động bổ sung, bạn + cũng phải cung cấp tính năng của chúng trong một {@link android.app.Activity} trong ứng dụng của bạn; xem + phần Xử lý tính tương thích để biết thêm chi tiết. +

+

+ Bên trong một {@link android.app.Notification}, bản thân hành động được định nghĩa bởi một + {@link android.app.PendingIntent} chứa một + {@link android.content.Intent} có chức năng bắt đầu + một {@link android.app.Activity} trong ứng dụng của bạn. Để liên kết + {@link android.app.PendingIntent} với một cử chỉ, hãy gọi phương pháp + {@link android.support.v4.app.NotificationCompat.Builder} phù hợp. Ví dụ, nếu bạn muốn bắt đầu + {@link android.app.Activity} khi người dùng nhấp vào văn bản thông báo trong + ngăn kéo thông báo, bạn hãy thêm {@link android.app.PendingIntent} bằng cách gọi + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()}. +

+

+ Bắt đầu một {@link android.app.Activity} khi người dùng nhấp vào thông báo là kịch bản + hành động phổ biến nhất. Bạn cũng có thể bắt đầu một {@link android.app.Activity} khi người dùng + bỏ một thông báo. Trong phiên bản Android 4.1 và sau đó, bạn có thể bắt đầu một + {@link android.app.Activity} từ một nút hành động. Để tìm hiểu thêm, hãy đọc hướng dẫn tham khảo cho + {@link android.support.v4.app.NotificationCompat.Builder}. +

+ +

Mức ưu tiên của thông báo

+

+ Nếu muốn, bạn có thể đặt mức ưu tiên của một thông báo. Mức ưu tiên đóng vai trò + như một gợi ý cho UI của thiết bị về cách thông báo sẽ được hiển thị. + Để đặt mức ưu tiên của một thông báo, hãy gọi {@link + android.support.v4.app.NotificationCompat.Builder#setPriority(int) + NotificationCompat.Builder.setPriority()} và chuyển trong một trong các hằng số mức ưu tiên {@link + android.support.v4.app.NotificationCompat}. Có năm mức ưu tiên, + dao động từ {@link + android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) đến {@link + android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); nếu không được đặt, + mức ưu tiên mặc định thành {@link + android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0). +

+

Để biết thông tin về việc đặt một mức ưu tiên phù hợp, hãy xem phần "Đặt + và quản lý mức ưu tiên của thông báo cho đúng" trong hướng dẫn Thiết kế Thông báo +. +

+ +

Tạo một thông báo đơn giản

+

+ Đoạn mã HTML sau minh họa một thông báo đơn giản, trong đó quy định một hoạt động sẽ mở khi + người dùng nhấp vào thông báo. Để ý rằng đoạn mã này sẽ tạo một đối tượng + {@link android.support.v4.app.TaskStackBuilder} và sử dụng nó để tạo + {@link android.app.PendingIntent} cho hành động. Kiểu mẫu này được giải thích chi tiết hơn + trong phần + Giữ lại Điều hướng khi Bắt đầu một Hoạt động: +

+
+NotificationCompat.Builder mBuilder =
+        new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.notification_icon)
+        .setContentTitle("My notification")
+        .setContentText("Hello World!");
+// Creates an explicit intent for an Activity in your app
+Intent resultIntent = new Intent(this, ResultActivity.class);
+
+// The stack builder object will contain an artificial back stack for the
+// started Activity.
+// This ensures that navigating backward from the Activity leads out of
+// your application to the Home screen.
+TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+// Adds the back stack for the Intent (but not the Intent itself)
+stackBuilder.addParentStack(ResultActivity.class);
+// Adds the Intent that starts the Activity to the top of the stack
+stackBuilder.addNextIntent(resultIntent);
+PendingIntent resultPendingIntent =
+        stackBuilder.getPendingIntent(
+            0,
+            PendingIntent.FLAG_UPDATE_CURRENT
+        );
+mBuilder.setContentIntent(resultPendingIntent);
+NotificationManager mNotificationManager =
+    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+// mId allows you to update the notification later on.
+mNotificationManager.notify(mId, mBuilder.build());
+
+

Vậy là xong. Người dùng của bạn hiện đã được thông báo.

+ +

Áp dụng bố trí mở rộng cho một thông báo

+

+ Để có một thông báo xuất hiện trong một dạng xem mở rộng, trước tiên hãy tạo một đối tượng + {@link android.support.v4.app.NotificationCompat.Builder} với các tùy chọn dạng xem thông thường + mà bạn muốn. Tiếp theo, hãy gọi {@link android.support.v4.app.NotificationCompat.Builder#setStyle + Builder.setStyle()} với một đối tượng bố trí mở rộng làm tham đối. +

+

+ Ghi nhớ rằng các thông báo mở rộng không sẵn có trên các nền tảng trước Android 4.1. Để + tìm hiểu về cách xử lý thông báo đối với nền tảng phiên bản Android 4.1 và trước đó, hãy đọc + phần Xử lý tính tương thích. +

+

+ Ví dụ, đoạn mã HTML sau minh họa cách thay đổi thông báo được tạo + trong đoạn mã HTML trước để sử dụng bố trí mở rộng: +

+
+NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
+    .setSmallIcon(R.drawable.notification_icon)
+    .setContentTitle("Event tracker")
+    .setContentText("Events received")
+NotificationCompat.InboxStyle inboxStyle =
+        new NotificationCompat.InboxStyle();
+String[] events = new String[6];
+// Sets a title for the Inbox in expanded layout
+inboxStyle.setBigContentTitle("Event tracker details:");
+...
+// Moves events into the expanded layout
+for (int i=0; i < events.length; i++) {
+
+    inboxStyle.addLine(events[i]);
+}
+// Moves the expanded layout object into the notification object.
+mBuilder.setStyle(inBoxStyle);
+...
+// Issue the notification here.
+
+ +

Xử lý tính tương thích

+ +

+ Không phải tất cả tính năng thông báo đều sẵn có đối với một phiên bản cụ thể, mặc dù + các phương pháp đặt chúng đều nằm trong lớp thư viện hỗ trợ + {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder}. + Ví dụ, nút hành động phụ thuộc vào thông báo mở rộng chỉ xuất hiện trên phiên bản Android + 4.1 trở lên, bởi bản thân thông báo mở rộng chỉ sẵn có trên phiên bản + Android 4.1 trở lên. +

+

+ Để đảm bảo tính tương thích tốt nhất, hãy tạo thông báo bằng + {@link android.support.v4.app.NotificationCompat NotificationCompat} và các lớp con của nó, + đặc biệt là {@link android.support.v4.app.NotificationCompat.Builder + NotificationCompat.Builder}. Bên cạnh đó, hãy tuân theo tiến trình sau khi bạn triển khai một thông báo: +

+
    +
  1. + Cung cấp tất cả tính năng thông báo cho tất cả người dùng, không phụ thuộc vào phiên bản + mà họ đang sử dụng. Để làm vậy, hãy xác minh rằng tất cả tính năng đều sẵn có từ một + {@link android.app.Activity} trong ứng dụng của bạn. Bạn có thể muốn thêm một + {@link android.app.Activity} mới để làm điều này. +

    + Ví dụ, nếu bạn muốn sử dụng + {@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} để + cung cấp khả năng điều khiển dừng và bắt đầu phát lại phương tiện, trước tiên hãy triển khai khả năng + điều khiển này trong một {@link android.app.Activity} trong ứng dụng của bạn. +

    +
  2. +
  3. + Đảm bảo rằng tất cả người dùng đều có thể tiếp cận với tính năng trong {@link android.app.Activity}, + bằng cách cho nó khởi động khi người dùng nhấp vào thông báo. Để làm điều này, + hãy tạo một {@link android.app.PendingIntent} + cho {@link android.app.Activity}. Gọi + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} để thêm {@link android.app.PendingIntent} vào thông báo. +
  4. +
  5. + Bây giờ, hãy thêm các tính năng thông báo mở rộng mà bạn muốn sử dụng vào thông báo. Ghi nhớ + rằng bất kỳ tính năng nào mà bạn thêm cũng phải sẵn có trong {@link android.app.Activity} + mà bắt đầu khi người dùng nhấp vào thông báo. +
  6. +
+ + + + +

Quản lý Thông báo

+

+ Khi cần phát hành một thông báo nhiều lần cho cùng loại sự kiện, bạn + nên tránh tạo một thông báo hoàn toàn mới. Thay vào đó, bạn nên cân nhắc cập nhật + một thông báo trước đó, hoặc bằng cách thay đổi một vài giá trị hoặc bằng cách thêm vào nó, hoặc cả hai. +

+

+ Ví dụ, Gmail thông báo với người dùng rằng e-mail mới đã đến bằng cách tăng số đếm + tin nhắn chưa đọc và bằng cách thêm một phần tóm tắt từng e-mail vào thông báo. Đây được gọi là + "xếp chồng" thông báo; nó được mô tả chi tiết hơn trong phần hướng dẫn Thiết kế + Thông báo. +

+

+ Lưu ý: Tính năng Gmail này yêu cầu bố trí mở rộng "hộp thư đến", đó là + một phần của tính năng thông báo mở rộng sẵn có bắt đầu từ Android 4.1. +

+

+ Phần sau mô tả cách cập nhật thông báo và cả cách loại bỏ chúng. +

+

Cập nhật thông báo

+

+ Để thiết lập một thông báo để nó có thể được cập nhật, hãy phát hành nó cùng một ID thông báo bằng cách gọi + {@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()}. + Để cập nhật thông báo sau khi bạn đã phát hành + nó, hãy cập nhật hoặc tạo một đối tượng {@link android.support.v4.app.NotificationCompat.Builder}, + xây dựng một đối tượng {@link android.app.Notification} từ nó, và phát hành + {@link android.app.Notification} với cùng ID mà bạn đã sử dụng trước đó. Nếu + thông báo trước đó vẫn hiển thị, hệ thống sẽ cập nhật nó từ nội dung của + đối tượng {@link android.app.Notification}. Nếu thông báo trước đó đã bị bỏ đi, một + thông báo mới sẽ được tạo thay thế. +

+

+ Đoạn mã HTML sau minh họa một thông báo được cập nhật để phản ánh + số sự kiện đã xảy ra. Nó xếp chồng thông báo, hiển thị một tóm tắt: +

+
+mNotificationManager =
+        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+// Sets an ID for the notification, so it can be updated
+int notifyID = 1;
+mNotifyBuilder = new NotificationCompat.Builder(this)
+    .setContentTitle("New Message")
+    .setContentText("You've received new messages.")
+    .setSmallIcon(R.drawable.ic_notify_status)
+numMessages = 0;
+// Start of a loop that processes data and then notifies the user
+...
+    mNotifyBuilder.setContentText(currentText)
+        .setNumber(++numMessages);
+    // Because the ID remains unchanged, the existing notification is
+    // updated.
+    mNotificationManager.notify(
+            notifyID,
+            mNotifyBuilder.build());
+...
+
+ + +

Loại bỏ thông báo

+

+ Thông báo vẫn hiển thị cho tới khi xảy ra một trong những điều sau: +

+
    +
  • + Người dùng bỏ từng thông báo một hoặc bỏ tất cả bằng cách sử dụng "Xóa Tất cả" (nếu + thông báo có thể xóa được). +
  • +
  • + Người dùng nhấp vào thông báo và bạn đã gọi + {@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()} khi + tạo thông báo. +
  • +
  • + Bạn gọi {@link android.app.NotificationManager#cancel(int) cancel()} cho một ID thông báo + cụ thể. Phương pháp này cũng xóa các thông báo đang diễn ra. +
  • +
  • + Bạn gọi {@link android.app.NotificationManager#cancelAll() cancelAll()}, nó sẽ xóa + tất cả thông báo mà bạn đã phát hành trước đó. +
  • +
+ + +

Giữ lại Điều hướng khi Bắt đầu một Hoạt động

+

+ Khi bạn bắt đầu một {@link android.app.Activity} từ một thông báo, bạn phải giữ lại trải nghiệm điều hướng kỳ vọng + của người dùng. Nhấp vào Quay lại sẽ đưa người dùng quay lại thông qua + tiến trình làm việc bình thường của ứng dụng về màn hình Trang chủ, và nhấp vào Gần đây sẽ hiển thị + {@link android.app.Activity} như một tác vụ riêng. Để giữ lại trải nghiệm điều hướng, bạn + nên bắt đầu {@link android.app.Activity} trong một tác vụ mới. Cách bạn thiết lập + {@link android.app.PendingIntent} để cấp cho bạn một tác vụ mới sẽ phụ thuộc vào tính chất của + {@link android.app.Activity} mà bạn đang bắt đầu. Có hai tình huống thông thường: +

+
+
+ Hoạt động thường xuyên +
+
+ Bạn đang bắt đầu một {@link android.app.Activity} là một phần của tiến trình công việc + bình thường của ứng dụng. Trong tình huống này, hãy thiết lập {@link android.app.PendingIntent} để + bắt đầu một tác vụ mới, và cung cấp {@link android.app.PendingIntent} với một ngăn xếp + có chức năng tái tạo lại hành vi thông thường Quay lại của ứng dụng. +

+ Thông báo từ ứng dụng Gmail thể hiện điều này. Khi bạn nhấp vào một thông báo + cho một thư e-mail đơn lẻ, bạn sẽ thấy chính thư đó. Chạm vào Quay lại sẽ đưa bạn + ngược lại qua Gmail về màn hình Trang chủ, giống như thể bạn đã vào Gmail từ + màn hình Trang chủ chứ không phải vào từ một thông báo. +

+

+ Điều này xảy ra mà không phụ thuộc vào ứng dụng bạn đang ở trong khi chạm vào + thông báo. Ví dụ, nếu bạn đang vào Gmail để soạn thư, và bạn nhấp vào một + thông báo cho một e-mail đơn lẻ, bạn sẽ đến e-mail đó ngay lập tức. Chạm vào Quay lại + sẽ đưa bạn về hộp thư đến rồi tới màn hình Trang chủ, thay vì đưa bạn tới + thư mà bạn đang soạn. +

+
+
+ Hoạt động đặc biệt +
+
+ Người dùng chỉ thấy {@link android.app.Activity} này nếu nó được bắt đầu từ một thông báo. + Nghĩa là, {@link android.app.Activity} mở rộng thông báo bằng cách cung cấp + thông tin mà sẽ khó hiển thị trong chính thông báo đó. Đối với tình huống này, + hãy thiết lập {@link android.app.PendingIntent} để bắt đầu một tác vụ mới. Tuy nhiên, không cần tạo + một ngăn xếp vì {@link android.app.Activity} được bắt đầu không phải là một phần trong + tiến trình hoạt động của ứng dụng. Nhấp vào Quay lại sẽ vẫn đưa người dùng đến + màn hình Trang chủ. +
+
+ +

Thiết đặt một PendingIntent cho hoạt động thường xuyên

+

+ Để thiết lập {@link android.app.PendingIntent} để bắt đầu một mục nhập trực tiếp + {@link android.app.Activity}, hãy làm theo những bước sau: +

+
    +
  1. + Định nghĩa phân cấp {@link android.app.Activity} của ứng dụng của bạn trong bản kê khai. +
      +
    1. + Thêm hỗ trợ cho phiên bản Android 4.0.3 và trước đó. Để làm điều này, hãy quy định mẹ của + {@link android.app.Activity} mà bạn đang bắt đầu bằng cách thêm phần tử +<meta-data> + làm con của +<activity>. +

      + Đối với phần tử này, hãy đặt +android:name="android.support.PARENT_ACTIVITY". + Đặt +android:value="<parent_activity_name>" + trong đó <parent_activity_name> là giá trị của +android:name + đối với phần tử +<activity> + mẹ. Xem ví dụ trong XML sau. +

      +
    2. +
    3. + Cũng thêm hỗ trợ cho phiên bản Android 4.1 và sau đó. Để làm điều này, hãy thêm thuộc tính +android:parentActivityName + vào phần tử +<activity> + của {@link android.app.Activity} mà bạn đang bắt đầu. +
    4. +
    +

    + XML cuối cùng sẽ trông như sau: +

    +
    +<activity
    +    android:name=".MainActivity"
    +    android:label="@string/app_name" >
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +<activity
    +    android:name=".ResultActivity"
    +    android:parentActivityName=".MainActivity">
    +    <meta-data
    +        android:name="android.support.PARENT_ACTIVITY"
    +        android:value=".MainActivity"/>
    +</activity>
    +
    +
  2. +
  3. + Tạo một ngăn xếp dựa trên {@link android.content.Intent} mà bắt đầu + {@link android.app.Activity}: +
      +
    1. + Tạo {@link android.content.Intent} để bắt đầu {@link android.app.Activity}. +
    2. +
    3. + Tạo một bộ dựng chồng bằng cách gọi {@link android.app.TaskStackBuilder#create + TaskStackBuilder.create()}. +
    4. +
    5. + Thêm ngăn xếp vào bộ dựng ngăn xếp bằng cách gọi + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}. + Đối với mỗi {@link android.app.Activity} trong phân cấp mà bạn đã định nghĩa trong + bản kê khai, ngăn xếp chứa một đối tượng {@link android.content.Intent} mà + sẽ khởi động {@link android.app.Activity}. Phương pháp này cũng thêm cờ có chức năng bắt đầu + chồng trong một tác vụ mới. +

      + Lưu ý: Mặc dù tham đối đến + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} + là một tham chiếu tới {@link android.app.Activity} được bắt đầu, phương pháp gọi + không thêm {@link android.content.Intent} có chức năng bắt đầu + {@link android.app.Activity}. Thay vào đó, nó được xử lý ở bước tiếp theo. +

      +
    6. +
    7. + Thêm {@link android.content.Intent} có chức năng bắt đầu {@link android.app.Activity} + từ thông báo bằng cách gọi + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. + Chuyển {@link android.content.Intent} mà bạn đã tạo ở bước đầu tiên làm + tham đối tới + {@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()}. +
    8. +
    9. + Nếu bạn cần, hãy thêm các tham đối tới các đối tượng {@link android.content.Intent} trên chồng + bằng cách gọi {@link android.support.v4.app.TaskStackBuilder#editIntentAt + TaskStackBuilder.editIntentAt()}. Đôi khi cần phải đảm bảo rằng + {@link android.app.Activity} mục tiêu sẽ hiển thị dữ liệu có ý nghĩa khi người dùng điều hướng + tới nó bằng cách sử dụng Quay lại. +
    10. +
    11. + Nhận một {@link android.app.PendingIntent} cho ngăn xếp này bằng cách gọi + {@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()}. + Sau đó, bạn có thể sử dụng {@link android.app.PendingIntent} này làm tham đối tới + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
    12. +
    +
  4. +
+

+ Đoạn mã HTML sau minh họa tiến trình: +

+
+...
+Intent resultIntent = new Intent(this, ResultActivity.class);
+TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+// Adds the back stack
+stackBuilder.addParentStack(ResultActivity.class);
+// Adds the Intent to the top of the stack
+stackBuilder.addNextIntent(resultIntent);
+// Gets a PendingIntent containing the entire back stack
+PendingIntent resultPendingIntent =
+        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
+...
+NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+builder.setContentIntent(resultPendingIntent);
+NotificationManager mNotificationManager =
+    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+mNotificationManager.notify(id, builder.build());
+
+ +

Thiết đặt một PendingIntent cho hoạt động đặc biệt

+

+ Phần sau mô tả cách thiết lập một + {@link android.app.PendingIntent} cho hoạt động đặc biệt. +

+

+ Một {@link android.app.Activity} đặc biệt không cần ngăn xếp, vì thế bạn không phải + định nghĩa phân cấp {@link android.app.Activity} của nó trong bản kê khai, và bạn không phải + gọi + {@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} để xây dựng một + ngăn xếp. Thay vào đó, hãy sử dụng bản kê khai để thiết lập các tùy chọn tác vụ {@link android.app.Activity}, + và tạo {@link android.app.PendingIntent} bằng cách gọi + {@link android.app.PendingIntent#getActivity getActivity()}: +

+
    +
  1. + Trong bản kê khai của mình, hãy thêm các thuộc tính sau vào phần tử +<activity> + cho {@link android.app.Activity} +
    +
    +android:name="activityclass" +
    +
    + Tên lớp được xác định đầy đủ của hoạt động. +
    +
    +android:taskAffinity="" +
    +
    + Kết hợp với cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + mà bạn đặt trong mã, điều này đảm bảo rằng {@link android.app.Activity} này không + đi đến tác vụ mặc định của ứng dụng. Bất kỳ tác vụ hiện tại nào mà có + bố trí mặc định của ứng dụng đều không bị ảnh hưởng. +
    +
    +android:excludeFromRecents="true" +
    +
    + Loại bỏ tác vụ mới khỏi Gần đây, sao cho người dùng không thể vô tình + điều hướng quay lại nó. +
    +
    +

    + Đoạn mã HTML này thể hiện phần tử: +

    +
    +<activity
    +    android:name=".ResultActivity"
    +...
    +    android:launchMode="singleTask"
    +    android:taskAffinity=""
    +    android:excludeFromRecents="true">
    +</activity>
    +...
    +
    +
  2. +
  3. + Xây dựng và phát hành thông báo: +
      +
    1. + Tạo một {@link android.content.Intent} có chức năng bắt đầu + {@link android.app.Activity}. +
    2. +
    3. + Đặt {@link android.app.Activity} để bắt đầu trong một tác vụ mới, trống bằng cách gọi + {@link android.content.Intent#setFlags setFlags()} với các cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} + và + {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK}. +
    4. +
    5. + Đặt bất kỳ tùy chọn nào khác mà bạn cần cho {@link android.content.Intent}. +
    6. +
    7. + Tạo một {@link android.app.PendingIntent} từ {@link android.content.Intent} + bằng cách gọi {@link android.app.PendingIntent#getActivity getActivity()}. + Sau đó, bạn có thể sử dụng {@link android.app.PendingIntent} này làm tham đối tới + {@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()}. +
    8. +
    +

    + Đoạn mã HTML sau minh họa tiến trình: +

    +
    +// Instantiate a Builder object.
    +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    +// Creates an Intent for the Activity
    +Intent notifyIntent =
    +        new Intent(this, ResultActivity.class);
    +// Sets the Activity to start in a new, empty task
    +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    +                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    +// Creates the PendingIntent
    +PendingIntent notifyPendingIntent =
    +        PendingIntent.getActivity(
    +        this,
    +        0,
    +        notifyIntent,
    +        PendingIntent.FLAG_UPDATE_CURRENT
    +);
    +
    +// Puts the PendingIntent into the notification builder
    +builder.setContentIntent(notifyPendingIntent);
    +// Notifications are issued by sending them to the
    +// NotificationManager system service.
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// Builds an anonymous Notification object from the builder, and
    +// passes it to the NotificationManager
    +mNotificationManager.notify(id, builder.build());
    +
    +
  4. +
+ + +

Hiển thị Tiến độ trong một Thông báo

+

+ Thông báo có thể bao gồm một chỉ báo tiến độ dạng hoạt ảnh để cho người dùng thấy trạng thái + của một thao tác đang diễn ra. Nếu bạn có thể ước lượng thao tác mất bao lâu và nó được + được hoàn thành bao nhiêu vào bất cứ lúc nào, hãy sử dụng hình thức "xác định" của chỉ báo + (thanh tiến độ). Nếu bạn không thể ước lượng thời lượng của thao tác, hãy sử dụng hình thức + "không xác định" của chỉ báo (chỉ báo hoạt động). +

+

+ Chỉ báo tiến độ được hiển thị bằng triển khai lớp + {@link android.widget.ProgressBar} của nền tảng. +

+

+ Để sử dụng chỉ báo tiến độ trên các nền tảng bắt đầu từ Android 4.0, hãy gọi + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. Đối + với các phiên bản trước đây, bạn phải tạo bố trí thông báo tùy chỉnh của chính mình + trong đó chứa một dạng xem {@link android.widget.ProgressBar}. +

+

+ Phần sau đây mô tả cách hiển thị tiến độ trong một thông báo bằng cách sử dụng + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}. +

+ +

Hiển thị một chỉ báo tiến độ thời lượng cố định

+

+ Để hiển thị một thanh tiến độ xác định, hãy thêm thanh vào thông báo của bạn bằng cách gọi + {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(max, progress, false)} rồi phát hành thông báo. Khi thông báo của bạn tiến hành, + tăng dầnprogress, và cập nhật thông báo. Khi kết thúc thao tác, + progress sẽ bằng max. Một cách thông thường để gọi + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} + đó là đặt max thành 100 rồi tăng dần progress dưới dạng giá trị + "phần trăm hoàn thành" cho thao tác. +

+

+ Bạn có thể hoặc để thanh tiến độ hiển thị khi nào thì thao tác hoàn thành, hoặc loại bỏ nó. Dù + trong trường hợp nào hãy nhớ cập nhật văn bản thông báo để hiển thị thao tác hoàn tất. + Để loại bỏ thanh tiến độ, hãy gọi + {@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(0, 0, false)}. Ví dụ: +

+
+...
+mNotifyManager =
+        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+mBuilder = new NotificationCompat.Builder(this);
+mBuilder.setContentTitle("Picture Download")
+    .setContentText("Download in progress")
+    .setSmallIcon(R.drawable.ic_notification);
+// Start a lengthy operation in a background thread
+new Thread(
+    new Runnable() {
+        @Override
+        public void run() {
+            int incr;
+            // Do the "lengthy" operation 20 times
+            for (incr = 0; incr <= 100; incr+=5) {
+                    // Sets the progress indicator to a max value, the
+                    // current completion percentage, and "determinate"
+                    // state
+                    mBuilder.setProgress(100, incr, false);
+                    // Displays the progress bar for the first time.
+                    mNotifyManager.notify(0, mBuilder.build());
+                        // Sleeps the thread, simulating an operation
+                        // that takes time
+                        try {
+                            // Sleep for 5 seconds
+                            Thread.sleep(5*1000);
+                        } catch (InterruptedException e) {
+                            Log.d(TAG, "sleep failure");
+                        }
+            }
+            // When the loop is finished, updates the notification
+            mBuilder.setContentText("Download complete")
+            // Removes the progress bar
+                    .setProgress(0,0,false);
+            mNotifyManager.notify(ID, mBuilder.build());
+        }
+    }
+// Starts the thread by calling the run() method in its Runnable
+).start();
+
+ + +

Hiển thị một chỉ báo hoạt động liên tục

+

+ Để hiển thị một chỉ báo hoạt động không xác định, hãy thêm nó vào thông báo của bạn bằng + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)} + (hai tham đối đầu tiên bị bỏ qua), và phát hành thông báo. Kết quả là một chỉ báo + mà có cùng kiểu như một thanh tiến độ, khác ở chỗ hoạt ảnh của nó đang diễn ra. +

+

+ Phát hành thông báo khi bắt đầu thao tác. Hoạt ảnh sẽ chạy tới khi bạn + sửa đổi thông báo của mình. Khi thao tác hoàn thành, hãy gọi + {@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)} + và rồi cập nhật thông báo để loại bỏ chỉ báo hoạt động. + Luôn làm điều này; nếu không, hoạt ảnh sẽ chạy ngay cả khi thao tác đã hoàn thành. Đồng thời + hãy nhớ thay đổi văn bản thông báo để biểu thị thao tác hoàn tất. +

+

+ Để xem chỉ báo hoạt động vận hành như thế nào, hãy tham khảo đoạn mã HTML trước. Định vị các dòng sau: +

+
+// Sets the progress indicator to a max value, the current completion
+// percentage, and "determinate" state
+mBuilder.setProgress(100, incr, false);
+// Issues the notification
+mNotifyManager.notify(0, mBuilder.build());
+
+

+ Thay thế các dòng bạn đã tìm thấy bằng các dòng sau: +

+
+ // Sets an activity indicator for an operation of indeterminate length
+mBuilder.setProgress(0, 0, true);
+// Issues the notification
+mNotifyManager.notify(0, mBuilder.build());
+
+ +

Siêu dữ liệu Thông báo

+ +

Thông báo có thể được sắp xếp theo siêu dữ liệu mà bạn gán cho bằng các +phương pháp {@link android.support.v4.app.NotificationCompat.Builder} sau:

+ +
    +
  • {@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} + thông báo hệ thống cách xử lý thông báo ứng dụng của bạn khi thiết bị đang trong chế độ Ưu tiên + (ví dụ, nếu thông báo của bạn biểu diễn một cuộc gọi đến, tin nhắn tức thời hoặc báo thức).
  • +
  • {@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} khiến + thông báo với trường mức ưu tiên được đặt thành {@code PRIORITY_MAX} hoặc {@code PRIORITY_HIGH} xuất hiện + trong một cửa sổ nổi nhỏ nếu thông báo cũng có âm thanh hoặc rung.
  • +
  • {@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} + cho phép bạn thêm một danh sách người vào thông báo. Ứng dụng của bạn có thể sử dụng tín hiệu này cho + hệ thống mà nó sẽ nhóm cùng với thông báo từ những người được quy định, hoặc xếp hạng thông báo + từ những người này là quan trọng hơn.
  • +
+ +
+ +

+ Hình 3. Hoạt động toàn màn hình thể hiện một thông báo cảnh báo +

+
+ +

Thông báo Cảnh báo

+ +

Với Android 5.0 (API mức 21), thông báo có thể xuất hiện trong một cửa sổ nổi nhỏ +(còn gọi là thông báo cảnh báo) khi thiết bị hiện hoạt +(tức là thiết bị được mở khóa và màn hình của nó đang bật). Những thông báo +này có vẻ tương tự như dạng rút gọn thông báo của bạn, chỉ khác là +thông báo cảnh báo cũng hiển thị các nút hành động. Người dùng có thể hành động trên đó, hoặc bỏ, +một thông báo cảnh báo mà không phải rời khỏi ứng dụng hiện tại.

+ +

Các ví dụ về điều kiện có thể kích khởi thông báo cảnh báo bao gồm:

+ +
    +
  • Hoạt động của người dùng đang trong chế độ toàn màn hình (ứng dụng sử dụng +{@link android.app.Notification#fullScreenIntent}), hoặc
  • +
  • Thông báo có mức ưu tiên cao và sử dụng nhạc chuông hoặc + rung
  • +
+ +

Thông báo Màn hình Khóa

+ +

Với việc phát hành Android 5.0 (API mức 21), giờ đây thông báo có thể xuất hiện trên màn hình +khóa. Ứng dụng của bạn có thể sử dụng tính năng này để cung cấp các chức năng điều khiển phát lại phương tiện và các hành động +thông dụng khác. Người dùng có thể chọn thông qua Cài đặt để xem có hiển thị thông báo trên màn hình khóa không, và +bạn có thể chỉ định xem thông báo từ ứng dụng của mình có hiển thị trên màn hình khóa không.

+ +

Thiết đặt Khả năng Hiển thị

+ +

Ứng dụng của bạn có thể điều khiển mức chi tiết có thể nhìn thấy được trong thông báo được hiển thị trên một +màn hình khóa bảo mật. Bạn gọi {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()} +và quy định một trong những giá trị sau:

+ +
    +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC} hiển thị đầy đủ nội dung của + thông báo.
  • +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET} không hiển thị bất kỳ phần nào của + thông báo này trên màn hình khóa.
  • +
  • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} hiển thị thông tin cơ bản, + chẳng hạn như biểu tượng và tiêu đề nội dung của thông báo, nhưng ẩn nội dung đầy đủ của thông báo.
  • +
+ +

Khi {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} được đặt, bạn cũng có thể +cung cấp một phiên bản thay thế cho nội dung thông báo, trong đó ẩn một số chi tiết nhất định. Ví dụ, +một ứng dụng SMS có thể hiển thị một thông báo hiển thị Bạn có 3 tin nhắn văn bản mới, nhưng ẩn +nội dung của tin nhắn và người gửi. Để cung cấp thông báo thay thế này, trước tiên hãy tạo thông báo +thay thế bằng cách sử dụng {@link android.support.v4.app.NotificationCompat.Builder}. Khi bạn tạo +đối tượng thông báo riêng tư, hãy đính kèm thông báo thay thế cho nó thông qua phương pháp +{@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} +.

+ +

Điều khiển Phát lại Phương tiện trên Màn hình Khóa

+ +

Trong Android 5.0 (API mức 21) màn hình khóa không còn hiển thị điều khiển phương tiện +dựa trên {@link android.media.RemoteControlClient}, điều mà nay đã bị bỏ đi. Thay vào đó, hãy sử dụng mẫu +{@link android.app.Notification.MediaStyle} với phương pháp +{@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +, có chức năng chuyển hành động thành các biểu tượng có thể nhấp.

+ +

Lưu ý: Mẫu và phương pháp {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +không nằm trong thư viện hỗ trợ, vì thế những tính năng này chỉ chạy trong phiên bản Android 5.0 trở lên +.

+ +

Để hiển thị điều khiển phát lại phương tiện trên màn hình khóa trong Android 5.0, hãy đặt mức độ nhìn thấy +thành {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC}, như mô tả bên trên. Sau đó thêm +các hành động và đặt mẫu {@link android.app.Notification.MediaStyle}, như được mô tả trong +đoạn mã mẫu sau:

+ +
+Notification notification = new Notification.Builder(context)
+    // Show controls on lock screen even when user hides sensitive content.
+    .setVisibility(Notification.VISIBILITY_PUBLIC)
+    .setSmallIcon(R.drawable.ic_stat_player)
+    // Add media control buttons that invoke intents in your media service
+    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
+    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
+    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
+    // Apply the media style template
+    .setStyle(new Notification.MediaStyle()
+    .setShowActionsInCompactView(1 /* #1: pause button */)
+    .setMediaSession(mMediaSession.getSessionToken())
+    .setContentTitle("Wonderful music")
+    .setContentText("My Awesome Band")
+    .setLargeIcon(albumArtBitmap)
+    .build();
+
+ +

Lưu ý: Việc rút bỏ {@link android.media.RemoteControlClient} +còn có nhiều ý nghĩa khác đối với việc điều khiển phương tiện. Xem phần +Điều khiển Phát lại Phương tiện +để biết thêm thông tin về các API mới đối với quản lý phiên phương tiện và điều khiển phát lại này.

+ + + +

Bố trí Thông báo Tùy chỉnh

+

+ Khuôn khổ thông báo cho phép bạn định nghĩa một bố trí thông báo tùy chỉnh, nó + định nghĩa hình thức của thông báo trong một đối tượng {@link android.widget.RemoteViews}. + Thông báo có bố trí tùy chỉnh tương tự như thông báo thường, nhưng chúng được dựa trên + {@link android.widget.RemoteViews} được định nghĩa trong một tệp bố trí XML. +

+

+ Chiều cao sẵn có cho bố trí thông báo tùy chỉnh phụ thuộc vào dạng xem thông báo. Bố trí dạng xem bình thường + được giới hạn ở 64 dp, và bố trí dạng xem mở rộng được giới hạn ở 256 dp. +

+

+ Để định nghĩa một bố trí thông báo tùy chỉnh, hãy bắt đầu bằng việc khởi tạo đối tượng + {@link android.widget.RemoteViews} để bung một tệp bố trí XML. Sau đó, + thay vì gọi các phương pháp như + {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()}, + hãy gọi {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Để đặt + chi tiết nội dung trong thông báo tùy chỉnh, hãy sử dụng các phương pháp + {@link android.widget.RemoteViews} để đặt giá trị của các tập con của dạng xem: +

+
    +
  1. + Tạo một bố trí XML cho thông báo trong một tệp riêng. Bạn có thể sử dụng bất kỳ tên tệp nào + mà bạn muốn, nhưng phải sử dụng phần mở rộng .xml +
  2. +
  3. + Trong ứng dụng của bạn, hãy sử dụng các phương pháp {@link android.widget.RemoteViews} để định nghĩa các biểu tượng và văn bản + của thông báo của bạn. Đặt đối tượng {@link android.widget.RemoteViews} này vào + {@link android.support.v4.app.NotificationCompat.Builder} của bạn bằng cách gọi + {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()}. Tránh + đặt một {@link android.graphics.drawable.Drawable} nền trên đối tượng + {@link android.widget.RemoteViews} của bạn, vì màu văn bản của bạn có thể không đọc được. +
  4. +
+

+ Lớp {@link android.widget.RemoteViews} cũng bao gồm các phương pháp mà bạn có thể sử dụng để dễ dạng + thêm một {@link android.widget.Chronometer} hoặc {@link android.widget.ProgressBar} + vào bố trí thông báo của bạn. Để biết thêm thông tin về việc tạo bố trí tùy chỉnh cho thông báo + của mình, hãy tham khảo tài liệu tham khảo {@link android.widget.RemoteViews}. +

+

+ Chú ý: Khi bạn sử dụng một bố trí thông báo tùy chỉnh, hãy đặc biệt cẩn thận + để đảm bảo rằng bố trí tùy chỉnh của bạn có tác dụng với các hướng và độ phân giải thiết bị khác nhau. Trong khi + lời khuyên này áp dụng cho tất cả bố trí Dạng xem, nó đặc biệt quan trọng đối với thông báo + vì khoảng trống trong ngăn kéo thông báo rất hạn chế. Không được tạo bố trí tùy chỉnh của bạn quá + phức tạp và đảm bảo kiểm tra nó trong các cấu hình khác nhau. +

+ +

Sử dụng các tài nguyên kiểu cho văn bản thông báo tùy chỉnh

+

+ Luôn sử dụng tài nguyên kiểu cho văn bản của một thông báo tùy chỉnh. Màu nền của thông báo + có thể thay đổi giữa các thiết bị và phiên bản khác nhau, và việc sử dụng tài nguyên kiểu + sẽ giúp bạn khắc phục điều này. Bắt đầu từ Android 2.3, hệ thống đã định nghĩa kiểu cho + văn bản bố trí thông báo chuẩn. Nếu bạn sử dụng cùng kiểu trong các ứng dụng nhắm đến phiên bản Android + 2.3 hoặc cao hơn, bạn sẽ phải đảm bảo rằng văn bản của bạn nhìn thấy được trên nền hiển thị. +

diff --git a/docs/html-intl/intl/vi/guide/topics/ui/overview.jd b/docs/html-intl/intl/vi/guide/topics/ui/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..7bd45527c7b4b494d23294f69bf73ca7f7c08b6f --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=Tổng quan về UI +@jd:body + + +

Tất cả phần tử giao diện người dùng trong một ứng dụng Android đều được xây dựng bằng cách sử dụng các đối tượng {@link android.view.View} và +{@link android.view.ViewGroup}. {@link android.view.View} là một đối tượng có chức năng vẽ +thứ gì đó trên màn hình mà người dùng có thể tương tác với. {@link android.view.ViewGroup} là một đối tượng +có chức năng giữ các đối tượng {@link android.view.View} (và {@link android.view.ViewGroup}) khác +để định nghĩa bố trí của giao diện.

+ +

Android cung cấp một bộ sưu tập cả lớp con {@link android.view.View} và {@link +android.view.ViewGroup} cung cấp cho bạn các cách điều khiển nhập liệu thông dụng (chẳng hạn như nút và +trường văn bản) và các mô hình bố trí khác nhau (chẳng hạn như bố trí tuyến tính hoặc tương đối).

+ + +

Bố trí Giao diện Người dùng

+ +

Giao diện người dùng của từng thành phần trong ứng dụng của bạn được định nghĩa bằng cách sử dụng một phân cấp của các đối tượng {@link +android.view.View} và {@link android.view.ViewGroup} như minh họa trong hình 1. Mỗi nhóm dạng xem +là một bộ chứa vô hình có chức năng tổ chức các dạng xem con, trong khi dạng xem con có thể là +điều khiển nhập liệu hoặc các widget khác để +vẽ một phần nào đó của UI. Cây phân cấp này có thể đơn giản hoặc phức tạp như nhu cầu +của bạn (nhưng đơn giản sẽ tốt cho hiệu năng).

+ + +

Hình 1. Minh họa một phân cấp dạng xem có chức năng định nghĩa một +bố trí UI.

+ +

Để khai báo bố trí của mình, bạn có thể khởi tạo các đối tượng {@link android.view.View} trong mã và bắt đầu +xây dựng một cây, nhưng cách dễ nhất và hiệu quả nhất để định nghĩa bố trí của bạn đó là bằng một tệp XML. +XML cung cấp một cấu trúc mà người dùng có thể đọc được cho bố trí, tương tự như HTML.

+ +

Tên của phần tử XML đối với một dạng xem tương ứng với lớp Android mà nó biểu diễn. Vì thế một phần tử +<TextView> tạo ra một widget {@link android.widget.TextView} trong UI của bạn, +và một phần tử <LinearLayout> tạo ra một nhóm dạng xem {@link android.widget.LinearLayout} +.

+ +

Ví dụ, một bố trí thẳng đứng đơn giản với dạng xem văn bản và nút trông như sau:

+
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent" 
+              android:layout_height="fill_parent"
+              android:orientation="vertical" >
+    <TextView android:id="@+id/text"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:text="I am a TextView" />
+    <Button android:id="@+id/button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="I am a Button" />
+</LinearLayout>
+
+ +

Khi bạn tải một tài nguyên bố trí trong ứng dụng của mình, Android khởi tạo từng nút của bố trí vào một +đối tượng thời gian chạy mà bạn có thể sử dụng để định nghĩa các hành vi bổ sung, truy vấn trạng thái của đối tượng hoặc sửa đổi +bố trí.

+ +

Để xem hướng dẫn đầy đủ về tạo bố trí UI, hãy xem phần Bố trí +XML. + + +

Thành phần Giao diện Người dùng

+ +

Bạn không phải xây dựng tất cả UI của mình bằng cách sử dụng các đối tượng {@link android.view.View} và {@link +android.view.ViewGroup}. Android cung cấp một vài thành phần ứng dụng đưa ra một +bố trí UI chuẩn mà bạn chỉ cần định nghĩa nội dung cho nó. Mỗi thành phần UI như vậy +đều có một bộ API duy nhất được mô tả trong các tài liệu tương ứng, chẳng hạn như Thanh Hành động, Hộp thoạiThông báo Trạng thái.

+ + diff --git a/docs/html-intl/intl/vi/guide/topics/ui/settings.jd b/docs/html-intl/intl/vi/guide/topics/ui/settings.jd new file mode 100644 index 0000000000000000000000000000000000000000..8e19b979d0ac180451c42f100701391fc870cdc5 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=Thiết đặt +page.tags=preference,preferenceactivity,preferencefragment + +@jd:body + + + + + + + +

Ứng dụng thường bao gồm những thiết đặt cho phép người dùng sửa đổi các tính năng và hành vi của ứng dụng. Ví +dụ, một số ứng dụng cho phép người dùng quy định xem thông báo có được kích hoạt hay không hoặc quy định tần suất +ứng dụng sẽ đồng bộ dữ liệu với đám mây.

+ +

Nếu muốn cung cấp thiết đặt cho ứng dụng của mình, bạn nên sử dụng +các API {@link android.preference.Preference} của Android để xây dựng một giao diện phù hợp với +trải nghiệm người dùng trong các ứng dụng Android khác (bao gồm thiết đặt hệ thống). Tài liệu này mô tả +cách xây dựng thiết đặt ứng dụng của bạn bằng cách sử dụng các API {@link android.preference.Preference}.

+ +
+

Thiết kế Thiết đặt

+

Để biết thông tin về cách thiết kế thiết đặt của bạn, hãy đọc hướng dẫn thiết kế Thiết đặt.

+
+ + + +

Hình 1. Ảnh chụp màn hình từ thiết đặt của ứng dụng +Messaging trên Android. Chọn một mục được định nghĩa bởi một {@link android.preference.Preference} +sẽ mở ra một giao diện để thay đổi thiết đặt.

+ + + + +

Tổng quan

+ +

Thay vì sử dụng các đối tượng {@link android.view.View} để xây dựng giao diện người dùng, thiết đặt được +xây dựng bằng cách sử dụng các lớp con khác nhau của lớp {@link android.preference.Preference} mà bạn +khai báo trong một tệp XML.

+ +

Đối tượng {@link android.preference.Preference} là một khối dựng cho một thiết đặt +đơn lẻ. Mỗi {@link android.preference.Preference} xuất hiện như một mục trong một danh sách và cung cấp +UI phù hợp để người dùng sửa đổi thiết đặt. Ví dụ, một {@link +android.preference.CheckBoxPreference} tạo một mục danh sách hiển thị một hộp kiểm, và một {@link +android.preference.ListPreference} tạo một mục mở ra một hộp thoại với danh sách lựa chọn.

+ +

Mỗi {@link android.preference.Preference} mà bạn thêm có một cặp khóa-giá trị tương ứng mà +hệ thống sử dụng để lưu thiết đặt trong một tệp {@link android.content.SharedPreferences} +mặc định cho thiết đặt của ứng dụng của bạn. Khi người dùng thay đổi một thiết đặt, hệ thống sẽ cập nhật giá trị +tương ứng trong tệp {@link android.content.SharedPreferences} cho bạn. Lần duy nhất mà bạn nên +trực tiếp tương tác với tệp {@link android.content.SharedPreferences} được liên kết đó là khi bạn +cần đọc giá trị để xác định xem hành vi ứng dụng của mình có được dựa trên thiết đặt của người dùng không.

+ +

Giá trị được lưu trong {@link android.content.SharedPreferences} cho từng thiết đặt có thể là một trong các kiểu dữ liệu +sau:

+ +
    +
  • Boolean
  • +
  • Float
  • +
  • Int
  • +
  • Long
  • +
  • String
  • +
  • String {@link java.util.Set}
  • +
+ +

Vì thiết đặt của ứng dụng của bạn được xây dựng bằng cách sử dụng các đối tượng {@link android.preference.Preference} +thay vì đối tượng +{@link android.view.View}, bạn nên sử dụng một lớp con {@link android.app.Activity} hoặc +{@link android.app.Fragment} chuyên dụng để hiển thị thiết đặt danh sách:

+ +
    +
  • Nếu ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0 (API mức 10 và thấp hơn), bạn phải +xây dựng hoạt động như một phần mở rộng của lớp {@link android.preference.PreferenceActivity}.
  • +
  • Trên phiên bản Android 3.0 trở lên, thay vào đó, bạn nên sử dụng một {@link android.app.Activity} +truyền thống nơi lưu giữ {@link android.preference.PreferenceFragment} để hiển thị thiết đặt ứng dụng của bạn. +Tuy nhiên, bạn cũng có thể sử dụng {@link android.preference.PreferenceActivity} để tạo một bố trí hai bảng +cho màn hình lớn khi bạn có nhiều nhóm thiết đặt.
  • +
+ +

Cách thiết đặt {@link android.preference.PreferenceActivity} của bạn và các thực thể của {@link +android.preference.PreferenceFragment} được trình bày trong các phần về Tạo một Hoạt động Tùy chọnSử dụng +Phân đoạn Tùy chọn.

+ + +

Tùy chọn

+ +

Mọi thiết đặt cho ứng dụng của bạn đều được biểu diễn bởi một lớp con cụ thể của lớp {@link +android.preference.Preference}. Mỗi lớp con lại bao gồm một tập hợp các tính chất cốt lõi cho phép bạn +quy định những thứ như tiêu đề cho thiết đặt và giá trị mặc định. Mỗi lớp con cũng cung cấp +các tính chất và giao diện người dùng chuyên dụng của chính nó. Ví dụ, hình 1 mình họa một ảnh chụp màn hình từ thiết đặt của ứng dụng +Messaging. Mỗi mục danh sách trong màn hình thiết đặt được hỗ trợ bởi một đối tượng {@link +android.preference.Preference} khác nhau.

+ +

Sau đây là một số tùy chọn phổ biến nhất:

+ +
+
{@link android.preference.CheckBoxPreference}
+
Hiển thị một mục kèm một hộp kiểm cho thiết đặt hoặc được kích hoạt hoặc bị vô hiệu hóa. Giá trị +được lưu là một boolean (true nếu nó được chọn).
+ +
{@link android.preference.ListPreference}
+
Mở một hộp thoại kèm danh sách nút chọn một. Giá trị được lưu +có thể là bất kỳ loại giá trị được hỗ trợ nào (liệt kê bên trên).
+ +
{@link android.preference.EditTextPreference}
+
Mở một hộp thoại kèm một widget {@link android.widget.EditText}. Giá trị được lưu là một {@link +java.lang.String}.
+
+ +

Xem lớp {@link android.preference.Preference} để biết danh sách tất cả các lớp con khác và tính chất +tương ứng của chúng.

+ +

Dĩ nhiên, các lớp tích hợp không đáp ứng mọi nhu cầu và ứng dụng của bạn có thể yêu cầu +lớp con chuyên dụng hơn. Ví dụ, nền tảng này hiện chưa cung cấp một lớp {@link +android.preference.Preference} cho việc chọn một số hay ngày. Vì thế, bạn có thể cần phải định nghĩa +lớp con {@link android.preference.Preference} của chính mình. Để được trợ giúp khi làm vậy, hãy xem phần về Xây dựng Thiết đặt Tùy chỉnh.

+ + + +

Định nghĩa Tùy chọn trong XML

+ +

Mặc dù bạn có thể khởi tạo các đối tượng {@link android.preference.Preference} mới vào thời gian chạy, bạn +nên định nghĩa danh sách các thiết đặt của mình trong XML kèm một phân cấp của các đối tượng {@link android.preference.Preference} +. Việc sử dụng một tệp XML để định nghĩa bộ sưu tập thiết đặt của bạn sẽ được ưu tiên vì tệp +cung cấp một cấu trúc dễ đọc, cập nhật đơn giản. Bên cạnh đó, các thiết đặt ứng dụng của bạn thường được +xác định trước, mặc dù bạn vẫn có thể sửa đổi bộ sưu tập vào thời gian chạy.

+ +

Mỗi lớp con {@link android.preference.Preference} có thể được khai báo bằng một phần tử XML mà +khớp với tên lớp đó, chẳng hạn như {@code <CheckBoxPreference>}.

+ +

Bạn phải lưu tệp XML trong thư mục {@code res/xml/}. Mặc dù bạn có thể đặt tên tệp là +bất cứ thứ gì mình muốn, nó thường được đặt tên là{@code preferences.xml}. Bạn thường chỉ cần một tệp, +bởi các nhánh trong phân cấp (mà mở danh sách thiết đặt của riêng chúng) sẽ được khai báo bằng cách sử dụng các thực thể +lồng nhau của {@link android.preference.PreferenceScreen}.

+ +

Lưu ý: Nếu bạn muốn tạo một bố trí đa bảng cho thiết đặt +của mình, vậy bạn nên tách riêng các tệp XML cho từng phân đoạn.

+ +

Node gốc cho tệp XML phải là một phần tử {@link android.preference.PreferenceScreen +<PreferenceScreen>}. Trong phần tử này là nơi bạn thêm từng {@link +android.preference.Preference}. Từng phần tử con mà bạn thêm vào trong phần tử +{@link android.preference.PreferenceScreen <PreferenceScreen>} sẽ xuất hiện như một mục +đơn lẻ trong danh sách thiết đặt.

+ +

Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <CheckBoxPreference
+        android:key="pref_sync"
+        android:title="@string/pref_sync"
+        android:summary="@string/pref_sync_summ"
+        android:defaultValue="true" />
+    <ListPreference
+        android:dependency="pref_sync"
+        android:key="pref_syncConnectionType"
+        android:title="@string/pref_syncConnectionType"
+        android:dialogTitle="@string/pref_syncConnectionType"
+        android:entries="@array/pref_syncConnectionTypes_entries"
+        android:entryValues="@array/pref_syncConnectionTypes_values"
+        android:defaultValue="@string/pref_syncConnectionTypes_default" />
+</PreferenceScreen>
+
+ +

Trong ví dụ này, có một {@link android.preference.CheckBoxPreference} và một {@link +android.preference.ListPreference}. Cả hai mục đều bao gồm ba thuộc tính sau:

+ +
+
{@code android:key}
+
Thuộc tính này được yêu cầu cho các tùy chọn duy trì một giá trị dữ liệu. Nó quy định khóa +(xâu) duy nhất mà hệ thống sử dụng khi lưu giá trị của thiết đặt này trong {@link +android.content.SharedPreferences}. +

Các thực thể duy nhất mà thuộc tính này không được yêu cầu là khi tùy chọn là một +{@link android.preference.PreferenceCategory} hoặc {@link android.preference.PreferenceScreen}, hoặc +tùy chọn quy định một {@link android.content.Intent} để gọi ra (bằng phần tử {@code <intent>}) hoặc {@link android.app.Fragment} để hiển thị (bằng thuộc tính {@code +android:fragment}).

+
+
{@code android:title}
+
Thuộc tính này cung cấp một tên hiển thị với người dùng cho thiết đặt.
+
{@code android:defaultValue}
+
Nó quy định giá trị ban đầu mà hệ thống nên đặt trong tệp {@link +android.content.SharedPreferences}. Bạn nên cung cấp một giá trị mặc định cho tất cả +thiết đặt.
+
+ +

Để biết thông tin về tất cả thuộc tính được hỗ trợ khác, hãy xem tài liệu {@link +android.preference.Preference} (và lớp con tương ứng).

+ + +
+ +

Hình 2. Thiết đặt thể loại + có tiêu đề.
1. Thể loại được quy định bởi phần tử {@link +android.preference.PreferenceCategory <PreferenceCategory>}.
2. Tiêu đề +được quy định bằng thuộc tính {@code android:title}.

+
+ + +

Khi danh sách thiết đặt của bạn vượt quá khoảng 10 mục, bạn có thể muốn thêm tiêu đề để +định nghĩa các nhóm thiết đặt hoặc hiển thị các nhóm đó trong một +màn hình riêng. Những tùy chọn này được mô tả trong các phần sau.

+ + +

Tạo nhóm thiết đặt

+ +

Nếu bạn trình bày một danh sách từ 10 thiết đặt trở lên, người dùng +có thể gặp khó khăn trong việc dò tìm, hiểu và xử lý chúng. Bạn có thể khắc phục điều này bằng cách +chia một số hoặc tất cả thiết đặt thành các nhóm, qua đó biến một danh sách dài thành nhiều +danh sách ngắn hơn. Một nhóm các thiết đặt có liên quan có thể được trình bày bằng một trong hai cách:

+ + + +

Bạn có thể sử dụng một hoặc cả hai kỹ thuật tạo nhóm này để sắp xếp các thiết đặt cho ứng dụng của mình. Khi +quyết định sử dụng cái nào và làm thế nào để chia các thiết đặt của mình, bạn nên tuân theo các hướng dẫn trong tài liệu hướng dẫn +Thiết đặt của Thiết kế Android.

+ + +

Sử dụng tiêu đề

+ +

Nếu bạn muốn cung cấp các thanh chia có tiêu đề giữa các nhóm thiết đặt (như minh họa trong hình 2), +hãy đặt từng nhóm đối tượng {@link android.preference.Preference} vào bên trong một {@link +android.preference.PreferenceCategory}.

+ +

Ví dụ:

+ +
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory 
+        android:title="@string/pref_sms_storage_title"
+        android:key="pref_key_storage_settings">
+        <CheckBoxPreference
+            android:key="pref_key_auto_delete"
+            android:summary="@string/pref_summary_auto_delete"
+            android:title="@string/pref_title_auto_delete"
+            android:defaultValue="false"... />
+        <Preference 
+            android:key="pref_key_sms_delete_limit"
+            android:dependency="pref_key_auto_delete"
+            android:summary="@string/pref_summary_delete_limit"
+            android:title="@string/pref_title_sms_delete"... />
+        <Preference 
+            android:key="pref_key_mms_delete_limit"
+            android:dependency="pref_key_auto_delete"
+            android:summary="@string/pref_summary_delete_limit"
+            android:title="@string/pref_title_mms_delete" ... />
+    </PreferenceCategory>
+    ...
+</PreferenceScreen>
+
+ + +

Sử dụng màn hình con

+ +

Nếu bạn muốn đặt các nhóm thiết đặt vào một màn hình con (như minh họa trong hình 3), hãy đặt nhóm +các đối tượng {@link android.preference.Preference} vào bên trong một {@link +android.preference.PreferenceScreen}.

+ + +

Hình 3. Màn hình con thiết đặt. Phần tử {@code +<PreferenceScreen>} sẽ tạo +một mục mà, khi được chọn, nó sẽ mở ra một danh sách riêng để hiển thị các thiết đặt lồng nhau.

+ +

Ví dụ:

+ +
+<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- opens a subscreen of settings -->
+    <PreferenceScreen
+        android:key="button_voicemail_category_key"
+        android:title="@string/voicemail"
+        android:persistent="false">
+        <ListPreference
+            android:key="button_voicemail_provider_key"
+            android:title="@string/voicemail_provider" ... />
+        <!-- opens another nested subscreen -->
+        <PreferenceScreen
+            android:key="button_voicemail_setting_key"
+            android:title="@string/voicemail_settings"
+            android:persistent="false">
+            ...
+        </PreferenceScreen>
+        <RingtonePreference
+            android:key="button_voicemail_ringtone_key"
+            android:title="@string/voicemail_ringtone_title"
+            android:ringtoneType="notification" ... />
+        ...
+    </PreferenceScreen>
+    ...
+</PreferenceScreen>
+
+ + +

Sử dụng ý định

+ +

Trong một số trường hợp, bạn có thể muốn một mục tùy chọn mở một hoạt động khác thay vì một +màn hình thiết đặt, chẳng hạn như một trình duyệt web để xem một trang web. Để gọi ra một {@link +android.content.Intent} khi người dùng chọn một mục tùy chọn, hãy thêm một phần tử {@code <intent>} +làm con của phần tử {@code <Preference>} tương ứng.

+ +

Ví dụ, sau đây là cách bạn có thể sử dụng một mục tùy chọn để mở một trang web:

+ +
+<Preference android:title="@string/prefs_web_page" >
+    <intent android:action="android.intent.action.VIEW"
+            android:data="http://www.example.com" />
+</Preference>
+
+ +

Bạn có thể tạo cả ý định biểu thị và không biểu thị bằng cách sử dụng các thuộc tính sau:

+ +
+
{@code android:action}
+
Hành động cần gán, theo mỗi phương pháp {@link android.content.Intent#setAction setAction()} +.
+
{@code android:data}
+
Dữ liệu cần gán, theo mỗi phương pháp {@link android.content.Intent#setData setData()}.
+
{@code android:mimeType}
+
Kiểu MIME cần gán, theo mỗi phương pháp {@link android.content.Intent#setType setType()} +.
+
{@code android:targetClass}
+
Phần lớp của tên thành phần, theo mỗi phương pháp {@link android.content.Intent#setComponent +setComponent()}.
+
{@code android:targetPackage}
+
Phần gói của tên thành phần, theo mỗi phương pháp {@link +android.content.Intent#setComponent setComponent()}.
+
+ + + +

Tạo một Hoạt động Tùy chọn

+ +

Để hiển thị thiết đặt của bạn trong một hoạt động, hãy mở rộng lớp {@link +android.preference.PreferenceActivity}. Đây là phần mở rộng của lớp {@link +android.app.Activity} truyền thống mà hiển thị một danh sách các thiết đặt dựa trên một phân cấp của các đối tượng {@link +android.preference.Preference}. {@link android.preference.PreferenceActivity} +sẽ tự động duy trì các thiết đặt liên kết với từng {@link +android.preference.Preference} khi người dùng thực hiện một thay đổi.

+ +

Lưu ý: Nếu bạn đang phát triển ứng dụng của mình cho phiên bản Android 3.0 và +cao hơn, thay vào đó bạn nên sử dụng {@link android.preference.PreferenceFragment}. Đi đến phần +tiếp theo về Sử dụng Phân đoạn Tùy chọn.

+ +

Điều quan trọng nhất cần nhớ đó là bạn không được tải một bố trí dạng xem trong khi gọi lại {@link +android.preference.PreferenceActivity#onCreate onCreate()}. Thay vào đó, bạn hãy gọi {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} để +thêm tùy chọn mà bạn đã khai báo trong một tệp XML vào hoạt động. Ví dụ, sau đây là đoạn mã tối thiểu +cần thiết cho một {@link android.preference.PreferenceActivity} chức năng:

+ +
+public class SettingsActivity extends PreferenceActivity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.preferences);
+    }
+}
+
+ +

Đây là đoạn mã vừa đủ cho một số ứng dụng bởi ngay khi người dùng sửa đổi một tùy chọn, +hệ thống sẽ lưu thay đổi đối với tệp {@link android.content.SharedPreferences} mặc định mà các +thành phần ứng dụng khác của bạn có thể đọc khi bạn cần kiểm tra thiết đặt của người dùng. Tuy nhiên, +nhiều ứng dụng lại yêu cầu thêm mã để theo dõi những thay đổi xảy ra với các tùy chọn đó. +Để biết thông tin về việc theo dõi thay đổi trong tệp {@link android.content.SharedPreferences}, +hãy xem phần về Đọc Tùy chọn.

+ + + + +

Sử dụng Phân đoạn Tùy chọn

+ +

Nếu bạn đang phát triển cho phiên bản Android 3.0 (API mức 11) trở lên, bạn nên sử dụng một {@link +android.preference.PreferenceFragment} để hiển thị danh sách các đối tượng {@link android.preference.Preference} +của bạn. Bạn có thể thêm một {@link android.preference.PreferenceFragment} vào bất kỳ hoạt động nào—bạn không cần +sử dụng {@link android.preference.PreferenceActivity}.

+ +

Phân đoạn cung cấp một kiến trúc +linh hoạt hơn cho ứng dụng của bạn, so với việc sử dụng chỉ các hoạt động, dù loại hoạt động +mà bạn đang xây dựng là gì. Như vậy, chúng tôi gợi ý bạn sử dụng {@link +android.preference.PreferenceFragment} để kiểm soát hiển thị các thiết đặt của mình thay cho {@link +android.preference.PreferenceActivity} khi có thể.

+ +

Việc triển khai {@link android.preference.PreferenceFragment} có thể chỉ đơn giản như +định nghĩa phương pháp {@link android.preference.PreferenceFragment#onCreate onCreate()} để tải một +tệp tùy chọn bằng {@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()}. Ví dụ:

+ +
+public static class SettingsFragment extends PreferenceFragment {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Load the preferences from an XML resource
+        addPreferencesFromResource(R.xml.preferences);
+    }
+    ...
+}
+
+ +

Khi đó, bạn có thể thêm phân đoạn này vào một {@link android.app.Activity} giống như cách mà bạn sẽ làm với bất kỳ +{@link android.app.Fragment} nào khác. Ví dụ:

+ +
+public class SettingsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Display the fragment as the main content.
+        getFragmentManager().beginTransaction()
+                .replace(android.R.id.content, new SettingsFragment())
+                .commit();
+    }
+}
+
+ +

Lưu ý: {@link android.preference.PreferenceFragment} không có một +đối tượng {@link android.content.Context} của chính nó. Nếu bạn cần một đối tượng {@link android.content.Context} +, bạn có thể gọi {@link android.app.Fragment#getActivity()}. Tuy nhiên, hãy chắc chắn là chỉ gọi +{@link android.app.Fragment#getActivity()} khi phân đoạn đó được gắn kèm với một hoạt động. Khi +phân đoạn chưa được gắn kèm, hoặc bị bỏ gắn kèm trong khi kết thúc vòng đời của nó, {@link +android.app.Fragment#getActivity()} sẽ trả về rỗng.

+ + +

Thiết đặt Giá trị Mặc định

+ +

Tùy chọn mà bạn tạo có thể định nghĩa một số hành vi quan trọng cho ứng dụng của bạn, vì thế +bạn cần phải khởi tạo tệp {@link android.content.SharedPreferences} kèm theo với các +giá trị mặc định cho từng {@link android.preference.Preference} khi người dùng lần đầu mở +ứng dụng của bạn.

+ +

Điều đầu tiên bạn phải làm đó là quy định một giá trị mặc định cho từng đối tượng {@link +android.preference.Preference} +trong tệp XML của bạn bằng cách sử dụng thuộc tính {@code android:defaultValue}. Giá trị đó có thể là bất kỳ kiểu +dữ liệu nào mà phù hợp với đối tượng {@link android.preference.Preference} tương ứng. Ví +dụ:

+ +
+<!-- default value is a boolean -->
+<CheckBoxPreference
+    android:defaultValue="true"
+    ... />
+
+<!-- default value is a string -->
+<ListPreference
+    android:defaultValue="@string/pref_syncConnectionTypes_default"
+    ... />
+
+ +

Khi đó, từ phương pháp {@link android.app.Activity#onCreate onCreate()} trong hoạt động chính +—của ứng dụng của bạn và trong bất kỳ hoạt động nào khác mà thông qua đó người dùng có thể vào ứng dụng của bạn lần +đầu tiên—hãy gọi {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}:

+ +
+PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
+
+ +

Việc gọi này trong khi {@link android.app.Activity#onCreate onCreate()} sẽ đảm bảo rằng +ứng dụng của bạn được khởi tạo phù hợp với các thiết đặt mặc định mà ứng dụng của bạn có thể cần +đọc để xác định một số hành vi (chẳng hạn như có tải xuống dữ liệu trong khi đang trên +mạng di động hay không).

+ +

Phương pháp này dùng ba tham đối:

+
    +
  • {@link android.content.Context} ứng dụng của bạn.
  • +
  • ID tài nguyên cho tệp XML tùy chọn mà bạn muốn đặt các giá trị mặc định cho.
  • +
  • Một boolean cho biết các giá trị mặc định có nên được đặt nhiều hơn một lần hay không. +

    Khi tham đối này là false, hệ thống sẽ đặt các giá trị mặc định chỉ khi phương pháp này chưa từng được +gọi trước đây (hoặc {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} +trong tệp tùy chọn được chia sẻ giá trị mặc định là sai).

  • +
+ +

Miễn là bạn đặt tham đối thứ ba này thành false, bạn có thể gọi phương pháp này một cách an toàn +mỗi khi hoạt động của bạn bắt đầu mà không khống chế các tùy chọn đã lưu của người dùng bằng cách đặt lại chúng thành +mặc định. Tuy nhiên, nếu bạn đặt nó thành true, bạn sẽ khống chế mọi giá trị +trước đó bằng các giá trị mặc định.

+ + + +

Sử dụng Tiêu đề Tùy chọn

+ +

Trong vài trường hợp hiếm gặp, bạn có thể muốn thiết kế các thiết đặt của mình sao cho màn hình thứ nhất +chỉ hiển thị một danh sách các màn hình con (chẳng hạn như trong ứng dụng Thiết đặt của hệ thống, +như minh họa trong các hình 4 và 5). Khi phát triển thiết kế như vậy cho phiên bản Android 3.0 trở lên, bạn +nên sử dụng tính năng "tiêu đề" mới trong Android 3.0, thay vì xây dựng màn hình con với các phần tử +{@link android.preference.PreferenceScreen} lồng nhau.

+ +

Để xây dựng thiết đặt có tiêu đề của mình, bạn cần:

+
    +
  1. Tách riêng từng nhóm thiết đặt thành các thực thể riêng của {@link +android.preference.PreferenceFragment}. Cụ thể, mỗi nhóm thiết đặt cần một tệp XML +riêng.
  2. +
  3. Tạo một tệp tiêu đề XML liệt kê từng nhóm thiết đặt và khai báo phân đoạn nào +chứa danh sách thiết đặt tương ứng.
  4. +
  5. Mở rộng lớp {@link android.preference.PreferenceActivity} để lưu trữ các thiết đặt của bạn.
  6. +
  7. Triển khai lệnh gọi lại {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} để quy định +tệp tiêu đề.
  8. +
+ +

Một lợi ích tuyệt vời đối với việc sử dụng thiết kế này đó là {@link android.preference.PreferenceActivity} +tự động trình bày bố trí hai bảng như minh họa trong hình 4 khi chạy trên màn hình lớn.

+ +

Ngay cả khi ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0, bạn có thể xây dựng ứng dụng +của mình để sử dụng {@link android.preference.PreferenceFragment} cho một trình chiếu hai bảng trên +các thiết bị mới hơn, trong khi vẫn hỗ trợ phân cấp đa màn hình truyền thống trên các thiết bị +cũ hơn (xem phần nói về Hỗ trợ các phiên bản cũ hơn +với tiêu đề tùy chọn).

+ + +

Hình 4. Bố trí có hai bảng với tiêu đề.
1. +Tiêu đề được định nghĩa trong một tệp tiêu đề XML.
2. Mỗi nhóm thiết đặt được định nghĩa bởi một +{@link android.preference.PreferenceFragment}, được quy định bởi một phần tử {@code <header>} trong tệp tiêu đề +.

+ + +

Hình 5. Thiết bị cầm tay với các tiêu đề thiết đặt. Khi một +mục được chọn, {@link android.preference.PreferenceFragment} được liên kết sẽ thay thế +tiêu đề.

+ + +

Tạo tệp tiêu đề

+ +

Mỗi nhóm thiết đặt trong danh sách tiêu đề của bạn được quy định bởi một phần tử {@code <header>} +đơn lẻ bên trong một phần tử {@code <preference-headers>} gốc. Ví dụ:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+    <header 
+        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one" />
+    <header 
+        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" >
+        <!-- key/value pairs can be included as arguments for the fragment. -->
+        <extra android:name="someKey" android:value="someHeaderValue" />
+    </header>
+</preference-headers>
+
+ +

Với thuộc tính {@code android:fragment}, mỗi tiêu đề sẽ khai báo một thực thể của {@link +android.preference.PreferenceFragment} mà sẽ mở khi người dùng chọn tiêu đề đó.

+ +

Phần tử {@code <extras>} cho phép bạn chuyển các cặp khóa-giá trị sang phân đoạn trong một {@link +android.os.Bundle}. Phân đoạn có thể truy xuất các tham đối bằng cách gọi {@link +android.app.Fragment#getArguments()}. Bạn có thể chuyển các tham đối tới phân đoạn vì nhiều +lý do khác nhau, nhưng một lý do chính đáng đó là để sử dụng lại cùng lớp con của {@link +android.preference.PreferenceFragment} cho mỗi nhóm và sử dụng tham đối để quy định +tệp XML tùy chọn nào mà phân đoạn cần tải.

+ +

Ví dụ, sau đây là một phân đoạn mà có thể được tái sử dụng cho nhiều nhóm thiết đặt, khi từng +tiêu đề định nghĩa một tham đối {@code <extra>} với khóa {@code "settings"}:

+ +
+public static class SettingsFragment extends PreferenceFragment {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        String settings = getArguments().getString("settings");
+        if ("notifications".equals(settings)) {
+            addPreferencesFromResource(R.xml.settings_wifi);
+        } else if ("sync".equals(settings)) {
+            addPreferencesFromResource(R.xml.settings_sync);
+        }
+    }
+}
+
+ + + +

Hiển thị tiêu đề

+ +

Để hiển thị tiêu đề tùy chọn, bạn phải triển khai phương pháp gọi lại {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} và gọi +{@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}. Ví dụ:

+ +
+public class SettingsActivity extends PreferenceActivity {
+    @Override
+    public void onBuildHeaders(List<Header> target) {
+        loadHeadersFromResource(R.xml.preference_headers, target);
+    }
+}
+
+ +

Khi người dùng chọn một mục từ danh sách tiêu đề, hệ thống sẽ mở {@link +android.preference.PreferenceFragment} kèm theo.

+ +

Lưu ý: Khi sử dụng tiêu đề tùy chọn, lớp con {@link +android.preference.PreferenceActivity} của bạn không cần triển khai phương pháp {@link +android.preference.PreferenceActivity#onCreate onCreate()}, vì tác vụ cần thiết duy nhất +cho hoạt động đó là tải tiêu đề.

+ + +

Hỗ trợ các phiên bản cũ hơn với tiêu đề tùy chọn

+ +

Nếu ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0, bạn vẫn có thể sử dụng tiêu đề để +cung cấp một bố trí hai bảng khi chạy trên Android 3.0 trở lên. Tất cả những việc bạn cần làm đó là tạo một +tệp XML tùy chọn bổ sung có sử dụng phần tử cơ bản {@link android.preference.Preference +<Preference>} đóng vai trò như mục tiêu đề (để dùng cho các phiên bản Android +cũ hơn).

+ +

Tuy nhiên, thay vì mở một {@link android.preference.PreferenceScreen} mới, từng phần tử {@link +android.preference.Preference <Preference>} sẽ gửi một {@link android.content.Intent} tới +{@link android.preference.PreferenceActivity} mà quy định tệp XML tùy chọn cần +tải.

+ +

Ví dụ, sau đây là một tệp XML cho các tiêu đề tùy chọn được sử dụng trên Android 3.0 +trở lên ({@code res/xml/preference_headers.xml}):

+ +
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+    <header 
+        android:fragment="com.example.prefs.SettingsFragmentOne"
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one" />
+    <header 
+        android:fragment="com.example.prefs.SettingsFragmentTwo"
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" />
+</preference-headers>
+
+ +

Và sau đây là một tệp tùy chọn cung cấp cùng các tiêu đề cho các phiên bản cũ hơn +Android 3.0 ({@code res/xml/preference_headers_legacy.xml}):

+ +
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <Preference 
+        android:title="@string/prefs_category_one"
+        android:summary="@string/prefs_summ_category_one"  >
+        <intent 
+            android:targetPackage="com.example.prefs"
+            android:targetClass="com.example.prefs.SettingsActivity"
+            android:action="com.example.prefs.PREFS_ONE" />
+    </Preference>
+    <Preference 
+        android:title="@string/prefs_category_two"
+        android:summary="@string/prefs_summ_category_two" >
+        <intent 
+            android:targetPackage="com.example.prefs"
+            android:targetClass="com.example.prefs.SettingsActivity"
+            android:action="com.example.prefs.PREFS_TWO" />
+    </Preference>
+</PreferenceScreen>
+
+ +

Vì hỗ trợ dành cho {@code <preference-headers>} đã được thêm trong Android 3.0, hệ thống sẽ gọi +{@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} trong {@link +android.preference.PreferenceActivity} của bạn chỉ khi đang chạy trên phiên bản Androd 3.0 hoặc cao hơn. Để tải +tệp tiêu đề "kế thừa" ({@code preference_headers_legacy.xml}), bạn phải kiểm tra phiên bản Android +và, nếu phiên bản cũ hơn Android 3.0 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}), hãy gọi {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} để +tải tệp tiêu đề kế thừa. Ví dụ:

+ +
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    ...
+
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+        // Load the legacy preferences headers
+        addPreferencesFromResource(R.xml.preference_headers_legacy);
+    }
+}
+
+// Called only on Honeycomb and later
+@Override
+public void onBuildHeaders(List<Header> target) {
+   loadHeadersFromResource(R.xml.preference_headers, target);
+}
+
+ +

Việc duy nhất còn lại cần làm đó là xử lý {@link android.content.Intent} mà được chuyển vào +hoạt động để nhận biết tệp tùy chọn nào cần tải. Vì vậy, hãy truy xuất hành động của ý định và so sánh nó với +các xâu hành động đã biết mà bạn đã sử dụng trong tag {@code <intent>} của XML tùy chọn:

+ +
+final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
+...
+
+@Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    String action = getIntent().getAction();
+    if (action != null && action.equals(ACTION_PREFS_ONE)) {
+        addPreferencesFromResource(R.xml.preferences);
+    }
+    ...
+
+    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+        // Load the legacy preferences headers
+        addPreferencesFromResource(R.xml.preference_headers_legacy);
+    }
+}
+
+ +

Lưu ý rằng các lệnh gọi liên tiếp đến {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} sẽ +xếp chồng tất cả tùy chọn trong một danh sách duy nhất, vì thế hãy chắc chắn rằng nó chỉ được gọi một lần bằng cách liên kết các +điều kiện với mệnh đề else-if.

+ + + + + +

Đọc Tùy chọn

+ +

Theo mặc định, tất cả tùy chọn của ứng dụng của bạn đều được lưu vào một tệp có thể truy cập từ bất kỳ nơi nào +trong ứng dụng của bạn bằng cách gọi phương pháp tĩnh {@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()}. Điều này sẽ trả về đối tượng {@link +android.content.SharedPreferences} chứa tất cả cặp khóa-giá trị liên kết +với các đối tượng {@link android.preference.Preference} được sử dụng trong {@link +android.preference.PreferenceActivity}.

+ +

Ví dụ, sau đây là cách bạn có thể đọc một trong các giá trị tùy chọn từ bất kỳ hoạt động nào khác trong ứng dụng +của mình:

+ +
+SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
+String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
+
+ + + +

Theo dõi thay đổi tùy chọn

+ +

Có một vài lý do khiến bạn có thể muốn được thông báo càng sớm càng tốt nếu người dùng thay đổi một trong các +tùy chọn. Để nhận một phương pháp gọi lại khi thay đổi xảy ra với bất kỳ tùy chọn nào, +hãy triển khai giao diện {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} và đăng ký đối tượng theo dõi cho đối tượng +{@link android.content.SharedPreferences} bằng cách gọi {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}.

+ +

Giao diện này chỉ có một phương pháp gọi lại, {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()}, và bạn có thể thấy đây là cách dễ nhất để triển khai giao diện như một phần +hoạt động của mình. Ví dụ:

+ +
+public class SettingsActivity extends PreferenceActivity
+                              implements OnSharedPreferenceChangeListener {
+    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
+    ...
+
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+        String key) {
+        if (key.equals(KEY_PREF_SYNC_CONN)) {
+            Preference connectionPref = findPreference(key);
+            // Set summary to be the user-description for the selected value
+            connectionPref.setSummary(sharedPreferences.getString(key, ""));
+        }
+    }
+}
+
+ +

Trong ví dụ này, phương pháp sẽ kiểm tra xem thiết đặt bị thay đổi có áp dụng cho một khóa tùy chọn đã biết không. Nó +sẽ gọi {@link android.preference.PreferenceActivity#findPreference findPreference()} để nhận đối tượng +{@link android.preference.Preference} đã bị thay đổi để nó có thể sửa đổi tóm tắt +của mục đó thành mô tả lựa chọn của người dùng. Cụ thể, khi thiết đặt là một {@link +android.preference.ListPreference} hoặc thiết đặt nhiều lựa chọn khác, bạn nên gọi {@link +android.preference.Preference#setSummary setSummary()} khi thiết đặt thay đổi để hiển thị +trạng thái hiện tại (chẳng hạn như thiết đặt Ngủ như minh họa trong hình 5).

+ +

Lưu ý: Như đã mô tả trong tài liệu Thiết kế Android về Thiết đặt, chúng tôi khuyên bạn nên cập nhật +tóm tắt cho {@link android.preference.ListPreference} mỗi khi người dùng thay đổi tùy chọn để +mô tả thiết đặt hiện tại.

+ +

Để quản lý vòng đời trong hoạt động cho phù hợp, chúng tôi khuyên rằng bạn nên đăng ký và bỏ đăng ký +{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener} của mình tương ứng trong {@link +android.app.Activity#onResume} và các lệnh gọi lại {@link android.app.Activity#onPause}:

+ +
+@Override
+protected void onResume() {
+    super.onResume();
+    getPreferenceScreen().getSharedPreferences()
+            .registerOnSharedPreferenceChangeListener(this);
+}
+
+@Override
+protected void onPause() {
+    super.onPause();
+    getPreferenceScreen().getSharedPreferences()
+            .unregisterOnSharedPreferenceChangeListener(this);
+}
+
+ +

Chú ý: Khi bạn gọi {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()}, trình quản lý tùy chọn hiện +không lưu trữ một tham chiếu mạnh tới đối tượng theo dõi. Bạn phải lưu trữ một tham chiếu +mạnh tới đối tượng theo dõi, nếu không nó sẽ dễ bị thu thập thông tin rác. Chúng tôi +khuyên bạn nên giữ một tham chiếu tới đối tượng theo dõi trong dữ liệu thực thể của một đối tượng +mà sẽ tồn tại miễn là bạn còn cần đối tượng theo dõi đó.

+ +

Ví dụ, trong đoạn mã sau, hàm gọi không giữ tham chiếu tới +đối tượng theo dõi. Kết quả là đối tượng theo dõi sẽ bị thu thập thông tin rác, +và nó sẽ bị lỗi tại một thời điểm không xác định trong tương lai:

+ +
+prefs.registerOnSharedPreferenceChangeListener(
+  // Bad! The listener is subject to garbage collection!
+  new SharedPreferences.OnSharedPreferenceChangeListener() {
+  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+    // listener implementation
+  }
+});
+
+ +

Thay vào đó, hãy lưu một tham chiếu tới đối tượng theo dõi trong một trường dữ liệu thực thể của một +đối tượng mà sẽ tồn tại miễn là còn cần đối tượng theo dõi đó:

+ +
+SharedPreferences.OnSharedPreferenceChangeListener listener =
+    new SharedPreferences.OnSharedPreferenceChangeListener() {
+  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+    // listener implementation
+  }
+};
+prefs.registerOnSharedPreferenceChangeListener(listener);
+
+ +

Quản lý Sử dụng Mạng

+ + +

Bắt đầu với Android 4.0, ứng dụng Thiết đặt của hệ thống sẽ cho phép người dùng xem +ứng dụng của họ đang sử dụng bao nhiêu dữ liệu mạng khi đang ở tiền cảnh và dưới nền. Khi đó, người dùng có thể +vô hiệu hóa việc sử dụng dữ liệu chạy ngầm cho từng ứng dụng. Để tránh việc người dùng vô hiệu hóa truy cập dữ liệu +của ứng dụng của bạn từ dưới nền, bạn nên sử dụng kết nối dữ liệu một cách hiệu quả và cho phép +người dùng tinh chỉnh mức sử dụng dữ liệu cho ứng dụng của bạn thông qua thiết đặt ứng dụng.

+ +

Ví dụ, bạn có thể cho phép người dùng kiểm soát tần suất ứng dụng của bạn đồng bộ dữ liệu, ứng dụng của bạn +chỉ được thực hiện tải lên/tải xuống khi trên Wi-Fi, ứng dụng của bạn sử dụng dữ liệu trong khi đang chuyển vùng dữ liệu, v.v... hay không. Với +những kiểm soát này, người dùng sẽ ít có khả năng vô hiệu hóa truy cập dữ liệu của ứng dụng của bạn +hơn nhiều khi họ đạt gần mức giới hạn đặt ra trong Thiết đặt hệ thống, vì thay vào đó, họ có thể kiểm soát chính xác +lượng dữ liệu mà ứng dụng của bạn sử dụng.

+ +

Sau khi bạn đã thêm các tùy chọn cần thiết trong {@link android.preference.PreferenceActivity} +của mình để kiểm soát các thói quen dữ liệu của ứng dụng của bạn, bạn nên thêm một bộ lọc ý định cho {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} trong tệp bản kê khai của mình. Ví dụ:

+ +
+<activity android:name="SettingsActivity" ... >
+    <intent-filter>
+       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
+       <category android:name="android.intent.category.DEFAULT" />
+    </intent-filter>
+</activity>
+
+ +

Bộ lọc ý định này cho hệ thống biết rằng đây là hoạt động kiểm soát mức sử dụng dữ liệu +của ứng dụng của bạn. Vì thế, khi người dùng kiểm tra lượng dữ liệu mà ứng dụng của bạn đang dùng từ ứng dụng +Thiết đặt của hệ thống, sẽ có một nút Xem thiết đặt ứng dụng khởi chạy +{@link android.preference.PreferenceActivity} của bạn, vì thế người dùng có thể tinh chỉnh lượng dữ liệu mà ứng dụng của bạn +dùng.

+ + + + + + + +

Xây dựng một Thiết đặt Tùy chỉnh

+ +

Khuôn khổ Android bao gồm nhiều lớp con {@link android.preference.Preference} mà +cho phép bạn xây dựng một UI cho một vài loại thiết đặt khác nhau. +Tuy nhiên, bạn có thể khám phá thiết đặt mình cần mà chưa có giải pháp tích hợp sẵn, chẳng hạn như một +bộ chọn số hay bộ chọn ngày. Trong trường hợp như vậy, bạn sẽ cần tạo một tùy chọn tùy chỉnh bằng cách mở rộng +lớp {@link android.preference.Preference} hoặc một trong các lớp con khác.

+ +

Khi bạn mở rộng lớp {@link android.preference.Preference}, có một vài điều quan trọng +mà bạn cần làm:

+ +
    +
  • Quy định giao diện người dùng sẽ xuất hiện khi người dùng chọn thiết đặt.
  • +
  • Lưu giá trị của thiết đặt khi phù hợp.
  • +
  • Khởi tạo {@link android.preference.Preference} bằng giá trị hiện tại (hoặc mặc định) +khi xét tới dạng xem.
  • +
  • Cung cấp giá trị mặc định khi hệ thống yêu cầu.
  • +
  • Nếu {@link android.preference.Preference} cung cấp UI của chính mình (chẳng hạn như một hộp thoại), hãy lưu +và khôi phục trạng thái để xử lý các thay đổi trong vòng đời (chẳng hạn như khi người dùng xoay màn hình).
  • +
+ +

Các phần sau mô tả cách hoàn thành từng tác vụ này.

+ + + +

Quy định một giao diện người dùng

+ +

Nếu bạn trực tiếp mở rộng lớp {@link android.preference.Preference}, bạn cần triển khai +{@link android.preference.Preference#onClick()} để định nghĩa hành động xảy ra khi người dùng +chọn mục. Tuy nhiên, hầu hết các thiết đặt tùy chỉnh sẽ mở rộng {@link android.preference.DialogPreference} để +hiển thị một hộp thoại, điều này làm đơn giản hóa quy trình. Khi bạn mở rộng {@link +android.preference.DialogPreference}, bạn phải gọi {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()} trong khi đang ở trong +hàm dựng lớp để quy định bố trí cho hộp thoại.

+ +

Ví dụ, sau đây là hàm dựng cho một {@link +android.preference.DialogPreference} tùy chỉnh mà khai báo bố trí và quy định văn bản cho +các nút hộp thoại tích cực và tiêu cực mặc định:

+ +
+public class NumberPickerPreference extends DialogPreference {
+    public NumberPickerPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        setDialogLayoutResource(R.layout.numberpicker_dialog);
+        setPositiveButtonText(android.R.string.ok);
+        setNegativeButtonText(android.R.string.cancel);
+        
+        setDialogIcon(null);
+    }
+    ...
+}
+
+ + + +

Lưu giá trị của thiết đặt

+ +

Bạn có thể lưu một giá trị cho thiết đặt vào bất cứ lúc nào bằng cách gọi một trong các phương pháp của lớp {@link +android.preference.Preference}, {@code persist*()}, chẳng hạn như {@link +android.preference.Preference#persistInt persistInt()} nếu giá trị của thiết đặt là một số nguyên hoặc +{@link android.preference.Preference#persistBoolean persistBoolean()} để lưu một boolean.

+ +

Lưu ý: Mỗi {@link android.preference.Preference} chỉ có thể lưu một +kiểu dữ liệu, vì thế bạn phải sử dụng phương pháp {@code persist*()} phù hợp cho kiểu dữ liệu được sử dụng bởi +{@link android.preference.Preference} tùy chỉnh của mình.

+ +

Thời điểm bạn chọn duy trì thiết đặt có thể phụ thuộc vào lớp {@link +android.preference.Preference} nào mà bạn mở rộng. Nếu mở rộng {@link +android.preference.DialogPreference}, khi đó bạn nên duy trì giá trị đó chỉ khi hộp thoại +đóng lại do kết quả tích cực (người dùng chọn nút "OK").

+ +

Khi {@link android.preference.DialogPreference} đóng lại, hệ thống sẽ gọi phương pháp {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()}. Phương pháp bao gồm một +tham đối boolean quy định xem người dùng có trả về kết quả "tích cực" hay không—nếu kết quả là +true, khi đó, người dùng đã chọn nút tích cực và bạn nên lưu giá trị mới này. Ví +dụ:

+ +
+@Override
+protected void onDialogClosed(boolean positiveResult) {
+    // When the user selects "OK", persist the new value
+    if (positiveResult) {
+        persistInt(mNewValue);
+    }
+}
+
+ +

Trong ví dụ này, mNewValue là một thành viên lớp lưu giữ giá trị +hiện tại của thiết đặt. Việc gọi {@link android.preference.Preference#persistInt persistInt()} sẽ lưu giá trị vào +tệp {@link android.content.SharedPreferences} (tự động sử dụng khóa mà +được quy định trong tệp XML cho {@link android.preference.Preference} này).

+ + +

Khởi tạo giá trị hiện tại

+ +

Khi hệ thống thêm {@link android.preference.Preference} của bạn vào màn hình, nó +gọi {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} để thông báo +với bạn xem thiết đặt có giá trị được duy trì hay không. Nếu không có giá trị được duy trì, lệnh gọi này sẽ cung cấp +cho bạn giá trị mặc định.

+ +

Phương pháp {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} chuyển một +boolean, restorePersistedValue, để cho biết liệu giá trị đã được duy trì +cho thiết đặt hay không. Nếu nó là true, khi đó bạn nên truy xuất giá trị được duy trì bằng cách gọi +một trong các phương pháp của lớp {@link +android.preference.Preference}, {@code getPersisted*()}, chẳng hạn như {@link +android.preference.Preference#getPersistedInt getPersistedInt()} đối với một giá trị số nguyên. Bạn sẽ +thường muốn truy xuất giá trị được duy trì sao cho bạn có thể cập nhật UI cho phù hợp để phản ánh +giá trị đã lưu trước đó.

+ +

Nếu restorePersistedValuefalse, vậy bạn +nên sử dụng giá trị mặc định được chuyển trong tham đối thứ hai.

+ +
+@Override
+protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
+    if (restorePersistedValue) {
+        // Restore existing state
+        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
+    } else {
+        // Set default state from the XML attribute
+        mCurrentValue = (Integer) defaultValue;
+        persistInt(mCurrentValue);
+    }
+}
+
+ +

Mỗi phương pháp {@code getPersisted*()} sẽ lấy một tham đối quy định +giá trị mặc định sẽ sử dụng trong trường hợp thực sự không có giá trị được duy trì hoặc khóa không tồn tại. Trong +ví dụ trên, một hằng số cục bộ được sử dụng để quy định giá trị mặc định trong trường hợp {@link +android.preference.Preference#getPersistedInt getPersistedInt()} không thể trả về một giá trị được duy trì.

+ +

Chú ý: Bạn không thể sử dụng +defaultValue làm giá trị mặc định trong phương pháp {@code getPersisted*()}, bởi +giá trị của nó luôn rỗng khi restorePersistedValuetrue.

+ + +

Cung cấp một giá trị mặc định

+ +

Nếu trường hợp lớp {@link android.preference.Preference} của bạn quy định một giá trị mặc định +(với thuộc tính {@code android:defaultValue}), khi đó hệ thống +sẽ gọi {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()} khi nó khởi tạo đối tượng để truy xuất giá trị. Bạn phải +triển khai phương pháp này để hệ thống lưu giá trị mặc định trong {@link +android.content.SharedPreferences}. Ví dụ:

+ +
+@Override
+protected Object onGetDefaultValue(TypedArray a, int index) {
+    return a.getInteger(index, DEFAULT_VALUE);
+}
+
+ +

Các tham đối của phương pháp cung cấp mọi thứ bạn cần: mảng thuộc tính và vị trí chỉ mục +của {@code android:defaultValue} mà bạn phải truy xuất. Lý do bạn phải triển khai +phương pháp này nhằm trích xuất giá trị mặc định từ thuộc tính đó là bởi bạn phải quy định +một giá trị mặc định cục bộ cho thuộc tính trong trường hợp giá trị không được định nghĩa.

+ + + +

Lưu và khôi phục trạng thái của Tùy chọn

+ +

Giống như {@link android.view.View} trong một bố trí, lớp con {@link android.preference.Preference} +của bạn chịu trách nhiệm lưu và khôi phục trạng thái của nó trong trường hợp hoạt động hoặc phân đoạn +được khởi động lại (chẳng hạn như khi người dùng xoay màn hình). Để lưu và khôi phục +trạng thái của lớp {@link android.preference.Preference} của bạn cho đúng, bạn phải triển khai các +phương pháp gọi lại vòng đời {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} và {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}.

+ +

Trạng thái của {@link android.preference.Preference} của bạn được định nghĩa bởi một đối tượng mà triển khai +giao diện {@link android.os.Parcelable}. Khuôn khổ Android sẽ cung cấp một đối tượng như vậy cho bạn +như một điểm bắt đầu để định nghĩa đối tượng trạng thái của bạn: lớp {@link +android.preference.Preference.BaseSavedState}.

+ +

Để định nghĩa cách thức lớp {@link android.preference.Preference} của bạn lưu trạng thái của nó +hãy mở rộng lớp {@link android.preference.Preference.BaseSavedState}. Bạn cần khống chế chỉ + một vài phương pháp và định nghĩa đối tượng {@link android.preference.Preference.BaseSavedState#CREATOR} +.

+ +

Đối với hầu hết ứng dụng, bạn có thể sao chép triển khai sau và chỉ cần thay đổi các dòng +xử lý {@code value} nếu lớp con {@link android.preference.Preference} của bạn lưu một kiểu +dữ liệu khác số nguyên.

+ +
+private static class SavedState extends BaseSavedState {
+    // Member that holds the setting's value
+    // Change this data type to match the type saved by your Preference
+    int value;
+
+    public SavedState(Parcelable superState) {
+        super(superState);
+    }
+
+    public SavedState(Parcel source) {
+        super(source);
+        // Get the current preference's value
+        value = source.readInt();  // Change this to read the appropriate data type
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        // Write the preference's value
+        dest.writeInt(value);  // Change this to write the appropriate data type
+    }
+
+    // Standard creator object using an instance of this class
+    public static final Parcelable.Creator<SavedState> CREATOR =
+            new Parcelable.Creator<SavedState>() {
+
+        public SavedState createFromParcel(Parcel in) {
+            return new SavedState(in);
+        }
+
+        public SavedState[] newArray(int size) {
+            return new SavedState[size];
+        }
+    };
+}
+
+ +

Với triển khai {@link android.preference.Preference.BaseSavedState} bên trên được thêm +vào ứng dụng của bạn (thường dưới dạng một lớp con của lớp con {@link android.preference.Preference} của bạn), khi đó +bạn cần triển khai các phương pháp {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} và {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} cho lớp con +{@link android.preference.Preference} của mình.

+ +

Ví dụ:

+ +
+@Override
+protected Parcelable onSaveInstanceState() {
+    final Parcelable superState = super.onSaveInstanceState();
+    // Check whether this Preference is persistent (continually saved)
+    if (isPersistent()) {
+        // No need to save instance state since it's persistent,
+        // use superclass state
+        return superState;
+    }
+
+    // Create instance of custom BaseSavedState
+    final SavedState myState = new SavedState(superState);
+    // Set the state's value with the class member that holds current
+    // setting value
+    myState.value = mNewValue;
+    return myState;
+}
+
+@Override
+protected void onRestoreInstanceState(Parcelable state) {
+    // Check whether we saved the state in onSaveInstanceState
+    if (state == null || !state.getClass().equals(SavedState.class)) {
+        // Didn't save the state, so call superclass
+        super.onRestoreInstanceState(state);
+        return;
+    }
+
+    // Cast state to custom BaseSavedState and pass to superclass
+    SavedState myState = (SavedState) state;
+    super.onRestoreInstanceState(myState.getSuperState());
+    
+    // Set this Preference's widget to reflect the restored state
+    mNumberPicker.setValue(myState.value);
+}
+
+ diff --git a/docs/html-intl/intl/vi/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/vi/guide/topics/ui/ui-events.jd new file mode 100644 index 0000000000000000000000000000000000000000..b4d1635d1caa9e677b68dd31cf189781c60fa62c --- /dev/null +++ b/docs/html-intl/intl/vi/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=Sự kiện Nhập liệu +parent.title=Giao diện Người dùng +parent.link=index.html +@jd:body + + + +

Trên Android, có nhiều cách để can thiệp vào các sự kiện từ tương tác của một người dùng với ứng dụng của bạn. +Khi xem xét các sự kiện trong giao diện người dùng của bạn, cách tiếp cận là chụp lại sự kiện từ +đối tượng Dạng xem cụ thể mà người dùng tương tác với. Lớp Dạng xem sẽ cung cấp phương thức để làm việc này.

+ +

Trong các lớp Dạng xem khác nhau mà bạn sẽ sử dụng để soạn bố trí của mình, bạn có thể thấy một vài phương pháp gọi lại +công khai dường như hữu ích đối với sự kiện UI. Những phương pháp này được khuôn khổ Android gọi khi +xảy ra hành động tương ứng trên đối tượng đó. Ví dụ, khi một Dạng xem (chẳng hạn như một Nút) được chạm vào, +phương pháp onTouchEvent() được gọi trên đối tượng đó. Tuy nhiên, để can thiệp vào điều này, bạn phải mở rộng +lớp và khống chế phương pháp đó. Tuy nhiên, việc mở rộng mọi đối tượng Dạng xem +để xử lý một sự kiện như vậy sẽ là không thực tế. Đây là lý do tại sao lớp Dạng xem cũng chứa +một tập hợp giao diện lồng nhau cùng các phương pháp gọi lại mà bạn có thể định nghĩa dễ dàng hơn nhiều. Những giao diện này, +được gọi là đối tượng theo dõi sự kiện, là tấm vé để bạn chụp lại tương tác giữa người dùng với UI của bạn.

+ +

Trong khi các đối tượng theo dõi sự kiện sẽ thường được sử dụng để theo dõi tương tác của người dùng, có thể +có lúc bạn muốn mở rộng một lớp Dạng xem để xây dựng một thành phần tùy chỉnh. +Có thể là bạn muốn mở rộng lớp {@link android.widget.Button} +để khiến cái gì đó trông ấn tượng hơn. Trong trường hợp này, bạn sẽ có thể định nghĩa các hành vi sự kiện mặc định cho lớp +của mình bằng cách sử dụng bộ xử lý sự kiện của lớp.

+ + +

Đối tượng theo dõi Sự kiện

+ +

Đối tượng theo dõi sự kiện là một giao diện trong lớp {@link android.view.View} chứa một phương pháp gọi lại +đơn lẻ. Những phương pháp này sẽ được khuôn khổ Android gọi khi Dạng xem mà đối tượng theo dõi đã +được đăng ký với bị kích khởi bởi tương tác giữa người dùng với mục trong UI.

+ +

Trong giao diện của đối tượng theo dõi sự kiện là những phương pháp gọi lại sau:

+ +
+
onClick()
+
Từ {@link android.view.View.OnClickListener}. + Phương pháp này được gọi khi người dùng chạm vào mục + (khi ở chế độ cảm ứng), hoặc lấy tiêu điểm vào một mục bằng phím điều hướng hoặc bi xoay và + nhấn phím "enter" phù hợp hoặc nhấn bi xoay.
+
onLongClick()
+
Từ {@link android.view.View.OnLongClickListener}. + Phương pháp này được gọi khi người gọi chạm và giữ mục (khi ở chế độ cảm ứng), hoặc + lấy tiêu điểm vào một mục bằng phím điều hướng hoặc bi xoay và + nhấn và giữ phím "enter" phù hợp hoặc nhấn và giữ bi xoay (trong một giây).
+
onFocusChange()
+
Từ {@link android.view.View.OnFocusChangeListener}. + Phương pháp này được gọi khi người dùng điều hướng lên hoặc ra khỏi một mục bằng cách sử dụng các phím điều hướng hoặc bi xoay.
+
onKey()
+
Từ {@link android.view.View.OnKeyListener}. + Phương pháp này được gọi khi người dùng được lấy tiêu điểm vào một mục và nhấn hoặc nhả phím cứng trên thiết bị.
+
onTouch()
+
Từ {@link android.view.View.OnTouchListener}. + Phương pháp này được gọi khi người dùng thực hiện một hành động được coi như một sự kiện chạm, bao gồm nhấn, nhả, + hoặc bất kỳ động tác chuyển động nào trên màn hình (trong đường biên của mục đó).
+
onCreateContextMenu()
+
Từ {@link android.view.View.OnCreateContextMenuListener}. + Phương pháp này được gọi khi một Menu Ngữ cảnh đang được xây dựng (kết quả của một sự kiện "nhấp giữ" kéo dài). Xem phần thảo luận về + menu ngữ cảnh trong hướng dẫn dành cho nhà phát triển Menu +.
+
+ +

Những phương pháp này là phương pháp duy nhất nằm trong giao diện tương ứng của chúng. Để định nghĩa một trong những phương pháp này +và xử lý sự kiện của bạn, hãy triển khai giao diện lồng nhau trong Hoạt động của bạn hoặc định nghĩa nó thành một lớp vô danh. +Sau đó, chuyển một thực thể triển khai của bạn +tới phương pháp View.set...Listener() tương ứng. (Ví dụ, gọi +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()} +và chuyển cho nó triển khai {@link android.view.View.OnClickListener OnClickListener} của bạn.)

+ +

Ví dụ bên dưới cho biết cách đăng ký một đối tượng theo dõi khi nhấp cho một Nút.

+ +
+// Create an anonymous implementation of OnClickListener
+private OnClickListener mCorkyListener = new OnClickListener() {
+    public void onClick(View v) {
+      // do something when the button is clicked
+    }
+};
+
+protected void onCreate(Bundle savedValues) {
+    ...
+    // Capture our button from layout
+    Button button = (Button)findViewById(R.id.corky);
+    // Register the onClick listener with the implementation above
+    button.setOnClickListener(mCorkyListener);
+    ...
+}
+
+ +

Bạn cũng có thể thấy tiện hơn khi triển khai OnClickListener như một phần trong Hoạt động của mình. +Làm vậy sẽ tránh phải tải lớp bổ sung và phân bổ đối tượng. Ví dụ:

+
+public class ExampleActivity extends Activity implements OnClickListener {
+    protected void onCreate(Bundle savedValues) {
+        ...
+        Button button = (Button)findViewById(R.id.corky);
+        button.setOnClickListener(this);
+    }
+
+    // Implement the OnClickListener callback
+    public void onClick(View v) {
+      // do something when the button is clicked
+    }
+    ...
+}
+
+ +

Lưu ý rằng phương pháp gọi lại onClick() trong ví dụ trên không có giá trị +trả về, nhưng một số phương pháp đối tượng theo dõi sự kiện khác phải trả về một boolean. Lý do +này phụ thuộc vào sự kiện. Với số ít sự kiện thực hiện như vậy, sau đây là lý do:

+
    +
  • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()} - + Trả về một boolean cho biết bạn đã xử lý sự kiện và sự kiện không nên được tiếp tục hay không. + Cụ thể, trả về true để cho biết rằng bạn đã xử lý sự kiện và nó nên dừng ở đây; + trả về false nếu bạn chưa xử lý nó và/hoặc sự kiện sẽ tiếp tục đối với bất kỳ + đối tượng theo dõi khi nhấp nào khác.
  • +
  • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()} - + Trả về một boolean cho biết bạn đã xử lý sự kiện và sự kiện không nên được tiếp tục hay không. + Cụ thể, trả về true sẽ cho biết rằng bạn đã xử lý sự kiện và nó nên dừng ở đây; + trả về false nếu bạn chưa xử lý nó và/hoặc sự kiện sẽ tiếp tục đối với bất kỳ + đối tượng theo dõi trên phím nào khác.
  • +
  • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()} - + Trả về một boolean cho biết đối tượng theo dõi của bạn có xử lý sự kiện này hay không. Điều quan trọng đó là + sự kiện này có thể có nhiều hành động nối tiếp nhau. Vì vậy, nếu bạn trả về false khi + nhận được sự kiện hành động hướng xuống, bạn sẽ cho biết rằng mình chưa xử lý sự kiện và cũng + không quan tâm tới các hành động sau đó từ sự kiện này. Vì thế, bạn sẽ không bị gọi vì bất kỳ hành động nào khác + trong sự kiện, chẳng hạn như một cử chỉ ngón tay, hay sự kiện hành động cho sự kiện hướng lên.
  • +
+ +

Ghi nhớ rằng các sự kiện phím cứng luôn được chuyển tới Dạng xem đang được lấy tiêu điểm. Chúng được chuyển bắt đầu từ trên cùng +của phân cấp Dạng xem, rồi xuống dưới, tới khi chúng đến đích phù hợp. Nếu Dạng xem của bạn (hoặc con của Dạng xem) +hiện có tiêu điểm, khi đó bạn có thể thấy hành trình của sự kiện qua phương pháp {@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()}. Một cách khác để chụp lại các sự kiện phím bấm thông qua Dạng xem của mình, bạn cũng có thể nhận +tất cả sự kiện bên trong Hoạt động của mình bằng {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} +và {@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()}.

+ +

Đồng thời, khi nghĩ tới nhập liệu văn bản cho ứng dụng của bạn, hãy nhớ rằng nhiều thiết bị chỉ có các phương pháp +nhập liệu mềm. Những phương pháp như vậy không bắt buộc phải dựa trên phím bấm; một số có thể sử dụng nhập liệu bằng giọng nói, viết tay, v.v. Ngay cả khi +một phương pháp nhập liệu trình bày một giao diện như bàn phím, nó sẽ thường không kích khởi họ sự kiện +{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}. Bạn không nên +xây dựng UI yêu cầu kiểm soát các thao tác nhấn phím cụ thể trừ khi muốn giới hạn ứng dụng của bạn ở một số thiết bị +có bàn phím cứng. Cụ thể, không được dựa vào những phương pháp này để xác thực nhập liệu khi người dùng nhấn phím +quay lại; thay vào đó, hãy sử dụng các hành động như {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} để báo hiệu với +phương pháp nhập liệu bạn kỳ vọng ứng dụng của mình sẽ phản ứng như thế nào để nó có thể thay đổi UI của mình cho có nghĩa. Tránh các giả định +về cách thức hoạt động của một phương pháp nhập liệu mềm và chỉ tin tưởng để nó cung cấp văn bản đã có định dạng cho ứng dụng của mình.

+ +

Lưu ý: Android sẽ gọi bộ xử lý sự kiện trước rồi mới tới bộ xử lý +mặc định phù hợp từ định nghĩa lớp. Như thế, việc trả về true từ những đối tượng theo dõi sự kiện này sẽ dừng +việc lan truyền sự kiện tới đối tượng theo dõi sự kiện khác và cũng sẽ chặn phương pháp gọi lại tới +bộ xử lý sự kiện mặc định trong Dạng xem. Vì thế, hãy chắc chắn rằng bạn muốn chấm dứt sự kiện khi trả về true.

+ + +

Bộ xử lý Sự kiện

+ +

Nếu bạn đang xây dựng một thành phần tùy chỉnh từ Dạng xem, khi đó bạn sẽ có thể định nghĩa một vài phương pháp gọi lại +được sử dụng như bộ xử lý sự kiện mặc định. +Trong tài liệu về Thành phần +Tùy chỉnh, bạn sẽ tìm hiểu về một số phương pháp gọi lại phổ biến được sử dụng để xử lý sự kiện, +bao gồm:

+
    +
  • {@link android.view.View#onKeyDown} - Được gọi khi xảy ra một sự kiện phím bấm mới.
  • +
  • {@link android.view.View#onKeyUp} - Được gọi khi xảy ra một sự kiện phím bấm hướng lên.
  • +
  • {@link android.view.View#onTrackballEvent} - Được gọi khi xảy ra một sự kiện chuyển động bi xoay.
  • +
  • {@link android.view.View#onTouchEvent} - Được gọi khi xảy ra một sự kiện chuyển động màn hình cảm ứng.
  • +
  • {@link android.view.View#onFocusChanged} - Được gọi khi dạng xem có hoặc mất tiêu điểm.
  • +
+

Có một số phương pháp khác mà bạn cần lưu ý, chúng không thuộc lớp Dạng xem, +nhưng có thể tác động trực tiếp tới cách bạn có thể xử lý sự kiện. Vì thế, khi quản lý các sự kiện phức tạp hơn bên trong +một bố trí, hãy xét những phương pháp khác sau:

+
    +
  • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)} - Phương pháp này cho phép {@link + android.app.Activity} của bạn can thiệp vào tất cả sự kiện chạm trước khi chúng được phân phối tới cửa sổ.
  • +
  • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)} - Phương pháp này cho phép một {@link + android.view.ViewGroup} xem sự kiện khi chúng được phân phối tới Dạng xem con.
  • +
  • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)} - Gọi phương pháp + này trên Dạng xem mẹ để cho biết rằng nó sẽ không can thiệp vào các sự kiện chạm bằng {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)}.
  • +
+ +

Chế độ Cảm ứng

+

+Khi một người dùng đang điều hướng trong một giao diện người dùng bằng phím hướng hoặc bi xoay, cần +lấy tiêu điểm tới các mục có thể hành động (như nút) sao cho người dùng có thể thấy +mục nào sẽ chấp nhận nhập liệu. Tuy nhiên, nếu thiết bị có khả năng cảm ứng, và người dùng +bắt đầu tương tác với giao diện bằng cách chạm vào nó, khi đó không còn cần +tô sáng mục hay lấy tiêu điểm tới một Dạng xem cụ thể nữa. Do đó, có một chế độ cho +tương tác có tên là "chế độ cảm ứng." +

+

+Đối với thiết bị có khả năng cảm ứng, sau khi người dùng chạm vào màn hình, thiết bị +sẽ vào chế độ cảm ứng. Từ điểm này trở đi, chỉ những Dạng xem mà +{@link android.view.View#isFocusableInTouchMode} là đúng mới có thể lấy tiêu điểm, chẳng hạn như chế độ xem các widget chỉnh sửa văn bản. +Các Dạng xem chạm được, chẳng hạn như nút, sẽ không lấy được tiêu điểm khi chạm; chúng sẽ chỉ đơn giản +khởi chạy đối tượng theo dõi khi nhấp của mình khi được nhấn. +

+

+Bất cứ khi nào một người dùng nhấn phím hướng hoặc cuộn bằng bi xoay, thiết bị sẽ +thoát chế độ cảm ứng, và tìm một dạng xem để lấy tiêu điểm. Lúc này, người dùng có thể tiếp tục tương tác +với giao diện người dùng mà không chạm vào màn hình. +

+

+Trạng thái chế độ cảm ứng sẽ được duy trì trên toàn bộ hệ thống (tất cả cửa sổ và hoạt động). +Để truy vấn trạng thái hiện tại, bạn có thể gọi +{@link android.view.View#isInTouchMode} để xem liệu thiết bị có đang ở trong chế độ cảm ứng hay không. +

+ + +

Xử lý Tiêu điểm

+ +

Khuôn khổ sẽ xử lý chuyển động của tiêu điểm thường xuyên hồi đáp lại nhập liệu của người dùng. +Việc này bao gồm thay đổi tiêu điểm khi Dạng xem bị loại bỏ hoặc ẩn đi, hoặc khi Dạng xem +mới có sẵn. Dạng xem thể hiện sự sẵn sàng lấy tiêu điểm của chúng +thông qua phương pháp {@link android.view.View#isFocusable()}. Để thay đổi việc liệu một Dạng xem có thể lấy +tiêu điểm hay không, hãy gọi {@link android.view.View#setFocusable(boolean) setFocusable()}. Khi ở trong chế độ cảm ứng, +bạn có thể truy vấn xem Dạng xem có cho phép lấy tiêu điểm bằng {@link android.view.View#isFocusableInTouchMode()} hay không. +Bạn có thể thay đổi điều này bằng {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()}. +

+ +

Chuyển động tiêu điểm được dựa trên một giải thuật tìm kiếm đối tượng gần nhất theo +một hướng cho trước. Trong các trường hợp hiếm gặp, giải thuật mặc định có thể không khớp với +hành vi theo ý định của nhà phát triển. Trong những tình huống này, bạn có thể cung cấp +các khống chế rõ ràng với các thuộc tính XML sau trong tệp bố trí: +nextFocusDown, nextFocusLeft, nextFocusRight, và +nextFocusUp. Thêm một trong những thuộc tính này vào Dạng xem mà từ đó +tiêu điểm đang rời khỏi. Định nghĩa giá trị của thuộc tính là id của Dạng xem + cần được lấy tiêu điểm. Ví dụ:

+
+<LinearLayout
+    android:orientation="vertical"
+    ... >
+  <Button android:id="@+id/top"
+          android:nextFocusUp="@+id/bottom"
+          ... />
+  <Button android:id="@+id/bottom"
+          android:nextFocusDown="@+id/top"
+          ... />
+</LinearLayout>
+
+ +

Thông thưởng, trong bố trí thẳng đứng này, việc điều hướng lên từ Nút đầu tiên sẽ không +đi tới đâu hết và việc điều hướng xuống từ Nút thứ hai cũng vậy. Giờ thì khi Nút trên cùng +đã định nghĩa Nút dưới cùng là nextFocusUp (và ngược lại), tiêu điểm điều hướng sẽ +luân chuyển từ trên-xuống-dưới và dưới-lên-trên.

+ +

Nếu bạn muốn khai báo một Dạng xem là có thể lấy tiêu điểm trong UI của mình (thông thường thì không), +hãy thêm thuộc tính XML android:focusable vào Dạng xem, trong khai báo bố trí của bạn. +Đặt giá trị true. Bạn cũng có thể khai báo một Dạng xem +là có thể lấy tiêu điểm trong khi ở Chế độ Cảm ứng bằng android:focusableInTouchMode.

+

Để yêu cầu một Dạng xem cụ thể để lấy tiêu điểm, hãy gọi {@link android.view.View#requestFocus()}.

+

Để theo dõi các sự kiện tiêu điểm (được thông báo khi một Dạng xem nhận được hoặc mất tiêu điểm), hãy sử dụng +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}, +như được đề cập trong phần Đối tượng theo dõi Sự kiện bên trên.

+ + + + diff --git a/docs/html-intl/intl/vi/sdk/index.jd b/docs/html-intl/intl/vi/sdk/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..c4009f4c207a725b882c9ba11ed1d8b07af22ef8 --- /dev/null +++ b/docs/html-intl/intl/vi/sdk/index.jd @@ -0,0 +1,430 @@ +page.title=Tải xuống Android Studio và SDK Tools +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=Tải xuống Android IDE chính thức và bộ công cụ cho nhà phát triển để xây dựng ứng dụng cho điện thoại, máy tính bảng, thiết bị đeo được, TV chạy Android và nhiều thiết bị khác. + +@jd:body + + + + + + + +
+ + + + + + + + + +
+ +
 
+ + + +
+ +

Android Studio

+ +

IDE Android chính thức

+ +
    +
  • IDE Android Studio
  • +
  • Bộ công cụ SDK Android
  • +
  • Nền tảng Android 6.0 (Marshmallow)
  • +
  • Ảnh hệ thống trình mô phỏng Android 6.0 cùng các API của Google
  • +
+ +Download Android Studio
+ + +

+Để tải Android Studio hoặc bộ công cụ SDK độc lập, hãy truy cập developer.android.com/sdk/ +

+
+ + + + + + + +

Trình chỉnh sửa mã thông minh

+ +
+ +
+ +
+

Nằm ở cốt lõi của Android Studio là một trình biên tập mã thông minh có khả năng hoàn thành + mã, dựng lại và phân tích mã nâng cao.

+

Trình chỉnh sửa mã mạnh mẽ này sẽ giúp bạn trở thành một nhà phát triển ứng dụng Android năng suất hơn.

+
+ + + + + +

Các ví dụ mã và tích hợp GitHub

+ +
+ +
+ +
+

Các trình hướng dẫn dự án mới sẽ giúp bắt đầu một dự án mới dễ hơn bao giờ hết.

+ +

Bắt đầu các dự án bằng cách sử dụng mã mẫu cho các kiểu mẫu như ngăn kéo điều hướng và các trình tạo trang dạng xem, + và thậm chí còn nhập được các ví dụ mã của Google từ GitHub.

+
+ + + + +

Phát triển ứng dụng đa màn hình

+ +
+ +
+ +
+

Xây dựng ứng dụng cho điện thoại, máy tính bảng Android, Android Wear, + Android TV, Android Auto và Google Glass.

+

Với Dạng xem Dự án Android mới và hỗ trợ mô-đun trong Android Studio, việc + quản lý các dự án và tài nguyên ứng dụng trở nên dễ dàng hơn. +

+ + + + +

Các thiết bị ảo cho tất cả hình dạng và kích cỡ

+ +
+ +
+ +
+

Android Studio được cấu hình sẵn cùng một ảnh trình mô phỏng được tối ưu hóa.

+

Trình quản lý Thiết bị Ảo được cập nhật và hợp lý hoá sẽ cung cấp + các cấu hình thiết bị định nghĩa sẵn cho các thiết bị Android thông thường.

+
+ + + + +

+Phát triển xây dựng Android bằng Gradle

+ +
+ +
+ +
+

Tạo nhiều tệp APK cho ứng dụng Android của bạn với các tính năng khác nhau bằng cách sử dụng cùng dự án.

+

Quản lý phụ thuộc của ứng dụng bằng Maven.

+

Xây dựng APK từ Android Studio hoặc dòng lệnh.

+
+ + + + +

Tìm hiểu thêm về Android Studio

+
+ +Download + +
    +
  • Xây dựng Phiên bản Cộng đồng IntelliJ IDEA, IDE Java phổ biến của JetBrains.
  • +
  • Hệ thống xây dựng linh hoạt dựa trên Gradle.
  • +
  • Xây dựng các biến thể và khởi tạo nhiều APK.
  • +
  • Hỗ trợ mẫu mở rộng cho các Dịch vụ của Google và các loại thiết bị khác nhau.
  • +
  • Trình chỉnh sửa bố trí phong phú hỗ trợ chỉnh sửa chủ đề.
  • +
  • Bộ công cụ Lint để giải quyết các vấn đề về hiệu năng, khả năng sử dụng, tính tương thích với phiên bản và các vấn đề khác.
  • +
  • ProGuard và các khả năng ký ứng dụng.
  • +
  • Hỗ trợ tích hợp cho Nền tảng Đám mây của Google, giúp dễ dàng tích hợp Google Cloud + Nhắn tin và Công cụ Ứng dụng.
  • +
+ +

+Để biết thêm chi tiết về các tính năng có sẵn trong Android Studio, +hãy đọc hướng dẫn Nội dung Cơ bản về Android Studio.

+
+ + +

Nếu bạn đang sử dụng Eclipse với ADT, hãy lưu ý rằng hiện Android Studio là IDE chính thức +cho Android, vì thế bạn nên di chuyển sang Android Studio để nhận tất cả cập nhật +IDE mới nhất. Để được trợ giúp về di chuyển các dự án, +hãy xem phần Di chuyển sang Android +Studio.

+ + + + + + + +

Yêu cầu Hệ thống

+ +

Windows

+ +
    +
  • Microsoft® Windows® 8/7/Vista/2003 (32 hoặc 64-bit)
  • +
  • Tối thiểu 2 GB RAM, khuyến nghị 4 GB RAM
  • +
  • Dung lượng trống đĩa cứng 400 MB
  • +
  • Ít nhất 1 GB cho SDK Android, các ảnh hệ thống trình mô phỏng và bộ đệm ẩn
  • +
  • Độ phân giải màn hình tối thiểu 1280 x 800
  • +
  • Java Development Kit (JDK) 7
  • +
  • Tùy chọn dành cho trình mô phỏng tăng tốc: Bộ xử lý Intel® có hỗ trợ Intel® VT-x, Intel® EM64T +(Intel® 64), và tính năng Execute Disable (XD) Bit
  • +
+ + +

Mac OS X

+ +
    +
  • Mac® OS X® 10.8.5 hoặc cao hơn, lên tới 10.9 (Mavericks)
  • +
  • Tối thiểu 2 GB RAM, khuyến nghị 4 GB RAM
  • +
  • Dung lượng trống đĩa cứng 400 MB
  • +
  • Ít nhất 1 GB cho SDK Android, các ảnh hệ thống trình mô phỏng và bộ đệm ẩn
  • +
  • Độ phân giải màn hình tối thiểu 1280 x 800
  • +
  • Java Runtime Environment (JRE) 6
  • +
  • Java Development Kit (JDK) 7
  • +
  • Tùy chọn dành cho trình mô phỏng tăng tốc: Bộ xử lý Intel® có hỗ trợ Intel® VT-x, Intel® EM64T +(Intel® 64), và tính năng Execute Disable (XD) Bit
  • +
+ +

Trên Mac OS, hãy chạy Android Studio bằng Java Runtime Environment (JRE) 6 để dựng +phông chữ tối ưu. Khi đó, bạn có thể cấu hình dự án của mình để sử dụng Java Development Kit (JDK) 6 hoặc JDK 7.

+ + + +

Linux

+ +
    +
  • Máy tính bàn GNOME hoặc KDE
  • +
  • GNU C Library (glibc) 2.15 hoặc mới hơn
  • +
  • Tối thiểu 2 GB RAM, khuyến nghị 4 GB RAM
  • +
  • Dung lượng trống đĩa cứng 400 MB
  • +
  • Ít nhất 1 GB cho SDK Android, các ảnh hệ thống trình mô phỏng và bộ đệm ẩn
  • +
  • Độ phân giải màn hình tối thiểu 1280 x 800
  • +
  • Oracle® Java Development Kit (JDK) 7
  • +
+

Được thử nghiệm trên Ubuntu® 14.04, Trusty Tahr (có khả năng phân phối 64-bit khi chạy các ứng dụng +32-bit).

+ + + + +

Tùy chọn Tải xuống Khác

+ + diff --git a/docs/html-intl/intl/vi/sdk/installing/adding-packages.jd b/docs/html-intl/intl/vi/sdk/installing/adding-packages.jd new file mode 100644 index 0000000000000000000000000000000000000000..1c9072c746bf90660c56d4ed90d969f80fdd6e11 --- /dev/null +++ b/docs/html-intl/intl/vi/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=Thêm Gói SDK + +page.tags=trình quản lý sdk + +@jd:body + + + + +

+Theo mặc định, SDK Android không bao gồm mọi thứ bạn cần để bắt đầu phát triển. +SDK chia những công cụ, nền tảng và các thành phần khác vào các gói mà bạn có thể +tải xuống nếu cần bằng cách sử dụng +Trình quản lý SDK Android. +Vì vậy, trước khi có thể bắt đầu, có một vài gói bạn nên thêm vào SDK Android của mình.

+ +

Để bắt đầu thêm gói, hãy khởi chạy Trình quản lý SDK Android bằng một trong những cách sau:

+
    +
  • Trong Android Studio, nhấp vào Trình quản lý SDK + ở thanh công cụ.
  • +
  • Nếu bạn không đang sử dụng Android Studio: +
      +
    • Windows: Bấm đúp tệp SDK Manager.exe ở gốc của thư mục SDK + Android.
    • +
    • Mac/Linux: Mở một terminal và điều hướng đến thư mục tools/ ở vị trí + nơi cài đặt SDK Android, rồi chạy android sdk.
    • +
    +
  • +
+ +

Khi bạn mở Trình quản lý SDK lần đầu, một vài gói sẽ được chọn +theo mặc định. Để nguyên những gói được chọn này, nhưng hãy chắc chắn bạn có mọi thứ mình cần +để bắt đầu bằng cách làm theo những bước sau:

+ + +
    +
  1. +

    Tải công cụ SDK mới nhất

    + + + +

    Tối thiểu khi thiết đặt SDK Android, + bạn nên tải xuống những công cụ và nền tảng Android mới nhất:

    +
      +
    1. Mở thư mục Tools và chọn: +
        +
      • Công cụ SDK Android
      • +
      • Công cụ Nền tảng SDK Android
      • +
      • Công cụ Xây dựng SDK Android (phiên bản mới nhất)
      • +
      +
    2. +
    3. Mở thư mục Android X.X đầu tiên (phiên bản mới nhất) và chọn: +
        +
      • Nền tảng SDK
      • +
      • Một ảnh hệ thống cho trình mô phỏng, chẳng hạn như
        + Ảnh Hệ thống ARM EABI v7a
      • +
      +
    4. +
    +
  2. + +
  3. +

    Tải thư viện hỗ trợ cho các API bổ sung

    + + + +

    Thư viện Hỗ trợ Android + cung cấp một tập API mở rộng tương thích với hầu hết các phiên bản của Android.

    + +

    Mở thư mục Extras và chọn:

    +
      +
    • Kho Hỗ trợ Android
    • +
    • Thư viện Hỗ trợ Android
    • +
    + +

     

    +

     

    + +
  4. + + +
  5. +

    Tải dịch vụ Google Play để có nhiều API hơn nữa

    + + + +

    Để phát triển bằng các API của Google, bạn cần gói dịch vụ Google Play.

    +

    Mở thư mục Extras và chọn:

    +
      +
    • Kho Google
    • +
    • Dịch vụ Google Play
    • +
    + +

    Lưu ý: Các API dịch vụ Google Play không có sẵn trên tất cả + thiết bị dựa trên nền tảng Android, nhưng có sẵn trên tất cả thiết bị có Google Play Store. Để sử dụng những + API này trong trình mô phỏng Android, bạn cũng phải cài đặt ảnh hệ thống Google API + từ thư mục Android X.X mới nhất trong Trình quản lý SDK.

    +
  6. + + +
  7. +

    Cài đặt gói

    +

    Sau khi bạn đã chọn tất cả gói mong muốn, hãy tiếp tục cài đặt:

    +
      +
    1. Bấm Cài đặt X gói.
    2. +
    3. Trong cửa sổ tiếp theo, bấm đúp vào tên của từng gói ở bên trái + để chấp nhận thỏa thuận cấp phép cho từng gói.
    4. +
    5. Bấm Cài đặt.
    6. +
    +

    Tiến trình tải xuống được hiện ở dưới cùng của cửa sổ Trình quản lý SDK. + Không được thoát Trình quản lý SDK nếu không nó sẽ hủy bỏ việc tải xuống.

    +
  8. + +
  9. +

    Xây dựng một thứ gì đó!

    + +

    Với những gói trên có trong SDK Android của bạn, giờ bạn đã sẵn sàng để xây dựng ứng dụng +cho Android. Khi có sẵn các công cụ mới và API khác, chỉ cần khởi chạy Trình quản lý SDK + để tải xuống các gói mới cho SDK của bạn.

    + +

    Sau đây là một vài tùy chọn về cách bạn nên tiến hành:

    + +
    +
    +

    Bắt đầu

    +

    Nếu bạn mới làm quen với phát triển Android, hãy tìm hiểu những nội dung cơ bản của ứng dụng Androi bằng cách làm theo +hướng dẫn Xây dựng Ứng dụng Đầu tiên của bạn.

    + +
    +
    +

    Xây dựng cho thiết bị đeo được

    +

    Nếu bạn sẵn sàng bắt đầu xây dựng ứng dụng cho thiết bị đeo được Android, hãy xem hướng dẫn +Xây dựng Ứng dụng cho Android Wear.

    + +
    +
    +

    Sử dụng API của Google

    +

    Để bắt đầu sử dụng các API của Google, chẳng hạn như các dịch vụ Bản đồ hoặc +Chơi Trò chơi, hãy xem hướng dẫn +Thiết đặt Dịch vụ Google Play +.

    + +
    +
    + + +
  10. + +
+ + diff --git a/docs/html-intl/intl/vi/training/material/animations.jd b/docs/html-intl/intl/vi/training/material/animations.jd new file mode 100644 index 0000000000000000000000000000000000000000..e93c99d64508f1ff9efb54a993c978f494d19674 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=Định nghĩa Hoạt hình Tùy chỉnh + +@jd:body + + + + +

Hoạt hình theo phong cách material design phản hồi hành động của người dùng và cung cấp +tính liên tục trực quan khi người dùng tương tác với ứng dụng của bạn. Giao diện material cung cấp một số hoạt hình +mặc định cho các nút và chuyển tiếp hoạt động, và Android 5.0 (API mức 21) và cao hơn cho phép bạn tùy chỉnh +những hoạt hình này và tạo các hoạt hình mới:

+ +
    +
  • Phản hồi chạm
  • +
  • Lộ ra Vòng tròn
  • +
  • Chuyển tiếp hoạt động
  • +
  • Chuyển động cong
  • +
  • Thay đổi trạng thái xem
  • +
+ + +

Tùy chỉnh Phản hồi Chạm

+ +

Phản hồi chạm trong Material Design đưa ra một xác nhận trực quan tức thời tại +điểm tiếp xúc khi người dùng tương tác với các phần tử UI. Hoạt hình phản hồi chạm mặc định +cho nút sẽ sử dụng lớp {@link android.graphics.drawable.RippleDrawable} mới để chuyển tiếp +giữa các trạng thái khác nhau bằng hiệu ứng gợn sóng.

+ +

Trong hầu hết trường hợp, bạn nên áp dụng tính năng này trong tệp XML dạng xem của mình bằng cách chỉ định nền +dạng xem là:

+ +
    +
  • ?android:attr/selectableItemBackground cho gợn sóng có giới hạn.
  • +
  • ?android:attr/selectableItemBackgroundBorderless cho gợn sóng lan ra ngoài +dạng xem. Hiệu ứng sẽ được vẽ lên và được giới hạn bởi dạng xem mẹ gần nhất có nền +không rỗng.
  • +
+ +

Lưu ý: selectableItemBackgroundBorderless là một thuộc tính +mới được giới thiệu trong API mức 21.

+ + +

Hoặc, bạn có thể định nghĩa {@link android.graphics.drawable.RippleDrawable} +làm tài nguyên XML bằng cách sử dụng phần tử ripple.

+ +

Bạn có thể gán một màu cho các đối tượng {@link android.graphics.drawable.RippleDrawable}. Để thay đổi +màu phản hồi chạm mặc định, hãy sử dụng thuộc tính android:colorControlHighlight +của chủ đề.

+ +

Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp {@link +android.graphics.drawable.RippleDrawable}.

+ + +

Sử dụng Hiệu ứng Lộ ra

+ +

Hoạt hình lộ ra đảm bảo tính liên tục trực quan cho người dùng khi bạn hiện hoặc ẩn một nhóm phần tử +UI. Phương thức {@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()} cho phép bạn tạo hiệu ứng hoạt hình một vòng tròn cắt hình +để lộ ra hoặc ẩn một dạng xem.

+ +

Để lộ ra một dạng xem ẩn trước đó bằng hiệu ứng này:

+ +
+// previously invisible view
+View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the final radius for the clipping circle
+int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
+
+// create the animator for this view (the start radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
+
+// make the view visible and start the animation
+myView.setVisibility(View.VISIBLE);
+anim.start();
+
+ +

Để ẩn một dạng xem hiển thị trước đó bằng hiệu ứng này:

+ +
+// previously visible view
+final View myView = findViewById(R.id.my_view);
+
+// get the center for the clipping circle
+int cx = (myView.getLeft() + myView.getRight()) / 2;
+int cy = (myView.getTop() + myView.getBottom()) / 2;
+
+// get the initial radius for the clipping circle
+int initialRadius = myView.getWidth();
+
+// create the animation (the final radius is zero)
+Animator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
+
+// make the view invisible when the animation is done
+anim.addListener(new AnimatorListenerAdapter() {
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        super.onAnimationEnd(animation);
+        myView.setVisibility(View.INVISIBLE);
+    }
+});
+
+// start the animation
+anim.start();
+
+ + +

Tùy chỉnh Chuyển tiếp Hoạt động

+ + +
+
+ +
+
+

Hình 1 - Chuyển tiếp + với những phần tử chung.

+ Để phát lại phim, nhấp vào màn hình thiết bị +
+
+ +

Chuyển tiếp hoạt động trong các ứng dụng theo phong cách material design cung cấp kết nối trực quan giữa các trạng thái khác nhau +thông qua chuyển động và chuyển đổi giữa những phần tử hay dùng. Bạn có thể quy định hoạt hình tùy chỉnh cho +các chuyển tiếp ra vào và chuyển tiếp phần tử chung giữa các hoạt động.

+ +
    +
  • Chuyển tiếp vào xác định các dạng xem trong một hoạt động sẽ vào cảnh đó như thế nào. +Ví dụ, trong chuyển tiếp vào dạng nổ tung, dạng xem sẽ vào cảnh từ bên ngoài +và bay về phía chính giữa màn hình.
  • + +
  • Chuyển tiếp ra xác định các dạng xem trong một hoạt động sẽ ra khỏi cảnh như thế nào. Ví + dụ, trong chuyển tiếp ra kiểu nổ tung, dạng xem sẽ ra khỏi cảnh từ +trung tâm.
  • + +
  • Chuyển tiếp phần tử chung xác định các dạng xem chung giữa hai hoạt động +sẽ chuyển tiếp như thế nào giữa những hoạt động này. Ví dụ, nếu hai hoạt động có cùng +hình ảnh ở các vị trí và kích cỡ khác nhau, chuyển tiếp phần tử chung changeImageTransform +sẽ thể hiện và co giãn hình ảnh một cách mượt mà giữa những hoạt động này.
  • +
+ +

Android 5.0 (API mức 21) hỗ trợ những chuyển tiếp ra vào sau:

+ +
    +
  • nổ tung - Di chuyển các dạng xem vào hoặc ra khỏi chính giữa cảnh.
  • +
  • trượt - Di chuyển các dạng xem vào hoặc ra từ một trong các mép của cảnh.
  • +
  • mờ dần - Thêm hoặc gỡ bỏ dạng xem khỏi cảnh bằng cách thay đổi độ mờ đục của nó.
  • +
+ +

Bất cứ chuyển tiếp nào mở rộng lớp {@link android.transition.Visibility} đều được hỗ trợ +như một chuyển tiếp vào hoặc ra. Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp +{@link android.transition.Transition}.

+ +

Android 5.0 (API mức 21) cũng hỗ trợ những chuyển tiếp phần tử chung này:

+ +
    +
  • changeBounds - Tạo hiệu ứng hoạt hình các thay đổi trong giới hạn bố trí của dạng xem mục tiêu.
  • +
  • changeClipBounds - Tạo hiệu ứng hoạt hình các thay đổi trong giới hạn cắt hình của dạng xem mục tiêu.
  • +
  • changeTransform - Tạo hiệu ứng hoạt hình các thay đổi về co giãn và xoay dạng xem mục tiêu.
  • +
  • changeImageTransform - Tạo hiệu ứng hoạt hình các thay đổi về kích cỡ và co giãn của ảnh mục tiêu.
  • +
+ +

Khi bạn cho phép chuyển tiếp hoạt động trong ứng dụng của mình, chuyển tiếp mờ dần chéo cross-fading +mặc định sẽ được kích hoạt giữa các hoạt động ra vào.

+ + +

Hình 2 - Chuyển tiếp cảnh với một phần tử chung. +

+ +

Quy định chuyển tiếp tùy chỉnh

+ +

Trước tiên, cho phép chuyển tiếp nội dung cửa sổ bằng thuộc tính android:windowContentTransitions +khi bạn định nghĩa một kiểu kế thừa từ chủ đề material. Bạn cũng có thể quy định chuyển tiếp +ra, vào và phần tử chung trong định nghĩa kiểu của mình:

+ +
+<style name="BaseAppTheme" parent="android:Theme.Material">
+  <!-- enable window content transitions -->
+  <item name="android:windowContentTransitions">true</item>
+
+  <!-- specify enter and exit transitions -->
+  <item name="android:windowEnterTransition">@transition/explode</item>
+  <item name="android:windowExitTransition">@transition/explode</item>
+
+  <!-- specify shared element transitions -->
+  <item name="android:windowSharedElementEnterTransition">
+    @transition/change_image_transform</item>
+  <item name="android:windowSharedElementExitTransition">
+    @transition/change_image_transform</item>
+</style>
+
+ +

Chuyển tiếp change_image_transform trong ví dụ này được định nghĩa như sau:

+ +
+<!-- res/transition/change_image_transform.xml -->
+<!-- (see also Shared Transitions below) -->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+  <changeImageTransform/>
+</transitionSet>
+
+ +

Phần tử changeImageTransform tương ứng với lớp +{@link android.transition.ChangeImageTransform}. Để biết thêm thông tin, hãy xem tài liệu tham khảo API +cho {@link android.transition.Transition}.

+ +

Thay vào đó, để cho phép chuyển tiếp nội dung cửa sổ trong mã của bạn, hãy gọi phương thức +{@link android.view.Window#requestFeature Window.requestFeature()}:

+ +
+// inside your activity (if you did not enable transitions in your theme)
+getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+
+// set an exit transition
+getWindow().setExitTransition(new Explode());
+
+ +

Để quy định chuyển tiếp trong mã của bạn, hãy gọi những phương thức này bằng đối tượng {@link +android.transition.Transition}:

+ +
    +
  • {@link android.view.Window#setEnterTransition Window.setEnterTransition()}
  • +
  • {@link android.view.Window#setExitTransition Window.setExitTransition()}
  • +
  • {@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()}
  • +
  • {@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}
  • +
+ +

Phương thức {@link android.view.Window#setExitTransition setExitTransition()} và {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} định nghĩa +chuyển tiếp ra cho hoạt động gọi. Phương thức {@link android.view.Window#setEnterTransition +setEnterTransition()} và {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} định nghĩa chuyển tiếp vào cho hoạt động được gọi.

+ +

Để có đầy đủ hiệu ứng của một chuyển tiếp, bạn phải cho phép chuyển tiếp nội dung cửa sổ trên cả hoạt động +gọi và được gọi. Nếu không, hoạt động gọi sẽ bắt đầu chuyển tiếp ra, +nhưng khi đó bạn sẽ thấy chuyển tiếp cửa sổ (như co giãn và mờ dần).

+ +

Để bắt đầu một chuyển tiếp vào ngay khi có thể, hãy sử dụng phương thức +{@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} +trên hoạt động được gọi. Nó cho phép bạn có chuyển tiếp vào ấn tượng hơn.

+ +

Bắt đầu một hoạt động bằng chuyển tiếp

+ +

Nếu bạn cho phép chuyển tiếp và đặt chuyển tiếp ra cho một hoạt động, chuyển tiếp sẽ được +kích hoạt khi bạn khởi chạy một hoạt động khác như sau:

+ +
+startActivity(intent,
+              ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
+
+ +

Nếu bạn đặt một chuyển tiếp vào cho hoạt động thứ hai, chuyển tiếp này cũng được kích hoạt khi hoạt động +bắt đầu. Để vô hiệu hoá chuyển tiếp khi bạn bắt đầu một hoạt động khác, hãy cung cấp +một nhóm tùy chọn null.

+ +

Bắt đầu một hoạt động bằng một phần tử chung

+ +

Để tạo một hoạt hình chuyển tiếp màn hình giữa hai hoạt động có một phần tử chung:

+ +
    +
  1. Cho phép chuyển tiếp nội dung cửa sổ trong chủ đề của bạn.
  2. +
  3. Quy định một chuyển tiếp phần tử chung trong kiểu của bạn.
  4. +
  5. Định nghĩa chuyển tiếp của bạn dưới dạng một tài nguyên XML.
  6. +
  7. Gán một tên chung cho các phần tử chung ở cả hai bố trí bằng thuộc tính + android:transitionName.
  8. +
  9. Sử dụng phương thức {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()}.
  10. +
+ +
+// get the element that receives the click event
+final View imgContainerView = findViewById(R.id.img_container);
+
+// get the common element for the transition in this activity
+final View androidRobotView = findViewById(R.id.image_small);
+
+// define a click listener
+imgContainerView.setOnClickListener(new View.OnClickListener() {
+    @Override
+    public void onClick(View view) {
+        Intent intent = new Intent(this, Activity2.class);
+        // create the transition animation - the images in the layouts
+        // of both activities are defined with android:transitionName="robot"
+        ActivityOptions options = ActivityOptions
+            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
+        // start the new activity
+        startActivity(intent, options.toBundle());
+    }
+});
+
+ +

Đối với các dạng xem động dùng chung mà bạn khởi tạo trong mã của mình, hãy sử dụng phương thức +{@link android.view.View#setTransitionName View.setTransitionName()} để quy định một +tên phần tử chung trong cả hai hoạt động.

+ +

Để đảo ngược hoạt hình chuyển tiếp cảnh khi bạn kết thúc hoạt động thứ hai, hãy gọi phương thức +{@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} +thay vì {@link android.app.Activity#finish Activity.finish()}.

+ +

Bắt đầu một hoạt động bằng nhiều phần tử chung

+ +

Để tạo một hoạt hình chuyển tiếp cảnh giữa hai hoạt động có nhiều hơn một phần tử +chung, hãy định nghĩa các phần tử chung trong cả hai bố trí bằng thuộc tính android:transitionName + (hoặc sử dụng phương thức {@link android.view.View#setTransitionName View.setTransitionName()} +trong cả hai hoạt động), và tạo một đối tượng {@link android.app.ActivityOptions} như sau:

+ +
+ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
+        Pair.create(view1, "agreedName1"),
+        Pair.create(view2, "agreedName2"));
+
+ + +

Sử dụng Chuyển động Cong

+ +

Hoạt hình theo phong cách material design phụ thuộc vào đường cong làm mẫu hình nội suy thời gian +và chuyển động không gian. Với Android 5.0 (API mức 21) trở lên, bạn có thể định nghĩa đường cong định thời tùy chỉnh và +mẫu hình chuyển động cong cho hoạt hình.

+ +

Lớp {@link android.view.animation.PathInterpolator} là một hàm nội suy mới dựa trên đường cong +Bézier hoặc đối tượng {@link android.graphics.Path}. Hàm nội suy này quy định một đường cong chuyển động +trong một hình vuông 1x1, với các điểm neo tại (0,0) và (1,1) cùng các điểm kiểm soát được quy định bằng cách sử dụng +các tham đối của hàm dựng. Bạn cũng có thể định nghĩa một hàm nội suy đường dẫn dưới dạng tài nguyên XML:

+ +
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1"/>
+
+ +

Hệ thống cung cấp tài nguyên XML cho ba đường cong cơ bản trong đặc tả +material design:

+ +
    +
  • @interpolator/fast_out_linear_in.xml
  • +
  • @interpolator/fast_out_slow_in.xml
  • +
  • @interpolator/linear_out_slow_in.xml
  • +
+ +

Bạn có thể chuyển một đối tượng {@link android.view.animation.PathInterpolator} tới phương thức {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()}.

+ +

Lớp {@link android.animation.ObjectAnimator} có các hàm dựng mới cho phép bạn tạo hiệu ứng hoạt hình +cho các tọa độ dọc theo một đường dẫn bằng hai hoặc nhiều thuộc tính đồng thời. Ví dụ, trình tạo hoạt hình sau +sử dụng một đối tượng {@link android.graphics.Path} để tạo hiệu ứng hoạt hình cho thuộc tính X và Y của một dạng xem:

+ +
+ObjectAnimator mAnimator;
+mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
+...
+mAnimator.start();
+
+ + +

Tạo Hiệu ứng Hoạt hình Thay đổi Trạng thái Xem

+ +

Lớp {@link android.animation.StateListAnimator} cho phép bạn định nghĩa trình tạo hoạt hình để chạy khi +trạng thái của dạng xem thay đổi. Ví dụ sau cho biết cách định nghĩa một {@link +android.animation.StateListAnimator} dưới dạng tài nguyên XML:

+ +
+<!-- animate the translationZ property of a view when pressed -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_pressed="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="@android:integer/config_shortAnimTime"
+        android:valueTo="2dp"
+        android:valueType="floatType"/>
+        <!-- you could have other objectAnimator elements
+             here for "x" and "y", or other properties -->
+    </set>
+  </item>
+  <item android:state_enabled="true"
+    android:state_pressed="false"
+    android:state_focused="true">
+    <set>
+      <objectAnimator android:propertyName="translationZ"
+        android:duration="100"
+        android:valueTo="0"
+        android:valueType="floatType"/>
+    </set>
+  </item>
+</selector>
+
+ +

Để gắn kèm hoạt hình trạng thái dạng xem tùy chỉnh vào một dạng xem, hãy định nghĩa một trình tạo hoạt hình bằng cách sử dụng phần tử +selector trong một tệp tài nguyên XML như trong ví dụ này, và gán nó cho dạng xem +của bạn bằng thuộc tính android:stateListAnimator. Để gán một trình tạo hoạt hình danh sách trạng thái +cho một dạng xem trong mã của bạn, hãy sử dụng phương thức {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()} và gán trình tạo hoạt hình cho dạng xem của bạn bằng phương thức +{@link android.view.View#setStateListAnimator View.setStateListAnimator()}.

+ +

Khi chủ đề của bạn mở rộng ra chủ đề material, các nút sẽ có hoạt hình Z theo mặc định. Để tránh hành vi +này trong nút của bạn, hãy đặt thuộc tính android:stateListAnimator thành +@null.

+ +

Lớp {@link android.graphics.drawable.AnimatedStateListDrawable} cho phép bạn tạo các nội dung vẽ được +để hiển thị hoạt hình giữa các thay đổi trạng thái của dạng xem được liên kết. Một số widget hệ thống trong +Android 5.0 sử dụng những hoạt hình này theo mặc định. Ví dụ sau cho biết cách +cách định nghĩa {@link android.graphics.drawable.AnimatedStateListDrawable} dưới dạng tài nguyên XML:

+ +
+<!-- res/drawable/myanimstatedrawable.xml -->
+<animated-selector
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- provide a different drawable for each state-->
+    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
+        android:state_pressed="true"/>
+    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
+        android:state_focused="true"/>
+    <item android:id="@id/default"
+        android:drawable="@drawable/drawableD"/>
+
+    <!-- specify a transition -->
+    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
+        <animation-list>
+            <item android:duration="15" android:drawable="@drawable/dt1"/>
+            <item android:duration="15" android:drawable="@drawable/dt2"/>
+            ...
+        </animation-list>
+    </transition>
+    ...
+</animated-selector>
+
+ + +

Tạo Hiệu ứng Hoạt hình Nội dung vẽ được Véc-tơ

+ +

Nội dung vẽ được Véc-tơ sẽ co giãn được +mà không làm mất độ sắc nét. Lớp {@link android.graphics.drawable.AnimatedVectorDrawable} +cho phép bạn tạo hiệu ứng hoạt hình các thuộc tính của nội dung vẽ được véc-tơ.

+ +

Thường thì bạn định nghĩa nội dung vẽ được véc-tơ hoạt hình theo ba tệp XML:

+ +
    +
  • Nội dung vẽ được véc-tơ với phần tử <vector> trong +res/drawable/
  • +
  • Nội dung vẽ được véc-tơ hoạt hình với phần tử <animated-vector> trong +res/drawable/
  • +
  • Một hoặc nhiều trình tạo hoạt hình đối tượng với phần tử <objectAnimator> trong +res/anim/
  • +
+ +

Nội dung vẽ được véc-tơ hoạt hình có thể tạo hiệu ứng hoạt hình các thuộc tính của phần tử <group> và +<path>. Phần tử <group> định nghĩa một bộ +đường dẫn hoặc nhóm phụ và phần tử <path> định nghĩa các đường dẫn sẽ được vẽ.

+ +

Khi định nghĩa một nội dung vẽ được véc-tơ mà bạn muốn tạo hiệu ứng hoạt hình, hãy sử dụng thuộc tính android:name +để gán một tên duy nhất cho các nhóm và đường dẫn, sao cho bạn có thể tham chiếu tới chúng từ các định nghĩa +trình tạo hoạt hình của mình. Ví dụ:

+ +
+<!-- res/drawable/vectordrawable.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:width="64dp"
+    android:viewportHeight="600"
+    android:viewportWidth="600">
+    <group
+        android:name="rotationGroup"
+        android:pivotX="300.0"
+        android:pivotY="300.0"
+        android:rotation="45.0" >
+        <path
+            android:name="v"
+            android:fillColor="#000000"
+            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+    </group>
+</vector>
+
+ +

Định nghĩa nội dung vẽ được véc-tơ hoạt hình sẽ tham chiếu tới các nhóm và đường dẫn trong nội dung vẽ được véc-tơ theo +tên của chúng:

+ +
+<!-- res/drawable/animvectordrawable.xml -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:drawable="@drawable/vectordrawable" >
+    <target
+        android:name="rotationGroup"
+        android:animation="@anim/rotation" />
+    <target
+        android:name="v"
+        android:animation="@anim/path_morph" />
+</animated-vector>
+
+ +

Định nghĩa hoạt hình biểu diễn các đối tượng {@link android.animation.ObjectAnimator} hoặc {@link +android.animation.AnimatorSet}. Trình tạo hoạt hình đầu tiên trong ví dụ này sẽ xoay nhóm +đối tượng 360 độ:

+ +
+<!-- res/anim/rotation.xml -->
+<objectAnimator
+    android:duration="6000"
+    android:propertyName="rotation"
+    android:valueFrom="0"
+    android:valueTo="360" />
+
+ +

Trình tạo hoạt hình thứ hai trong ví dụ này sẽ đổi dạng đường dẫn của nội dung vẽ được véc-tơ từ hình này sang +hình khác. Cả hai đường dẫn đều phải tương thích với việc đổi dạng: chúng phải có cùng số lệnh +và cùng số lượng tham số cho từng lệnh.

+ +
+<!-- res/anim/path_morph.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <objectAnimator
+        android:duration="3000"
+        android:propertyName="pathData"
+        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
+        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
+        android:valueType="pathType" />
+</set>
+
+ +

Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho {@link +android.graphics.drawable.AnimatedVectorDrawable}.

diff --git a/docs/html-intl/intl/vi/training/material/compatibility.jd b/docs/html-intl/intl/vi/training/material/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..65e813129704f4a74ca2abfc52b2772c19420ee1 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=Duy trì Tính tương thích + +@jd:body + + + + +

Một số tính năng của Material Design như chủ đề Material và chuyển tiếp hoạt động tùy chỉnh +chỉ sẵn có trên Android 5.0 (API mức 21) trở lên. Tuy nhiên, bạn có thể thiết kế ứng dụng của mình để tận dụng +những tính năng này khi chạy trên thiết bị hỗ trợ material design mà vẫn tương thích +với thiết bị đang chạy các bản phát hành Android trước đây.

+ + +

Định nghĩa Kiểu Thay thế

+ +

Bạn có thể cấu hình ứng dụng của mình để sử dụng chủ đề material trên những thiết bị hỗ trợ và chuyển về +chủ đề cũ hơn trên những thiết bị đang chạy phiên bản Android cũ hơn:

+ +
    +
  1. Định nghĩa một chủ đề kế thừa từ một chủ đề cũ hơn (như Holo) trong + res/values/styles.xml.
  2. +
  3. Định nghĩa một chủ đề với cùng tên và kế thừa chủ đề material trong + res/values-v21/styles.xml.
  4. +
  5. Đặt chủ đề này làm chủ đề cho ứng dụng của bạn trong tệp bản kê khai.
  6. +
+ +

Lưu ý: +Nếu ứng dụng của bạn sử dụng chủ đề material nhưng không cung cấp chủ đề thay thế bằng cách này, +ứng dụng của bạn sẽ không chạy trên phiên bản Android trước 5.0. +

+ + +

Cung cấp Bố trí Thay thế

+ +

Nếu những bố trí mà bạn thiết kế theo hướng dẫn về material design không sử dụng bất cứ +thuộc tính XML mới nào được giới thiệu trong Android 5.0 (API mức 21), chúng sẽ hoạt động trên các phiên bản +Android trước đó. Nếu không, bạn có thể cung cấp bố trí thay thế. Bạn cũng có thể cung cấp +bố trí thay thế để tùy chỉnh diện mạo ứng dụng của mình trên các phiên bản Android cũ hơn.

+ +

Tạo tệp bố trí của bạn cho Android 5.0 (API mức 21) bên trong res/layout-v21/ và +tệp bố trí thay thế của bạn cho các phiên bản Android cũ hơn trong res/layout/. +Ví dụ, res/layout/my_activity.xml là một bố trí thay thế cho +res/layout-v21/my_activity.xml.

+ +

Để tránh lặp mã, hãy định nghĩa kiểu của bạn bên trong res/values/, sửa đổi +các kiểu trong res/values-v21/ cho các API mới và sử dụng kế thừa kiểu, định nghĩa +kiểu cơ bản trong res/values/ và kế thừa từ những kiểu trong res/values-v21/.

+ + +

Sử dụng Thư viện Hỗ trợ

+ +

Thư viện Hỗ trợ v7 +r21 và cao hơn gồm những tính năng material design sau:

+ + + +

Widget hệ thống

+ +

Chủ đề Theme.AppCompat cung cấp các kiểu phong cách material design cho những widget này:

+ +
    +
  • {@link android.widget.EditText}
  • +
  • {@link android.widget.Spinner}
  • +
  • {@link android.widget.CheckBox}
  • +
  • {@link android.widget.RadioButton}
  • +
  • {@link android.support.v7.widget.SwitchCompat}
  • +
  • {@link android.widget.CheckedTextView}
  • +
+ +

Bảng màu

+ +

Để có được các kiểu phong cách material design và tùy chỉnh bảng màu bằng Thư viện Hỗ trợ v7 +của Android, hãy áp dụng một trong các chủ đề Theme.AppCompat:

+ +
+<!-- extend one of the Theme.AppCompat themes -->
+<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
+    <!-- customize the color palette -->
+    <item name="colorPrimary">@color/material_blue_500</item>
+    <item name="colorPrimaryDark">@color/material_blue_700</item>
+    <item name="colorAccent">@color/material_green_A200</item>
+</style>
+
+ +

Danh sách và Thẻ

+ +

Widget {@link android.support.v7.widget.RecyclerView} và {@link +android.support.v7.widget.CardView} sẵn có trong các phiên bản Android cũ hơn thông qua +Thư viện Hỗ trợ v7 của Android với những hạn chế sau:

+
    +
  • {@link android.support.v7.widget.CardView} quay lại triển khai đổ bóng theo lập trình + bằng cách sử dụng phần đệm bổ sung.
  • +
  • {@link android.support.v7.widget.CardView} không cắt hình những dạng xem con của nó có giao cắt với + các góc bo tròn.
  • +
+ + +

Phụ thuộc

+ +

Để sử dụng những tính năng này trong các phiên bản Android trước 5.0 (API mức 21), hãy thêm +Thư viện Hỗ trợ v7 của Android vào dự án của bạn như một Phần phụ thuộc Gradle:

+ +
+dependencies {
+    compile 'com.android.support:appcompat-v7:21.0.+'
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
+ + +

Kiểm tra Phiên bản Hệ thống

+ +

Những tính năng sau chỉ sẵn có trong Android 5.0 (API mức 21) trở lên:

+ +
    +
  • Chuyển tiếp hoạt động
  • +
  • Phản hồi chạm
  • +
  • Lộ ra hoạt hình
  • +
  • Hoạt hình dựa trên đường dẫn
  • +
  • Nội dung vẽ được véc-tơ
  • +
  • Nhuộm màu nội dung vẽ được
  • +
+ +

Để duy trì tính tương thích với các phiên bản Android cũ hơn, hãy kiểm tra {@link +android.os.Build.VERSION#SDK_INT version} vào thời gian chạy trước khi bạn gọi ra API cho bất cứ tính năng nào +sau đây:

+ +
+// Check if we're running on Android 5.0 or higher
+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+    // Call some material design APIs here
+} else {
+    // Implement this feature without material design
+}
+
+ +

Lưu ý: Để quy định ứng dụng của bạn hỗ trợ những phiên bản Android nào, +hãy sử dụng thuộc tính android:minSdkVersionandroid:targetSdkVersion +trong tệp bản kê khai của bạn. Để sử dụng các tính năng của material design trong Android 5.0, hãy đặt +thuộc tính android:targetSdkVersion thành 21. Để biết thêm thông tin, hãy xem hướng dẫn + <uses-sdk> API +.

diff --git a/docs/html-intl/intl/vi/training/material/drawables.jd b/docs/html-intl/intl/vi/training/material/drawables.jd new file mode 100644 index 0000000000000000000000000000000000000000..175e77d629e8ab5a5e125eebd4ab3a374f181929 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=Làm việc với Nội dung vẽ được + +@jd:body + + + +

Những khả năng sau của nội dung vẽ được giúp bạn triển khai phong cách material design trong các ứng dụng của mình:

+ +
    +
  • Nhuộm màu nội dung vẽ được
  • +
  • Trích xuất màu nổi bật
  • +
  • Nội dung vẽ được véc-tơ
  • +
+ +

Bài học này cho bạn biết cách sử dụng những tính năng này trong ứng dụng của mình.

+ + +

Nhuộm màu Tài nguyên Vẽ được

+ +

Với Android 5.0 (API mức 21) và cao hơn, bạn có thể nhuộm màu ảnh bitmap và ảnh chín miếng được định nghĩa làm +mặt nạ alpha. Bạn có thể nhuộm chúng bằng tài nguyên màu hoặc những thuộc tính chủ đề nhằm phân giải thành các tài nguyên +màu (ví dụ, ?android:attr/colorPrimary). Thông thường, bạn chỉ tạo những tài sản này +một lần và tô màu tự động cho chúng để khớp với chủ đề của mình.

+ +

Bạn có thể áp dụng một màu nhuộm cho đối tượng {@link android.graphics.drawable.BitmapDrawable} hoặc {@link +android.graphics.drawable.NinePatchDrawable} bằng phương thức {@code setTint()}. Bạn cũng có thể +đặt màu nhuộm và chế độ trong bố trí của mình bằng các thuộc tính android:tint và +android:tintMode.

+ + +

Trích xuất Màu Nổi bật từ một Hình ảnh

+ +

Thư viện Hỗ trợ Android r21 và cao hơn bao gồm lớp {@link +android.support.v7.graphics.Palette}, cho phép bạn trích xuất màu nổi bật từ một hình ảnh. +Lớp này trích xuất những màu nổi bật sau:

+ +
    +
  • Rực rỡ
  • +
  • Tối rực rỡ
  • +
  • Sáng rực rỡ
  • +
  • Lặng
  • +
  • Tối lặng
  • +
  • Sáng lặng
  • +
+ +

Để trích xuất những màu này, hãy chuyển một đối tượng {@link android.graphics.Bitmap} cho phương thức tĩnh +{@link android.support.v7.graphics.Palette#generate Palette.generate()} trong +luồng chạy ngầm nơi bạn tải hình ảnh của mình. Nếu bạn không thể sử dụng luồng đó, hãy gọi phương thức +{@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} và +cung cấp một trình nghe.

+ +

Bạn có thể truy xuất màu nổi bật từ hình ảnh bằng cách sử dụng các phương thức getter trong lớp +Palette chẳng hạn như Palette.getVibrantColor.

+ +

Để sử dụng lớp {@link android.support.v7.graphics.Palette} trong dự án của mình, hãy thêm +Phần phụ thuộc Gradle sau vào +mô-đun ứng dụng của bạn:

+ +
+dependencies {
+    ...
+    compile 'com.android.support:palette-v7:21.0.0'
+}
+
+ +

Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp +{@link android.support.v7.graphics.Palette}.

+ + +

Tạo Nội dung vẽ được Véc-tơ

+ + + +
+

Video

+

Đồ họa Véc-tơ Android

+
+
+ +

Trong Android 5.0 (API mức 21) và cao hơn, bạn có thể định nghĩa nội dung vẽ được véc-tơ co giãn mà không làm mất +độ sắc nét. Bạn chỉ cần một tệp tài sản cho một hình ảnh véc-tơ, đối lập với tệp tài sản cho +từng mật độ màn hình trong trường hợp hình ảnh bitmap. Để tạo một hình ảnh véc-tơ, bạn định nghĩa chi tiết +của hình ảnh trong phần tử XML <vector>.

+ +

Ví dụ sau định nghĩa hình ảnh véc-tơ có hình một trái tim:

+ +
+<!-- res/drawable/heart.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    <!-- intrinsic size of the drawable -->
+    android:height="256dp"
+    android:width="256dp"
+    <!-- size of the virtual canvas -->
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+
+  <!-- draw a path -->
+  <path android:fillColor="#8fff"
+      android:pathData="M20.5,9.5
+                        c-1.955,0,-3.83,1.268,-4.5,3
+                        c-0.67,-1.732,-2.547,-3,-4.5,-3
+                        C8.957,9.5,7,11.432,7,14
+                        c0,3.53,3.793,6.257,9,11.5
+                        c5.207,-5.242,9,-7.97,9,-11.5
+                        C25,11.432,23.043,9.5,20.5,9.5z" />
+</vector>
+
+ +

Hình ảnh véc-tơ được biểu diễn trong Android dưới dạng đối tượng {@link android.graphics.drawable.VectorDrawable} +. Để biết thêm thông tin về cú pháp pathData, hãy xem tham chiếu Đường dẫn SVG. Để biết thêm thông tin +về việc tạo hiệu ứng hoạt hình các thuộc tính của nội dung vẽ được véc-tơ, hãy xem +Tạo Hiệu ứng Hoạt hình Nội dung vẽ được Véc-tơ.

diff --git a/docs/html-intl/intl/vi/training/material/get-started.jd b/docs/html-intl/intl/vi/training/material/get-started.jd new file mode 100644 index 0000000000000000000000000000000000000000..9e612ad10c07c25326a611551615ec98517ac695 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=Bắt đầu + +@jd:body + + + + +

Để tạo ứng dụng với material design:

+ +
    +
  1. + Xem phần đặc tả phong cách material design.
  2. +
  3. + Áp dụng chủ đề material cho ứng dụng của bạn.
  4. +
  5. + Tạo bố trí của bạn theo hướng dẫn về material design.
  6. +
  7. + Quy định độ cao cho dạng xem của bạn để đổ bóng.
  8. +
  9. + Sử dụng widget hệ thống cho danh sách và thẻ.
  10. +
  11. + Tùy chỉnh hoạt hình trong ứng dụng của bạn.
  12. +
+ +

Duy trì tính tương thích ngược

+ +

Bạn có thể thêm nhiều tính năng material design vào ứng dụng của mình trong khi vẫn duy trì tính tương thích với +các phiên bản Android trước 5.0. Để biết thêm thông tin, hãy xem phần +Duy trì Tính tương thích.

+ +

Cập nhật ứng dụng của bạn với material design

+ +

Để cập nhật một ứng dụng hiện tại bằng material design, hãy cập nhật các bố trí của bạn theo +hướng dẫn về material design. Cũng nhớ kết hợp chiều sâu, phản hồi chạm và +hoạt hình.

+ +

Tạo ứng dụng mới với material design

+ +

Nếu bạn tạo một ứng dụng mới với các tính năng của material design, hướng dẫn về material design cung cấp cho bạn một +khuôn khổ thiết kế súc tích. Làm theo hướng dẫn đó và sử dụng chức năng mới trong khuôn khổ Android +để thiết kế và phát triển ứng dụng của bạn.

+ + +

Áp dụng Chủ đề Material

+ +

Để áp dụng chủ đề material trong ứng dụng của mình, hãy quy định một kiểu kế thừa từ +android:Theme.Material:

+ +
+<!-- res/values/styles.xml -->
+<resources>
+  <!-- your theme inherits from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- theme customizations -->
+  </style>
+</resources>
+
+ +

Chủ đề material cung cấp các widget hệ thống được cập nhật để bạn có thể đặt bảng màu và +hoạt hình mặc định cho phản hồi chạm và chuyển tiếp hoạt động. Để biết thêm chi tiết, hãy xem phần +Sử dụng Chủ đề Material.

+ + +

Thiết kế Bố trí của Bạn

+ +

Bên cạnh việc áp dụng và tùy chỉnh chủ đề material, bố trí của bạn cần tuân thủ +hướng dẫn về material design. Khi thiết kế +bố trí của bạn, hãy đặc biệt chú ý tới điều sau đây:

+ +
    +
  • Lưới đường cơ sở
  • +
  • Dòng chính
  • +
  • Giãn cách
  • +
  • Kích cỡ mục tiêu chạm
  • +
  • Cấu trúc bố trí
  • +
+ + +

Quy định Độ cao trong Dạng xem của Bạn

+ +

Dạng xem có thể đổ bóng và giá trị độ cao của một dạng xem +xác định kích cỡ bóng và thứ tự vẽ của nó. Để đặt độ cao của một dạng xem, hãy sử dụng thuộc tính +android:elevation trong bố trí của bạn:

+ +
+<TextView
+    android:id="@+id/my_textview"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:text="@string/next"
+    android:background="@color/white"
+    android:elevation="5dp" />
+
+ +

Thuộc tính translationZ mới cho phép bạn tạo những hoạt hình phản ánh các thay đổi +tạm thời về độ cao của một dạng xem. Thay đổi về độ cao có thể hữu ích khi +phản hồi lại các cử chỉ +chạm.

+ +

Để biết thêm chi tiết, hãy xem phần Định nghĩa +Đổ bóng và Dạng xem Cắt hình.

+ + +

Tạo Danh sách và Thẻ

+ +

{@link android.support.v7.widget.RecyclerView} là một phiên bản dễ ghép nối hơn của {@link +android.widget.ListView} có hỗ trợ các kiểu bố trí khác nhau và cung cấp những cải tiến về hiệu năng. +{@link android.support.v7.widget.CardView} cho phép bạn hiện các mẩu thông tin bên trong thẻ với +một diện mạo nhất quán giữa các ứng dụng. Ví dụ về mã sau đây minh họa cách thêm +{@link android.support.v7.widget.CardView} vào bố trí của bạn:

+ +
+<android.support.v7.widget.CardView
+    android:id="@+id/card_view"
+    android:layout_width="200dp"
+    android:layout_height="200dp"
+    card_view:cardCornerRadius="3dp">
+    ...
+</android.support.v7.widget.CardView>
+
+ +

Để biết thêm thông tin, hãy xem phần Tạo Danh sách +và Thẻ.

+ + +

Tùy chỉnh Hoạt hình của Bạn

+ +

Android 5.0 (API mức 21) bao gồm các API mới để tạo hoạt hình tùy chỉnh trong ứng dụng của bạn. +Ví dụ, bạn có thể cho phép chuyển tiếp hoạt động và định nghĩa một chuyển tiếp ra bên trong một +hoạt động:

+ +
+public class MyActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        // enable transitions
+        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+        setContentView(R.layout.activity_my);
+    }
+
+    public void onSomeButtonClicked(View view) {
+        getWindow().setExitTransition(new Explode());
+        Intent intent = new Intent(this, MyOtherActivity.class);
+        startActivity(intent,
+                      ActivityOptions
+                          .makeSceneTransitionAnimation(this).toBundle());
+    }
+}
+
+ +

Khi bạn bắt đầu một hoạt động khác từ hoạt động này, chuyển tiếp ra được kích hoạt.

+ +

Để tìm hiểu thêm về các API hoạt hình mới, hãy xem Định nghĩa Hoạt hình Tùy chỉnh.

diff --git a/docs/html-intl/intl/vi/training/material/index.jd b/docs/html-intl/intl/vi/training/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..44b74e1826fdffd35b137ea48a6527080f5ba260 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=Material Design cho Nhà phát triển +page.type=design +page.image=images/cards/material_2x.png +page.metaDescription=Tìm hiểu cách áp dụng material design cho ứng dụng của bạn. + + +@jd:body + +
+
+

Phụ thuộc và Điều kiện Tiên quyết

+
    +
  • Android 5.0 (API mức 21)
  • +
+
+
+ +

Material design là một hướng dẫn toàn diện về thiết kế trực quan, chuyển động +và tương tác giữa nhiều nền tảng và thiết bị. Để sử dụng material design trong ứng dụng Androi của mình, hãy làm theo hướng dẫn +mô tả trong +đặc tả +material design và sử dụng những thành phần và tính năng mới sẵn có trong Android 5.0 +(API mức 21).

+ +

Bài học này sẽ cho bạn thấy cách tạo ứng dụng material design bằng những phần tử sau:

+ +
    +
  • Chủ đề material
  • +
  • Widget cho thẻ và danh sách
  • +
  • Đổ bóng và cắt xén dạng xem tùy chỉnh
  • +
  • Nội dung vẽ được véc-tơ
  • +
  • Hoạt hình tùy chỉnh
  • +
+ +

Bài học này cũng hướng dẫn cho bạn cách duy trì tính tương thích với các phiên bản Android trước +5.0 (API mức 21) khi bạn sử dụng các tính năng về material design trong ứng dụng của mình.

+ +

Bài học

+ +
+
Bắt đầu
+
Tìm hiểu cách cập nhật ứng dụng của bạn với các tính năng của material design.
+ +
Sử dụng Chủ đề Material
+
Tìm hiểu cách áp dụng các kiểu phong cách material design cho ứng dụng của bạn.
+ +
Tạo Danh sách và Thẻ
+
Tìm hiểu cách tạo danh sách và thẻ với một diện mạo và cảm giác nhất quán bằng cách sử dụng widget hệ thống.
+ +
Định nghĩa Đổ bóng và Dạng xem Cắt hình
+
Tìm hiểu cách đặt độ cao cho dạng xem của bạn để tạo đổ bóng tùy chỉnh và cách cắt hình dạng xem.
+ +
Làm việc với Nội dung vẽ được
+
Tìm hiểu cách tạo nội dung vẽ được véc-tơ và cách nhuộm màu tài nguyên vẽ được.
+ +
Định nghĩa Hoạt hình Tùy chỉnh
+
Tìm hiểu cách tạo hoạt hình tùy chỉnh cho các dạng xem và chuyển tiếp hoạt động bằng các phần tử chung.
+ +
Duy trì Tính tương thích
+
Tìm hiểu cách duy trì tính tương thích với các phiên bản nền tảng trước Android 5.0.
+
diff --git a/docs/html-intl/intl/vi/training/material/lists-cards.jd b/docs/html-intl/intl/vi/training/material/lists-cards.jd new file mode 100644 index 0000000000000000000000000000000000000000..7127649bda830b8be081cb1366a266e5e946d974 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=Tạo Danh sách và Thẻ + +@jd:body + +
+
+

Bài học này hướng dẫn bạn cách

+
    +
  1. Tạo Danh sách
  2. +
  3. Tạo Thẻ
  4. +
  5. Thêm Phụ thuộc
  6. +
+

Bạn cũng nên đọc

+ +
+
+ + +

Để tạo danh sách và thẻ phức tạp bằng phong cách material design trong ứng dụng của mình, bạn có thể sử dụng widget +{@link android.support.v7.widget.RecyclerView} và {@link android.support.v7.widget.CardView} +.

+ + +

Tạo Danh sách

+ +

Widget {@link android.support.v7.widget.RecyclerView} là một phiên bản nâng cao và linh hoạt hơn +của {@link android.widget.ListView}. Widget này là một bộ chứa để hiển thị các tập dữ liệu +lớn có thể được cuộn rất hiệu quả bằng cách duy trì một số dạng xem hữu hạn. Sử dụng widget +{@link android.support.v7.widget.RecyclerView} khi bạn có các bộ sưu tập dữ liệu với phần tử +thay đổi vào thời gian chạy dựa vào hành động của người dùng hoặc sự kiện mạng.

+ +

Lớp {@link android.support.v7.widget.RecyclerView} đơn giản hóa việc hiển thị và xử lý +các tập dữ liệu lớn bằng cách cung cấp:

+ +
    +
  • Trình quản lý bố trí để định vị mục
  • +
  • Hoạt hình mặc định cho các thao tác chung đối với mục như loại bỏ hoặc thêm mục
  • +
+ +

Bạn cũng có sự linh hoạt để định nghĩa các trình quản lý bố trí tùy chỉnh và hoạt hình cho widget {@link +android.support.v7.widget.RecyclerView}.

+ + +

+Hình 1. Widget RecyclerView. +

+ +

Để sử dụng widget {@link android.support.v7.widget.RecyclerView}, bạn phải quy định một +trình điều hợp và trình quản lý bố trí. Để tạo một trình điều hợp, hãy mở rộng lớp {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}. Chi tiết +của triển khai phụ thuộc vào các chỉ định của tập dữ liệu của bạn và kiểu dạng xem. Để biết thêm +thông tin, hãy xem ví dụ bên dưới.

+ +
+ +

+Hình 2 - Danh sách với RecyclerView. +

+
+ +

Trình quản lý bố trí sẽ định vị các dạng xem mục bên trong {@link +android.support.v7.widget.RecyclerView} và xác định thời điểm sử dụng lại các dạng xem mục +không còn hiển thị trước người dùng nữa. Để sử dụng lại (hoặc tái chế) một dạng xem, trình quản lý bố +trí có thể yêu cầu trình điều hợp thay thế nội dung của dạng xem bằng một phần tử khác từ tập dữ liệu. Tái chế +dạng xem bằng cách này sẽ cải thiện hiệu năng nhờ tránh tạo những dạng xem không cần thiết hoặc thực hiện +tra cứu {@link android.app.Activity#findViewById findViewById()} tốn kém.

+ +

{@link android.support.v7.widget.RecyclerView} cung cấp những trình quản lý bố trí dựng sẵn sau:

+ +
    +
  • {@link android.support.v7.widget.LinearLayoutManager} hiện các mục trong một danh sách cuộn +thẳng đứng hoặc nằm ngang.
  • +
  • {@link android.support.v7.widget.GridLayoutManager} hiện các mục trong lưới.
  • +
  • {@link android.support.v7.widget.StaggeredGridLayoutManager} hiện các mục trong một lưới so le.
  • +
+ +

Để tạo một trình quản lý bố trí tùy chỉnh, hãy mở rộng lớp {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager}.

+ +

Hoạt hình

+ +

Hoạt hình để thêm và loại bỏ mục được kích hoạt theo mặc định trong {@link +android.support.v7.widget.RecyclerView}. Để tùy chỉnh những hoạt hình này, hãy mở rộng lớp +{@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} và sử dụng +phương thức {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} +.

+ +

Ví dụ

+ +

Ví dụ đoạn mã sau minh họa cách thêm +{@link android.support.v7.widget.RecyclerView} vào bố trí:

+ +
+<!-- A RecyclerView with some commonly used attributes -->
+<android.support.v7.widget.RecyclerView
+    android:id="@+id/my_recycler_view"
+    android:scrollbars="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
+
+ +

Sau khi thêm widget {@link android.support.v7.widget.RecyclerView} vào bố trí của bạn, +hãy lấy một núm điều tác tới đối tượng, kết nối nó với một trình quản lý bố trí và gắn kèm một trình điều hợp cho dữ liệu +cần được hiển thị:

+ +
+public class MyActivity extends Activity {
+    private RecyclerView mRecyclerView;
+    private RecyclerView.Adapter mAdapter;
+    private RecyclerView.LayoutManager mLayoutManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.my_activity);
+        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
+
+        // use this setting to improve performance if you know that changes
+        // in content do not change the layout size of the RecyclerView
+        mRecyclerView.setHasFixedSize(true);
+
+        // use a linear layout manager
+        mLayoutManager = new LinearLayoutManager(this);
+        mRecyclerView.setLayoutManager(mLayoutManager);
+
+        // specify an adapter (see also next example)
+        mAdapter = new MyAdapter(myDataset);
+        mRecyclerView.setAdapter(mAdapter);
+    }
+    ...
+}
+
+ +

Trình điều hợp cung cấp truy cập vào các mục trong tập dữ liệu của bạn, tạo dạng xem cho mục và +thay thế nội dung của một số dạng xem bằng mục dữ liệu mới khi mục ban đầu không còn +hiển thị. Ví dụ đoạn mã sau đây thể hiện việc triển khai đơn giản của tập dữ liệu bao gồm +một mảng xâu được hiển thị bằng widget {@link android.widget.TextView}:

+ +
+public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
+    private String[] mDataset;
+
+    // Provide a reference to the views for each data item
+    // Complex data items may need more than one view per item, and
+    // you provide access to all the views for a data item in a view holder
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        // each data item is just a string in this case
+        public TextView mTextView;
+        public ViewHolder(TextView v) {
+            super(v);
+            mTextView = v;
+        }
+    }
+
+    // Provide a suitable constructor (depends on the kind of dataset)
+    public MyAdapter(String[] myDataset) {
+        mDataset = myDataset;
+    }
+
+    // Create new views (invoked by the layout manager)
+    @Override
+    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
+                                                   int viewType) {
+        // create a new view
+        View v = LayoutInflater.from(parent.getContext())
+                               .inflate(R.layout.my_text_view, parent, false);
+        // set the view's size, margins, paddings and layout parameters
+        ...
+        ViewHolder vh = new ViewHolder(v);
+        return vh;
+    }
+
+    // Replace the contents of a view (invoked by the layout manager)
+    @Override
+    public void onBindViewHolder(ViewHolder holder, int position) {
+        // - get element from your dataset at this position
+        // - replace the contents of the view with that element
+        holder.mTextView.setText(mDataset[position]);
+
+    }
+
+    // Return the size of your dataset (invoked by the layout manager)
+    @Override
+    public int getItemCount() {
+        return mDataset.length;
+    }
+}
+
+ + +
+ +

+Hình 3. Ví dụ thẻ. +

+
+ +

Tạo Thẻ

+ +

{@link android.support.v7.widget.CardView} mở rộng lớp {@link android.widget.FrameLayout} và +cho phép bạn hiển thị thông tin bên trong các thẻ có diện mạo nhất quán trên khắp nền tảng. Widget {@link +android.support.v7.widget.CardView} có thể có đổ bóng và góc bo tròn.

+ +

Để tạo một thẻ có bóng, hãy sử dụng thuộc tính card_view:cardElevation. +{@link android.support.v7.widget.CardView} sử dụng độ cao thực và đổ bóng động trên Android 5.0 +(API mức 21) và cao hơn, và quay lại triển khai đổ bóng theo lập trình trên các phiên bản cũ hơn. +Để biết thêm thông tin, hãy xem phần Duy trì +Tính tương thích.

+ +

Sử dụng những thuộc tính sau để tùy chỉnh diện mạo của widget +{@link android.support.v7.widget.CardView}:

+ +
    +
  • Để đặt bán kính góc trong bố trí của bạn, hãy sử dụng thuộc tính card_view:cardCornerRadius +.
  • +
  • Để đặt bán kính góc trong mã của bạn, hãy sử dụng phương thức CardView.setRadius.
  • +
  • Để đặt màu nền của thẻ, hãy sử dụng thuộc tính card_view:cardBackgroundColor +.
  • +
+ +

Ví dụ về mã sau đây cho bạn thấy cách thêm widget {@link android.support.v7.widget.CardView} +trong bố trí của mình:

+ +
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
+    ... >
+    <!-- A CardView that contains a TextView -->
+    <android.support.v7.widget.CardView
+        xmlns:card_view="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/card_view"
+        android:layout_gravity="center"
+        android:layout_width="200dp"
+        android:layout_height="200dp"
+        card_view:cardCornerRadius="4dp">
+
+        <TextView
+            android:id="@+id/info_text"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </android.support.v7.widget.CardView>
+</LinearLayout>
+
+ +

Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho {@link android.support.v7.widget.CardView}.

+ + +

Thêm Phụ thuộc

+ +

Widget {@link android.support.v7.widget.RecyclerView} và {@link android.support.v7.widget.CardView} +là bộ phận của Thư viện Hỗ trợ +v7. Để sử dụng những widget này trong dự án của mình, hãy thêm những +Phụ thuộc Gradle này vào mô-đun +ứng dụng của bạn:

+ +
+dependencies {
+    ...
+    compile 'com.android.support:cardview-v7:21.0.+'
+    compile 'com.android.support:recyclerview-v7:21.0.+'
+}
+
diff --git a/docs/html-intl/intl/vi/training/material/shadows-clipping.jd b/docs/html-intl/intl/vi/training/material/shadows-clipping.jd new file mode 100644 index 0000000000000000000000000000000000000000..e9091f289fcb1a4390a891444fc409462b4108a9 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=Định nghĩa Đổ bóng và Dạng xem Cắt hình + +@jd:body + + + +

Material design giới thiệu độ cao cho phần tử UI. Độ cao giúp người dùng hiểu được +tầm quan trọng tương đối của từng phần tử và tập chung sự chú ý của họ vào tác vụ hiện có.

+ +

Độ cao của một dạng xem, được biểu diễn bằng thuộc tính Z, sẽ xác định diện mạo trực quan của +bóng đổ: dạng xem có giá trị Z cao hơn sẽ đổ bóng lớn hơn, mềm hơn. Dạng xem có giá trị Z cao hơn sẽ che khuất dạng xem +có giá trị Z thấp hơn; tuy nhiên, giá trị Z của một dạng xem không ảnh hưởng tới kích cỡ của dạng xem.

+ +

Đổ bóng được vẽ bởi dạng xem mẹ của dạng xem cao hơn, do vậy nó phụ thuộc vào tiêu chuẩn cắt dạng xem, +được cắt bởi dạng xem mẹ theo mặc định.

+ +

Độ cao cũng hữu ích trong việc tạo hoạt hình ở nơi widget tạm thời dâng cao hơn +mặt phẳng dạng xem khi thực hiện một hành động nào đó.

+ +

Để biết thêm thông tin về độ cao trong material design, hãy xem +Đối tượng +trong không gian 3D.

+ + +

Gán Độ cao cho Dạng xem của Bạn

+ +

Giá trị Z của một dạng xem có hai thành phần: + +

    +
  • Độ cao: Thành phần tĩnh.
  • +
  • Độ dịch: Thành phần động được sử dụng cho hoạt hình.
  • +
+ +

Z = elevation + translationZ

+ + +

Hình 1 - Đổ bóng cho các độ cao dạng xem khác nhau.

+ +

Để đặt độ cao của dạng xem trong một định nghĩa bố trí, hãy sử dụng thuộc tính android:elevation +. Để đặt độ cao của dạng xem trong mã của một hoạt động, hãy sử dụng phương thức +{@link android.view.View#setElevation View.setElevation()}.

+ +

Để đặt độ dịch của dạng xem, hãy sử dụng phương thức {@link android.view.View#setTranslationZ +View.setTranslationZ()}.

+ +

Các phương thức {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} và {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} mới cho phép +bạn dễ dàng tạo hiệu ứng hoạt hình cho độ cao của dạng xem. Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho +{@link android.view.ViewPropertyAnimator} và hướng dẫn cho nhà phát triển về Hoạt hình Thuộc tính +.

+ +

Bạn cũng có thể sử dụng {@link android.animation.StateListAnimator} để +quy định những hoạt hình này bằng cách khai báo. Điều này đặc biệt hữu ích đối với trường hợp các thay đổi +trạng thái sẽ kích hoạt hoạt hình như khi người dùng nhấn một nút. Để biết thêm thông tin, hãy xem phần +Tạo Hiệu ứng Hoạt hình Thay đổi Trạng thái Xem.

+ +

Giá trị Z được đo bằng dp (điểm ảnh độc lập với mật độ).

+ + +

Tùy chỉnh Đổ bóng và Viền của Dạng xem

+ +

Các giới hạn nội dung vẽ được của nền dạng xem sẽ xác định hình dạng mặc định của bóng. +Viền biểu thị hình dạng ngoài của một đối tượng đồ họa và định nghĩa vùng +gợn sóng cho phản hồi chạm.

+ +

Hãy cân nhắc dạng xem sau được định nghĩa với nội dung vẽ được làm nền:

+ +
+<TextView
+    android:id="@+id/myview"
+    ...
+    android:elevation="2dp"
+    android:background="@drawable/myrect" />
+
+ +

Nội dung vẽ được làm nền được định nghĩa là một hình chữ nhật với góc bo tròn:

+ +
+<!-- res/drawable/myrect.xml -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="#42000000" />
+    <corners android:radius="5dp" />
+</shape>
+
+ +

Dạng xem sẽ tạo đổ bóng có góc bo tròn do nội dung vẽ được làm nền sẽ định nghĩa +viền của dạng xem. Khi cung cấp viền tùy chỉnh, nó ghi đè hình dạng mặc định của bóng đổ dạng xem.

+ +

Để định nghĩa viền tùy chỉnh cho một dạng xem trong mã của bạn:

+ +

    +
  1. Mở rộng lớp {@link android.view.ViewOutlineProvider}.
  2. +
  3. Ghi đè phương thức {@link android.view.ViewOutlineProvider#getOutline getOutline()}.
  4. +
  5. Gán trình cung cấp viền mới cho dạng xem của bạn bằng phương thức {@link +android.view.View#setOutlineProvider View.setOutlineProvider()}.
  6. +
+ +

Bạn có thể tạo viền hình bầu dục và hình chữ nhật có góc bo tròn bằng cách sử dụng các phương thức trong lớp +{@link android.graphics.Outline}. Trình cung cấp viền mặc định cho dạng xem sẽ lấy viền +từ nền của dạng xem. Để ngăn dạng xem đổ bóng, hãy đặt trình cung cấp viền của nó +thành null.

+ + +

Dạng xem Cắt hình

+ +

Dạng xem cắt hình cho phép bạn dễ dàng thay đổi hình dạng của một dạng xem. Bạn có thể cắt hình dạng xem để +nhất quán với các phần tử thiết kế khác hoặc để thay đổi hình dạng của một dạng xem để đáp ứng lại thông tin đầu vào của người dùng. +Bạn có thể cắt một dạng xem thành vùng viền của nó bằng cách sử dụng phương thức {@link android.view.View#setClipToOutline +View.setClipToOutline()} hoặc thuộc tính android:clipToOutline. Chỉ +viền là hình chữ nhật, hình tròn và hình chữ nhật bo tròn mới hỗ trợ cắt hình như được xác định bởi phương thức +{@link android.graphics.Outline#canClip Outline.canClip()}.

+ +

Để cắt dạng xem thành hình dạng của một nội dung vẽ được, hãy đặt nội dung vẽ được làm nền của dạng xem +(như minh họa bên trên) và gọi phương thức {@link android.view.View#setClipToOutline View.setClipToOutline()} +.

+ +

Cắt dạng xem là một thao tác tốn kém, vì vậy đừng tạo hiệu ứng hoạt hình cho hình dạng bạn sử dụng để +cắt dạng xem. Để đạt được hiệu ứng này, hãy sử dụng hoạt hình Hiệu ứng Lộ ra.

diff --git a/docs/html-intl/intl/vi/training/material/theme.jd b/docs/html-intl/intl/vi/training/material/theme.jd new file mode 100644 index 0000000000000000000000000000000000000000..9c46b5d18cabca1968874a42f168c1ae37e75182 --- /dev/null +++ b/docs/html-intl/intl/vi/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=Sử dụng Chủ đề Material + +@jd:body + + + + +

Chủ đề material mới cung cấp:

+ +
    +
  • Widget hệ thống cho phép bạn đặt bảng màu của chúng
  • +
  • Hoạt hình phản hồi chạm cho widget hệ thống
  • +
  • Hoạt hình chuyển tiếp hoạt động
  • +
+ +

Bạn có thể tùy chỉnh diện mạo của chủ đề material +theo nhận diện thương hiệu của mình bằng một bảng màu mà bạn kiểm soát. Bạn có thể nhuộm màu thanh hành động và +thanh trạng thái bằng cách sử dụng các thuộc tính chủ đề như minh họa trong Hình 3.

+ +

Widget hệ thống có một thiết kế mới và hoạt hình phản hồi chạm. Bạn có thể tùy chỉnh +bảng màu, hoạt hình phản hồi chạm và chuyển tiếp hoạt động cho ứng dụng của mình.

+ +

Chủ đề material được định nghĩa là:

+ +
    +
  • @android:style/Theme.Material (phiên bản tối)
  • +
  • @android:style/Theme.Material.Light (phiên bản sáng)
  • +
  • @android:style/Theme.Material.Light.DarkActionBar
  • +
+ +

Để biết danh sách các kiểu material mà bạn có thể sử dụng, hãy xem tài liệu tham chiếu API cho +{@link android.R.style R.style}.

+ + +
+
+ +
+

Hình 1. Giao diện material tối

+
+
+
+ +
+

Hình 2. Giao diện material sáng

+
+
+
+
+ +

+Lưu ý: Chủ đề material chỉ sẵn có trong Android 5.0 (API mức 21) trở +lên. Thư viện Hỗ trợ v7 +cung cấp chủ đề mang phong cách material design cho một số widget và hỗ trợ tùy chỉnh +bảng màu. Để biết thêm thông tin, hãy xem phần +Duy trì Tính tương thích. +

+ + +

Tùy chỉnh Bảng màu

+ +

Để tùy chỉnh các màu cơ bản của chủ đề cho phù hợp với nhãn hiệu của bạn, hãy định nghĩa +màu tùy chỉnh của bạn bằng thuộc tính chủ đề khi bạn kế thừa từ chủ đề material:

+ +
+<resources>
+  <!-- inherit from the material theme -->
+  <style name="AppTheme" parent="android:Theme.Material">
+    <!-- Main theme colors -->
+    <!--   your app branding color for the app bar -->
+    <item name="android:colorPrimary">@color/primary</item>
+    <!--   darker variant for the status bar and contextual app bars -->
+    <item name="android:colorPrimaryDark">@color/primary_dark</item>
+    <!--   theme UI controls like checkboxes and text fields -->
+    <item name="android:colorAccent">@color/accent</item>
+  </style>
+</resources>
+
+ +
+ +

+Hình 3. Tùy chỉnh chủ đề material.

+
+ + +

Tùy chỉnh Thanh Trạng thái

+ +

Chủ đề material cho phép bạn dễ dàng tùy chỉnh thanh trạng thái để bạn có thể quy định +màu cho phù hợp với nhãn hiệu của mình và đảm bảo đủ độ tương phản để hiển thị biểu tượng trạng thái màu trắng. Để +đặt màu tùy chỉnh cho thanh trạng thái, hãy sử dụng thuộc tính android:statusBarColor khi +bạn mở rộng chủ đề material. Theo mặc định, android:statusBarColor kế thừa +giá trị của android:colorPrimaryDark.

+ +

Bạn cũng có thể tự vẽ phía sau thanh trạng thái. Ví dụ, nếu bạn muốn hiển thị +thanh trạng thái trong suốt trên một ảnh chụp, với graddien hơi tối để đảm bảo biểu tượng +trạng thái màu trắng nhìn thấy được. Để làm vậy, hãy đặt thuộc tính android:statusBarColor thành +@android:color/transparent và điều chỉnh cờ cửa sổ nếu cần. Bạn cũng có thể +sử dụng phương thức {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} cho hoạt hình +hoặc hiệu ứng mờ dần.

+ +

+Lưu ý: Thanh trạng thái cần luôn có một bản vẽ rõ ràng từ +thanh công cụ chính, trừ trường hợp bạn hiển thị nội dung hình ảnh hoặc phương tiện phong phú từ cạnh tới cạnh phía sau +những thanh này và khi bạn sử dụng gradient để đảm bảo rằng các biểu tượng vẫn nhìn thấy được. +

+ +

Khi bạn tùy chỉnh thanh điều hướng và trạng thái, hãy khiến cả hai đều trong suốt hoặc chỉ sửa đổi +thanh trạng thái. Thanh điều hướng nên giữ lại màu đen trong mọi trường hợp khác.

+ + +

Tạo Chủ đề Dạng xem Riêng

+ +

Những phần tử trong định nghĩa bố trí XML có thể quy định thuộc tính android:theme để +tham chiếu tới một tài nguyên chủ đề. Thuộc tính này sửa đổi chủ đề cho phần tử và bất cứ +phần tử con nào, điều này có ích đối với việc thay đổi bảng màu của chủ đề trong một phần cụ thể +của giao diện.

diff --git a/docs/html-intl/intl/zh-cn/design/get-started/principles.jd b/docs/html-intl/intl/zh-cn/design/get-started/principles.jd index fbb46c97306c14f8e1a8334ad4a1087fc12866ae..1738c84ba3eda95693baf034c88958987175787a 100644 --- a/docs/html-intl/intl/zh-cn/design/get-started/principles.jd +++ b/docs/html-intl/intl/zh-cn/design/get-started/principles.jd @@ -14,8 +14,8 @@ page.title=Android 设计原则

让我着迷

-
-
+
+

以意想不到的方式让我眼前一亮

漂亮的界面、精心布置的动画,或恰到好处的声效,都会带来快乐的体验。 @@ -23,7 +23,7 @@ page.title=Android 设计原则

-
+
@@ -32,15 +32,15 @@ page.title=Android 设计原则
 
-
-
+
+

实际对象要比按钮和菜单更有趣

-

让用户可以直接触摸和操作您应用中的对象,让执行任务更轻松,让用户更满意。 +

让用户可以直接触摸和操作您应用中的对象,这样,可以让执行任务更轻松,让用户更满意。

-
+
@@ -49,8 +49,8 @@ page.title=Android 设计原则
 
-
-
+
+

我的应用我做主

用户喜欢添加个性化手势,因为这样会让他们觉得更顺手并且一切尽在他们的掌控之中。提供感性、美观的默认手势,但也要考虑到趣味性,不要让可选的定制手势妨碍主要任务的操作。 @@ -58,7 +58,7 @@ page.title=Android 设计原则

-
+
@@ -67,15 +67,15 @@ page.title=Android 设计原则
 
-
-
+
+

让应用了解我

逐渐了解用户的偏好。不要让用户反复做出相同的选择,将之前的选择列作快捷选择。

-
+
@@ -84,14 +84,14 @@ page.title=Android 设计原则

让我的生活更轻松

-
-
+
+

语言简洁

使用简单的字词组成简短的句子。用户往往会跳过冗长的句子。

-
+
@@ -100,15 +100,15 @@ page.title=Android 设计原则
 
-
-
+
+

图片比文字更直观

考虑以图示意。它们会吸引用户的注意力,并且比文字更高效。

-
+
@@ -117,15 +117,15 @@ page.title=Android 设计原则
 
-
-
+
+

为我决定,但最终由我做主

先提供您心目中的最佳选择,而不是先进行询问。太多的选择和决策会让人不悦。 只是在您的选择可能不当时,才赋予用户“撤消”的权利。

-
+
@@ -134,15 +134,15 @@ page.title=Android 设计原则
 
-
-
+
+

仅在我需要时显示我所需要的

如果一次性看到过多内容,用户会感到无所适从。将任务和信息细分为更小、更容易接受的片段。 隐藏当前不必要的选项,并在用户操作期间提供指导。

-
+
@@ -151,15 +151,15 @@ page.title=Android 设计原则
 
-
-
+
+

我应该始终清楚自己在哪里

让用户感到轻车熟路。为应用中的不同场景赋予不同的外观,使用过渡效果来展现屏幕之间的关系。 提供任务进度反馈。

-
+
@@ -168,8 +168,8 @@ page.title=Android 设计原则
 
-
-
+
+

决不能让我的成果付诸东流

务必保存好用户耗费花时间创造的内容,让他们能随处访问这些内容。跨手机、平板电脑和计算机等平台,记住设置、个人手势和创作内容, @@ -177,7 +177,7 @@ page.title=Android 设计原则

-
+
@@ -186,15 +186,15 @@ page.title=Android 设计原则
 
-
-
+
+

如果看上去一样,其行为也应当相同

通过视觉上的显著差异(而不是微妙差异)帮助用户区分不同的功能。避免使用固定模式,那样会导致外观相似的场景中,用户执行同样的输入,应用却给出不同的响应。

-
+
@@ -203,15 +203,15 @@ page.title=Android 设计原则
 
-
-
+
+

只在确实对我很重要时才打断我

就像优秀的个人助理一样,不要让上司被一些无关紧要的琐事打扰。用户希望专注于核心任务,除非确实非常重要并且情况紧急,否则,打断用户会让人烦不胜烦。

-
+
@@ -220,8 +220,8 @@ page.title=Android 设计原则

给我惊喜

-
-
+
+

到处为我提供有用的诀窍

如果能自己摸索出花样来,用户总是很开心的。利用直观的图案以及其他 Android 应用中常见的习惯手势,让您的应用更容易学习。 @@ -229,7 +229,7 @@ page.title=Android 设计原则

-
+
@@ -238,16 +238,16 @@ page.title=Android 设计原则
 
-
-
+
+

不是我的错

-

在提醒用户改正错误时要礼貌。他们希望在使用您的应用时感觉自己很聪明。如果用户操作出错,请提供清晰的恢复说明,而不要让他们咀嚼详细的技术信息。如果您能在后台修复错误,那当然更好。 - +

在提醒用户改正错误时要礼貌。他们希望在使用您的应用时感觉自己很聪明。 +如果用户操作出错,请提供清晰的恢复说明,而不要让他们咀嚼详细的技术信息。如果您能在后台修复错误,那当然更好。

-
+
@@ -256,15 +256,15 @@ page.title=Android 设计原则
 
-
-
+
+

多多鼓励

将复杂的任务分解为可轻松完成的小步骤。对操作提供反馈,哪怕只是微弱的灯光,也聊胜于无。

-
+
@@ -273,8 +273,8 @@ page.title=Android 设计原则
 
-
-
+
+

为我处理繁重事务

通过让新手完成他们认为自己不可能做到的事情,让他们感觉自己就像一名行家。例如,通过提供融合多种照片特效的快捷操作,只需几步,就可以让业余照片达到惊艳的效果。 @@ -282,7 +282,7 @@ page.title=Android 设计原则

-
+
@@ -291,15 +291,15 @@ page.title=Android 设计原则
 
-
-
+
+

让重要事项能更快地完成

不是所有操作都同样重要。确定应用中哪些功能是最重要的,将这些功能放在便于找到和使用的地方,例如相机的快门按钮,或者音乐播放器中的暂停按钮。

-
+
diff --git a/docs/html-intl/intl/zh-cn/design/material/index.jd b/docs/html-intl/intl/zh-cn/design/material/index.jd index 78e6d3845227c4a4acdaec4ca1161f5a77078fe5..b667bb8ec77201c5b9210637eb5ce4c4da73743c 100644 --- a/docs/html-intl/intl/zh-cn/design/material/index.jd +++ b/docs/html-intl/intl/zh-cn/design/material/index.jd @@ -1,7 +1,6 @@ -page.title=材料设计 -page.tags=Material, design -page.type=设计 -page.image=design/material/images/MaterialLight.png +page.title=Android 材料设计 +page.tags=Material,design,设计 +page.image=images/cards/design-material-for-android_2x.jpg @jd:body @@ -53,13 +52,13 @@ Android 现在已支持材料设计应用。
  • 用于自定义阴影和动画的全新 API
  • -

    有关在 Android 上实现材料设计的详细信息,请参阅创建材料设计应用。 +

    有关在 Android 上实现材料设计的详细信息,请参阅使用材料设计创建应用

    材料主题

    -

    材料主题提供了新的应用样式和系统小工具,让您能够为触摸反馈以及活动转换设置配色工具以及默认动画。 +

    材料主题提供了新的应用样式和系统小工具,让您能够为触摸反馈以及 Activity 转换设置配色工具以及默认动画。

    @@ -136,7 +135,7 @@ Android 现在已支持材料设计应用。

    动画

    -

    动画 API 让您可为 UI 控件中的触摸反馈、视图状态更改以及活动转换创建自定义动画。 +

    动画 API 让您可为 UI 控件中的触摸反馈、视图状态更改以及 Activity 转换创建自定义动画。

    这些 API 的作用是:

    @@ -149,7 +148,7 @@ Android 现在已支持材料设计应用。 使用循环显示动画隐藏和显示视图。
  • -使用自定义活动转换动画切换活动。 +使用自定义 Activity 转换动画切换 Activity。
  • 使用曲线运动创建更自然的动画。 diff --git a/docs/html-intl/intl/zh-cn/design/patterns/compatibility.jd b/docs/html-intl/intl/zh-cn/design/patterns/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..7ec1bf3fa8c4941a463bd864833c8db63377799a --- /dev/null +++ b/docs/html-intl/intl/zh-cn/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=向后兼容性 +page.tags="support" +page.metaDescription=关于 Android 4.x 如何让其 UI 设计适应早期硬件和操作系统版本的说明。 +@jd:body + + +
    +

    开发者文档

    +

    支持不同设备

    +
    +
    + +

    Android 3.0 中的重大更改包括:

    +
      +
    • 弃用导航硬按键(返回、菜单、搜索、主屏幕),改为通过虚拟控件(返回、主屏幕、最近使用记录)处理导航。 +
    • +
    • 使用操作栏中的菜单的可靠模式。
    • +
    +

    Android 4.0 将这些针对平板电脑的更改引入手机平台。

    + +

    让 Android 4.0 适应早期硬件和应用

    + +
    +
    + +

    带有虚拟导航控件的手机

    +

    针对 Android 3.0 和后续版本编写的 Android 应用在操作栏中显示操作。操作栏中放不下或者不够重要因而不值得显示在操作栏顶级菜单中的操作将显示在操作溢出菜单中。 + +

    +

    用户可通过在操作栏中触摸操作溢出菜单来访问该菜单。

    + +
    +
    + + + +
    +
    + +
    +
    + +

    带有实体导航按键的手机

    +

    带有传统导航硬按键的 Android 手机在屏幕底部不会显示虚拟导航栏。 +其操作溢出菜单可从硬按键菜单中进行访问。其形成的操作弹出菜单与上例风格相同,但显示在屏幕底部。 +

    + +
    +
    + + + +
    +
    + +
    +
    + +

    带有虚拟导航控件的手机上的传统应用

    +

    当您在带有虚拟导航控件的手机上运行为 Android 2.3 或更早版本编写的应用时,在虚拟导航栏的右侧将会显示操作溢出菜单控件。 +您可以触摸这些控件,以传统 Android 菜单风格显示应用的操作。 +

    + +
    +
    + + + +
    +
    diff --git a/docs/html-intl/intl/zh-cn/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/zh-cn/design/patterns/confirming-acknowledging.jd index aa4ae1fad4af9428f60b79fd39038be981d956a0..d4eaaffe9f8f0bea41339498ca663bc10f333f35 100644 --- a/docs/html-intl/intl/zh-cn/design/patterns/confirming-acknowledging.jd +++ b/docs/html-intl/intl/zh-cn/design/patterns/confirming-acknowledging.jd @@ -4,12 +4,12 @@ page.tags=dialog,toast,notification

    在某些情况下,当用户在您的应用中调用某个操作时,最好使用文字内容来确认确知该操作。

    -
    -
    +
    +

    确认是要求用户确认自己确实希望执行刚才所调用的操作。在某些情况下,确认随需要用户考虑的操作所相关的警告或关键信息一起显示。

    -
    +

    确知就是显示一段文字信息,让用户知道自己刚才调用的操作已经完成。这将消除系统正在执行的隐式操作的不确定性。在某些情况下,确知随撤消操作的选项一起显示。

    @@ -22,14 +22,14 @@ page.tags=dialog,toast,notification

    确认

    -
    -
    +
    +

    示例:Google Play 书籍

    在该示例中,用户要求从其 Google Play 书架中删除一本书。系统显示了一条警告来要求确认该操作,因为用户必须知道,一旦删除,该书籍将无法继续在任何设备上使用。

    在设计确认对话框时,请使用反映所请求操作的文字作为标题,使标题更有意义。

    -
    +

    示例:Android Beam

    确认不一定要显示在有两个按钮的警告中。在启动 Android Beam 后,系统提示用户触摸要分享的内容(本例中是一张照片)。如果他们决定不继续操作,只需移开手机即可。

    @@ -37,15 +37,15 @@ page.tags=dialog,toast,notification

    确知

    -
    -
    +
    +

    示例:已保存中断的 Gmail 草稿

    在本例中,如果用户在 Gmail 撰写邮件屏幕中向后或向上导航,可能会发生一些意外情况,此时会自动保存当前草稿。Toast 形式的确知会明确告知这一点。它会在几秒钟后消失。

    在这种情况下不能执行撤消操作,因为保存是由应用发起的,而不是由用户执行的。通过导航至草稿列表,可迅速方便地恢复邮件。

    -
    +

    示例:已删除 Gmail 会话

    在用户从 Gmail 中的列表删除某个会话后,会出现带撤消选项的确知。确知消息会一直等到用户采取无关操作才会消失,例如滚动列表。

    @@ -53,14 +53,14 @@ page.tags=dialog,toast,notification

    无需确认或确知

    -
    -
    +
    +

    示例:+1 操作

    无需确认。如果用户意外触摸 +1 按钮,不会有什么大问题。他们只需再次触摸该按钮,撤消该操作即可。

    无需确知。用户将看到 +1 按钮弹起并变红。这是非常明确的信号。

    -
    +

    示例:从主屏幕删除应用

    无需确认。这属于有意操作:用户必须将项目拖放到相对较大并且独立的目标上。因此,很难发生意外。但是如果用户反悔决定,只需数秒钟时间即可复原。

    diff --git a/docs/html-intl/intl/zh-cn/design/patterns/navigation.jd b/docs/html-intl/intl/zh-cn/design/patterns/navigation.jd index 339a2c5b301710f974370e759ecc9e72b770dcac..5d8eae9a8fc6fb3c678b654490d0f6d254ad61a8 100644 --- a/docs/html-intl/intl/zh-cn/design/patterns/navigation.jd +++ b/docs/html-intl/intl/zh-cn/design/patterns/navigation.jd @@ -14,15 +14,15 @@ page.image=/design/media/navigation_between_siblings_gmail.png Android 3.0对全局导航行为做出了重大改变。 对用户来说,认真遵照“返回”和“向上”的指导准则可让应用的导航更可靠、更符合预期。

    -

    Android 2.3 及更早的版本使用返回按钮来支持应用内的导航。在 Android 3.0 中引入操作栏后,出现了第二种导航机制:即向上按钮,由应用图标和左向箭头构成。 - +

    Android 2.3 及更早的版本使用系统返回按钮来支持应用内的导航。 +在 Android 3.0 中引入操作栏后,出现了第二种导航机制:即向上按钮,由应用图标和左向箭头构成。

    -

    向上和返回

    +

    向上与返回的比较

    -

    “向上”按钮用于根据屏幕之间的层级关系在应用中导航。 +

    “向上”按钮用于根据屏幕之间的层级关系在某个应用内部导航。 例如,如果屏幕 A 显示项目列表,并且选择某个项目会调出屏幕 B(该屏幕显示项目的更多详情),则屏幕 B 应提供可返回屏幕 A 的“向上”按钮。

    @@ -50,9 +50,9 @@ Android 3.0对全局导航行为做出了重大改变。

    应用内导航

    通过多个入口点导航屏幕

    -

    有时,某个屏幕在应用层级中的位置并不固定,可以从多个入口点抵达 — 例如可从应用中的其他任何屏幕抵达设置屏幕。在这种情况下,选择“向上”按钮会返回到引用屏幕,其行为跟“返回”按钮相同。 - +

    有时,某个屏幕在应用层级中的位置并不固定,可以从多个入口点抵达 — 例如可从应用中的其他任何屏幕抵达设置屏幕。 +在这种情况下,应选择“向上”按钮来返回到引用屏幕,其行为跟“返回”按钮相同。

    更改屏幕中的视图

    更改屏幕的视图选项不会更改“向上”或“返回”的行为:屏幕仍然位于应用层级中的同一位置,并且不会创建新的导航历史记录。 @@ -117,8 +117,8 @@ Android 3.0对全局导航行为做出了重大改变。

    间接通知

    如果您的应用需要同时提供关于多个事件的信息,可使用一条通知将用户引导至某个间隙屏幕。 -该屏幕将这些事件汇总,并为用户提供深度导航应用的路径。这种样式的通知称为间接通知。 - +该屏幕将这些事件汇总,并为用户提供深度导航应用的路径。 +这种样式的通知称为间接通知

    与标准(直接)通知不同,在间接通知的间隙屏幕上按下“返回”会让用户回到触发通知的点 — 不会在返回栈中插入额外的屏幕。 @@ -153,53 +153,53 @@ Android 3.0对全局导航行为做出了重大改变。

    在应用之间导航

    Android 系统的一个基本优势在于能够让应用相互激活,这样用户就可以直接从一个应用导航到另一个应用。 -例如,需要获取照片的应用可激活相机应用,后者可将照片返回给前者。这对于开发者和用户来说都极为有利,开发者可以方便地利用来自其他应用的代码,而用户则在执行常用操作时可以获得一致的体验。 - +例如,需要获取照片的应用可激活相机应用,后者可将照片返回给前者。 +这对于开发者和用户来说都极为有利,开发者可以方便地利用来自其他应用的代码,而用户则在执行常用操作时可以获得一致的体验。

    -

    为了理解应用到应用的导航,必须理解下面探讨的 Android 框架行为 -。

    +

    为了理解应用到应用的导航,必须理解下面探讨的 Android 框架行为。 +

    -

    活动、任务和意向

    +

    Activity、任务和 Intent

    -

    在 Android 中,活动是一个应用组件,用于定义信息屏幕以及用户可执行的所有相关操作。 -您的应用是活动的集合,由您创建的活动以及从其他应用重用的活动构成。 +

    在 Android 中,Activity 是一个应用组件,用于定义信息屏幕以及用户可执行的所有相关操作。 +您的应用是 Activity 的集合,由您创建的 Activity 以及从其他应用重用的 Activity 构成。

    -

    任务是用户为达成某个目标而执行的活动序列。一个 -任务可以只利用一个应用的活动,也可以利用来自多个不同应用的活动。 +

    任务是用户为达成某个目标而执行的 Activity 序列。一个 +任务可以只利用一个应用的 Activity,也可以利用来自多个不同应用的 Activity。

    -

    意向是应用的一种机制,用于发出信号以表明需要另一个应用的辅助才能执行某个操作。 -应用的活动可指示其可响应哪些意向。 -对于诸如“分享”这样的常用意向,用户可能安装有许多可执行该请求的应用。 +

    Intent是应用的一种机制,用于发出信号以表明需要另一个应用的辅助才能执行某个操作。 +应用的 Activity 可指示其可响应哪些 Intent。 +对于诸如“分享”这样的常用Intent,用户可能安装有许多可执行该请求的应用。

    示例:在应用之间导航以支持分享

    -

    要了解如何结合使用活动、任务和意向,可以考虑一个应用如何让用户通过使用另一应用来分享内容的例子。例如,从主屏幕启动 Play 商店应用可启动新任务 A(参见下图)。 - -在 Play 商店中导航并触摸某本促销图书来查看其详情之后,用户会停留在该任务中并通过添加活动来扩展该任务。 -触发“分享”操作会通过一个对话框来提示用户选择活动,该对话框中会列出来自不同应用、之前已注册用于处理“分享”意向的每一种活动。 +

    要了解如何结合使用 Activity、任务和 Intent,可以考虑如何让一个应用允许用户通过使用另一应用来分享内容的例子。 +例如,从主屏幕启动 Play 商店应用可启动新任务 A(参见下图)。 +在 Play 商店中导航并触摸某本促销图书来查看其详情之后,用户会停留在该任务中并通过添加 Activity 来扩展该任务。 +触发“分享”操作会通过一个对话框来提示用户选择 Activity,该对话框中会列出来自不同应用、之前已注册用于处理“分享” Intent 的每一种 Activity。

    -

    如果用户选择通过 Gmail 分享,则会以延续任务 A 的形式添加 Gmail 的写邮件活动 — 而不会创建新任务。 +

    如果用户选择通过 Gmail 分享,则会以延续任务 A 的形式添加 Gmail 的写邮件 Activity — 而不会创建新任务。 如果 Gmail 在后台还运行有自己的任务,该任务不会受影响。

    -

    在写邮件活动中,发送邮件或触摸“返回”按钮会让用户回到图书详情活动。 +

    在写邮件 Activity 中,发送邮件或触摸“返回”按钮会让用户回到图书详情 Activity。 之后继续触摸“返回”会在 Play 商店中继续回退,直至抵达主屏幕。

    -

    尽管如此,用户可通过在写邮件活动中触摸“向上”按钮表明其希望留在 Gmail 中。 -此时会显示 Gmail 的会话列表活动,并为其创建新任务 B。新任务都始于主屏幕,因此从会话列表触摸“返回”按钮,始终会回到主屏幕。 +

    尽管如此,用户可通过在写邮件 Activity 中触摸“向上”按钮表明其希望留在 Gmail 中。 +此时会显示 Gmail 的会话列表 Activity,并为其创建新任务 B。新任务都始于主屏幕,因此从会话列表触摸“返回”按钮,始终会回到主屏幕。

    @@ -208,6 +208,6 @@ Android 3.0对全局导航行为做出了重大改变。 如果 Gmail 在后台还运行有自己的任务,该任务会被任务 B 取代 — 之前的上下文会被丢弃,以使用户达成新目标。

    -

    如果您的应用经过注册,以使用应用中的深层活动来处理意向,可参阅通过主屏幕小工具和通知进入您的应用,获得有关如何指定“向上”导航行为的指导。 +

    如果您的应用经过注册,以使用应用中的深层 Activity 来处理 Intent,可参阅通过主屏幕小工具和通知进入您的应用,获得有关如何指定“向上”导航行为的指导。

    diff --git a/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd b/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd index 065673ee5cb77c148ef3bbb12d1c39b956f203c2..8984fc5cecf22197b2ff70ba07d1c8d99c1c7a83 100644 --- a/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd +++ b/docs/html-intl/intl/zh-cn/distribute/tools/promote/brand.jd @@ -69,77 +69,6 @@ page.metaDescription=关于Android 和 Goolge Play 品牌的指南和下载。

    不得使用 Android 徽标专用的字体。

    -

    Google Play

    - - -

    以下是关于 Google Play 品牌和相关资源的指南。

    - -

    在文本中使用 Google Play

    - -

    在文本中,请务必为首次出现或位于显著位置的 Google Play™ 加上 TM 符号。

    - -

    在提及移动设备体验时,除非相应文本中是很明显的说明性内容,否则请使用“Google Play”。例如,营销资料的标题可以是“从 Google Play™ 下载我们的游戏”,但是说明性内容可以是“使用 Google Play™ 商店应用下载我们的游戏”。 - -

    每次使用 Google Play 名称或图标时,你的资料中都应注明以下归属信息:

    - -
    Google Play 是 Google Inc. 的商标。
    - - -
    - -

    - 48x48 | - 96x96
    - 600x576 -

    -
    - -

    Google Play 商店图标

    - -

    你可以使用 Google Play 商店图标,但不得修改它。

    - -

    如上文所述,在文案中提及 Google Play 商店应用时,请使用全称:“Google Play 商店”。但直接标注 Google Play 商店图标时,可以只使用“Play 商店”,以便与设备上显示的图标标签一致。

    - - -

    Google Play 徽章

    - -
    - -

    - 129x45 | - 172x60

    -
    - -
    - -

    - 129x45 | - 172x60

    -
    - -

    你可以在网站和宣传资料中使用“即刻获取 Google Play”和“即刻获取 Android 应用 Google Play”徽标作为徽章,以指向你在 Google Play 上的商品。你也可以使用其他 Google Play 徽章格式,以及用于音乐、图书、杂志、电影和电视节目的徽章。要申请这些徽章,请使用 Android 和 Google Play 品牌权限咨询表单

    - -
      -
    • 请不要修改徽章图片的颜色、比例、间距或其他任何方面。 -
    • -
    • 当与其他应用市场的徽标一起使用时,Google Play 徽标尺寸不应小于这些徽标的尺寸。
    • -
    • 在线使用时,徽章应链接至以下内容之一: -
        -
      • 由你发布的商品的列表,例如:
        - http://play.google.com/store/search?q=publisherName -
      • -
      • Google Play 中相应应用的商品详情页,例如:
        - http://play.google.com/store/apps/details?id=packageName -
      • -
      -
    • -
    - -

    要快速创建链接至你在 Google Play 上的应用的徽章,请使用 Google Play 徽章生成器(支持 40 多种语言)。

    - -

    要创建自定义尺寸的徽章,请下载支持 40 多种语言的 Google Play 徽章的 Adobe® Illustrator® (.ai) 文件。

    - -

    要详细了解如何链接至 Google Play 中的商品详情页,请参阅链接至你的商品

    营销审核和品牌咨询

    diff --git a/docs/html-intl/intl/zh-cn/guide/components/activities.jd b/docs/html-intl/intl/zh-cn/guide/components/activities.jd new file mode 100644 index 0000000000000000000000000000000000000000..efc1fb1ba71129161d5ff9465a48cd7c06c860d5 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Activity +page.tags=Activity, Intent +@jd:body + + + + + +

    {@link android.app.Activity} +是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操作。 +每个 Activity 都会获得一个用于绘制其用户界面的窗口。窗口通常会充满屏幕,但也可小于屏幕并浮动在其他窗口之上。 + +

    + +

    一个应用通常由多个彼此松散联系的 Activity 组成。 +一般会指定应用中的某个 Activity 为“主” Activity,即首次启动应用时呈现给用户的那个 Activity。 +而且每个 Activity 均可启动另一个 Activity,以便执行不同的操作。 +每次新 Activity 启动时,前一 Activity 便会停止,但系统会在堆栈(“返回栈”)中保留该 Activity。 + +当新 Activity 启动时,系统会将其推送到返回栈上,并取得用户焦点。 +返回栈遵循“后进先出”堆栈机制,因此,当用户完成当前 Activity 并按“返回” + +按钮时,系统会从堆栈中将其弹出(并销毁),然后恢复前一 Activity。(任务和返回栈文档中对返回栈有更详细的阐述。) + +

    + +

    当一个 Activity 因某个新 Activity 启动而停止时,系统会通过该 Activity 的生命周期回调方法通知其这一状态变化。Activity 因状态变化—系统是创建 Activity、停止 Activity、恢复 Activity 还是销毁 Activity— 而收到的回调方法可能有若干种,每一种回调方法都会为您提供执行与该状态变化相应的特定操作的机会。 + + + + +例如,停止时,您的 Activity 应释放任何大型对象,例如网络或数据库连接。 +当 Activity 恢复时,您可以重新获取所需资源,并恢复执行中断的操作。 +这些状态转变都是 Activity 生命周期的一部分。 +

    + +

    本文的其余部分阐述有关如何创建和使用 Activity 的基础知识(包括对 Activity 生命周期工作方式的全面阐述),以便您正确管理各种 Activity 状态之间的转变。 + +

    + + + +

    创建 Activity

    + +

    要创建 Activity,您必须创建 {@link android.app.Activity} +的子类(或使用其现有子类)。您需要在子类中实现 Activity 在其生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity 时)系统调用的回调方法。 + +两个最重要的回调方法是: +

    + +
    +
    {@link android.app.Activity#onCreate onCreate()}
    +
    您必须实现此方法。系统会在创建您的 +Activity 时调用此方法。您应该在实现内初始化 Activity 的必需组件。 + + 最重要的是,您必须在此方法内调用 {@link android.app.Activity#setContentView + setContentView()},以定义 Activity 用户界面的布局。
    +
    {@link android.app.Activity#onPause onPause()}
    +
    系统将此方法作为用户离开 Activity 的第一个信号(但并不总是意味着 Activity 会被销毁)进行调用。 +您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。 + +
    +
    + +

    您还应使用几种其他生命周期回调方法,以便提供流畅的 Activity 间用户体验,以及处理导致您的 Activity 停止甚至被销毁的意外中断。 + +后文的管理 Activity 生命周期部分对所有生命周期回调方法进行了阐述。 +

    + + + +

    实现用户界面

    + +

    Activity 的用户界面是由层级式视图—衍生自 {@link android.view.View} +类的对象—提供的。每个视图都控制 Activity 窗口内的特定矩形空间,可对用户交互作出响应。 +例如,视图可以是在用户触摸时启动某项操作的按钮。 +

    + +

    您可以利用 Android +提供的许多现成视图设计和组织您的布局。“小工具”是提供按钮、文本字段、复选框或仅仅是一幅图像等屏幕视觉(交互式)元素的视图。 +“布局”是衍生自 {@link +android.view.ViewGroup} +的视图,为其子视图提供唯一布局模型,例如线性布局、网格布局或相对布局。您还可以为 {@link android.view.View} 类和 +{@link android.view.ViewGroup} +类创建子类(或使用其现有子类)来自行创建小工具和布局,然后将它们应用于您的 Activity 布局。

    + +

    利用视图定义布局的最常见方法是借助保存在您的应用资源内的 XML +布局文件。这样一来,您就可以将用户界面的设计与定义 Activity 行为的源代码分开维护。 +您可以通过 {@link android.app.Activity#setContentView(int) setContentView()} +将布局设置为 Activity 的 UI,从而传递布局的资源 +ID。不过,您也可以在 Activity 代码中创建新 +{@link android.view.View},并通过将新 {@link +android.view.View} 插入 {@link android.view.ViewGroup} 来创建视图层次,然后通过将根 +{@link android.view.ViewGroup} 传递到 {@link android.app.Activity#setContentView(View) +setContentView()} 来使用该布局。

    + +

    如需了解有关创建用户界面的信息,请参阅用户界面文档。

    + + + +

    在清单文件中声明 Activity

    + +

    您必须在清单文件中声明您的 Activity,这样系统才能访问它。 +要声明您的 Activity,请打开您的清单文件,并将 {@code <activity>} 元素添加为 +{@code <application>} +元素的子项。例如:

    + +
    +<manifest ... >
    +  <application ... >
    +      <activity android:name=".ExampleActivity" />
    +      ...
    +  </application ... >
    +  ...
    +</manifest >
    +
    + +

    您还可以在此元素中加入几个其他特性,以定义 Activity 标签、Activity 图标或风格主题等用于设置 Activity +UI +风格的属性。{@code android:name} +特性是唯一的必需特性—它指定 Activity 的类名。应用一旦发布,即不应更改此类名,否则,可能会破坏诸如应用快捷方式等一些功能(请阅读博客文章 +Things +That Cannot Change +[不能更改的内容])。

    + +

    请参阅 {@code <activity>} +元素参考文档,了解有关在清单文件中声明 Activity 的详细信息。

    + + +

    使用 Intent 过滤器

    + +

    {@code +<activity>} 元素还可指定各种 Intent 过滤器—使用 {@code +<Intent-filter>} +元素—以声明其他应用组件激活它的方法。

    + +

    当您使用 Android SDK 工具创建新应用时,系统自动为您创建的存根 Activity 包含一个 Intent 过滤器,其中声明了该 Activity 响应“主”操作且应置于“launcher”类别内。 + + Intent 过滤器的内容与以下所示类似: +

    + +
    +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +
    + +

    {@code +<action>} 元素指定这是应用的“主”入口点。{@code +<category>} +元素指定此 Activity 应列入系统的应用启动器内(以便用户启动该 Activity)。

    + +

    如果您打算让应用成为独立应用,不允许其他应用激活其 Activity,则您不需要任何其他 Intent 过滤器。 +正如前例所示,只应有一个 Activity 具有“主”操作和“launcher”类别。 +您不想提供给其他应用的 Activity 不应有任何 Intent 过滤器,您可以利用显式 Intent 自行启动它们(下文对此做了阐述)。 + +

    + +

    不过,如果您想让 Activity 对衍生自其他应用(以及您的自有应用)的隐式 Intent 作出响应,则必须为 Activity 定义其他 Intent 过滤器。 + +对于您想要作出响应的每一个 Intent 类型,您都必须加入相应的 {@code +<Intent-filter>},其中包括一个 +{@code +<action>} 元素,还可选择性地包括一个 {@code +<category>} 元素和/或一个 {@code +<data>} 元素。这些元素指定您的 Activity 可以响应的 Intent 类型。 +

    + +

    如需了解有关您的 Activity 如何响应 Intent 的详细信息,请参阅 Intent 和 Intent 过滤器文档。 +

    + + + +

    启动 Activity

    + +

    您可以通过调用 {@link android.app.Activity#startActivity + startActivity()},并将其传递给描述您想启动的 Activity 的 {@link android.content.Intent} +来启动另一个 Activity。Intent 对象会指定您想启动的具体 Activity 或描述您想执行的操作类型(系统会为您选择合适的 Activity,甚至是来自其他应用的 Activity)。 + + +Intent 对象还可能携带少量供所启动 Activity 使用的数据。 +

    + +

    在您的自有应用内工作时,您经常只需要启动某个已知 Activity。 + 您可以通过使用类名创建一个显式定义您想启动的 Activity 的 Intent 对象来实现此目的。 +例如,可以通过以下代码让一个 Activity 启动另一个名为 {@code +SignInActivity} 的 Activity:

    + +
    +Intent intent = new Intent(this, SignInActivity.class);
    +startActivity(intent);
    +
    + +

    不过,您的应用可能还需要利用您的 Activity 数据执行某项操作,例如发送电子邮件、短信或状态更新。 +在这种情况下,您的应用自身可能不具有执行此类操作所需的 Activity,因此您可以改为利用设备上其他应用提供的 Activity 为您执行这些操作。 + +这便是 Intent 对象的真正价值所在—您可以创建一个 Intent 对象,对您想执行的操作进行描述,系统会从其他应用启动相应的 Activity。 + + +如果有多个 Activity 可以处理 Intent,则用户可以选择要使用哪一个。 +例如,如果您想允许用户发送电子邮件,可以创建以下 Intent 对象: + +

    + +
    +Intent intent = new Intent(Intent.ACTION_SEND);
    +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
    +startActivity(intent);
    +
    + +

    添加到 Intent 中的 {@link android.content.Intent#EXTRA_EMAIL} extra 是一个字符串数组,其中包含应将电子邮件发送到的电子邮件地址。 +当电子邮件应用响应此 Intent 时,它会读取 extra 中提供的字符串数组,并将它们放入电子邮件撰写窗体的“收件人”字段。 + +在这种情况下,电子邮件应用的 Activity 启动,并且当用户完成操作时,您的 Activity 会恢复执行。 +

    + + + + +

    启动 Activity 以获得结果

    + +

    有时,您可能需要从启动的 Activity 获得结果。在这种情况下,请通过调用 {@link android.app.Activity#startActivityForResult + startActivityForResult()}(而非 {@link android.app.Activity#startActivity + startActivity()})来启动 Activity。 +要想在随后收到后续 Activity 的结果,请实现 +{@link android.app.Activity#onActivityResult onActivityResult()} 回调方法。 +当后续 Activity 完成时,它会使用 {@link +android.content.Intent} 向您的 {@link android.app.Activity#onActivityResult onActivityResult()} +方法返回结果。

    + +

    例如,您可能希望用户选取其中一位联系人,以便您的 Activity 对该联系人中的信息执行某项操作。 +您可以通过以下代码创建此类 Intent 并处理结果: +

    + +
    +private void pickContact() {
    +    // Create an intent to "pick" a contact, as defined by the content provider URI
    +    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    +    startActivityForResult(intent, PICK_CONTACT_REQUEST);
    +}
    +
    +@Override
    +protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    +    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    +    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
    +        // Perform a query to the contact's content provider for the contact's name
    +        Cursor cursor = getContentResolver().query(data.getData(),
    +        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
    +        if (cursor.moveToFirst()) { // True if the cursor is not empty
    +            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
    +            String name = cursor.getString(columnIndex);
    +            // Do something with the selected contact's name...
    +        }
    +    }
    +}
    +
    + +

    上例显示的是,您在处理 Activity 结果时应该在 {@link +android.app.Activity#onActivityResult onActivityResult()} +方法中使用的基本逻辑。第一个条件检查请求是否成功(如果成功,则{@code resultCode} 将为 {@link android.app.Activity#RESULT_OK})以及此结果响应的请求是否已知 — 在此情况下,{@code requestCode}与随 {@link android.app.Activity#startActivityForResult +startActivityForResult()} 发送的第二个参数匹配。 + + +代码通过查询 {@link android.content.Intent} 中返回的数据({@code data} 参数)从该处开始处理 Activity 结果。 +

    + +

    实际情况是,{@link +android.content.ContentResolver} 对一个内容提供程序执行查询,后者返回一个 +{@link android.database.Cursor},让查询的数据能够被读取。如需了解详细信息,请参阅内容提供程序文档。 +

    + +

    如需了解有关 Intent 用法的详细信息,请参阅 Intent 和 Intent 过滤器文档。 +

    + + +

    结束 Activity

    + +

    您可以通过调用 Activity 的 {@link android.app.Activity#finish +finish()} 方法来结束该 Activity。您还可以通过调用 +{@link android.app.Activity#finishActivity finishActivity()} 结束您之前启动的另一个 Activity。

    + +

    注:在大多数情况下,您不应使用这些方法显式结束 Activity。 +正如下文有关 Activity 生命周期的部分所述,Android 系统会为您管理 Activity 的生命周期,因此您无需完成自己的 Activity。 + +调用这些方法可能对预期的用户体验产生不良影响,因此只应在您确实不想让用户返回此 Activity 实例时使用。 + +

    + + +

    管理 Activity 生命周期

    + +

    通过实现回调方法管理 Activity 的生命周期对开发强大而又灵活的应用至关重要。 + +Activity 的生命周期会直接受到 Activity 与其他 Activity、其任务及返回栈的关联性的影响。 +

    + +

    Activity 基本上以三种状态存在:

    + +
    +
    已继续
    +
    此 Activity 位于屏幕前台并具有用户焦点。(有时也将此状态称作“运行中”。) +
    + +
    已暂停
    +
    另一个 Activity 位于屏幕前台并具有用户焦点,但此 Activity 仍可见。也就是说,另一个 Activity 显示在此 Activity 上方,并且该 Activity 部分透明或未覆盖整个屏幕。 + +已暂停的 Activity 处于完全 Activity 状态({@link android.app.Activity} +对象保留在内存中,它保留了所有状态和成员信息,并与窗口管理器保持连接),但在内存极度不足的情况下,可能会被系统终止。 +
    + +
    已停止
    +
    该 Activity 被另一个 Activity 完全遮盖(该 Activity 目前位于“后台”)。 +已停止的 Activity 同样仍处于 Activity 状态({@link android.app.Activity} +对象保留在内存中,它保留了所有状态和成员信息,但与窗口管理器连接)。 +不过,它对用户不再可见,在他处需要内存时可能会被系统终止。 +
    +
    + +

    如果 Activity 处于暂停或停止状态,系统可通过要求其结束(调用其 +{@link android.app.Activity#finish finish()} +方法)或直接终止其进程,将其从内存中删除。(将其结束或终止后)再次打开 Activity 时,必须重建。 +

    + + + +

    实现生命周期回调

    + +

    当一个 Activity 转入和转出上述不同状态时,系统会通过各种回调方法向其发出通知。 +所有回调方法都是挂钩,您可以在 Activity 状态发生变化时替代这些挂钩来执行相应操作。 +以下框架 Activity 包括每一个基本生命周期方法: +

    + + +
    +public class ExampleActivity extends Activity {
    +    @Override
    +    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // The activity is being created.
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onStart onStart()} {
    +        super.onStart();
    +        // The activity is about to become visible.
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onResume onResume()} {
    +        super.onResume();
    +        // The activity has become visible (it is now "resumed").
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onPause onPause()} {
    +        super.onPause();
    +        // Another activity is taking focus (this activity is about to be "paused").
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onStop onStop()} {
    +        super.onStop();
    +        // The activity is no longer visible (it is now "stopped")
    +    }
    +    @Override
    +    protected void {@link android.app.Activity#onDestroy onDestroy()} {
    +        super.onDestroy();
    +        // The activity is about to be destroyed.
    +    }
    +}
    +
    + +

    注:正如以上示例所示,您在实现这些生命周期方法时必须始终先调用超类实现,然后再执行任何操作。 +

    + +

    这些方法共同定义 Activity 的整个生命周期。您可以通过实现这些方法监控 Activity 生命周期中的三个嵌套循环: +

    + +
      +
    • Activity 的整个生命周期发生在 {@link +android.app.Activity#onCreate onCreate()} 调用与 {@link +android.app.Activity#onDestroy} 调用之间。您的 Activity 应在 {@link android.app.Activity#onCreate onCreate()} +中执行“全局”状态设置(例如定义布局),并释放 {@link android.app.Activity#onDestroy} +中的所有其余资源。例如,如果您的 Activity 有一个在后台运行的线程,用于从网络上下载数据,它可能会在 +{@link android.app.Activity#onCreate onCreate()} 中创建该线程,然后在 +{@link +android.app.Activity#onDestroy} 中停止该线程。
    • + +
    • Activity 的可见生命周期发生在 {@link +android.app.Activity#onStart onStart()} 调用与 {@link +android.app.Activity#onStop onStop()} 调用之间。在这段时间,用户可以在屏幕上看到 Activity 并与其交互。 +例如,当一个新 Activity 启动,并且此 Activity 不再可见时,系统会调用 +{@link android.app.Activity#onStop onStop()}。您可以在调用这两个方法之间保留向用户显示 Activity 所需的资源。 +例如,您可以在 +{@link +android.app.Activity#onStart onStart()} 中注册一个 {@link android.content.BroadcastReceiver} 以监控影响 UI +的变化,并在用户无法再看到您显示的内容时在 {@link android.app.Activity#onStop onStop()} +中将其取消注册。在 Activity 的整个生命周期,当 Activity 在对用户可见和隐藏两种状态中交替变化时,系统可能会多次调用 {@link android.app.Activity#onStart onStart()} 和 {@link +android.app.Activity#onStop onStop()}。 +

    • + +
    • Activity 的前台生命周期发生在 {@link +android.app.Activity#onResume onResume()} 调用与 {@link android.app.Activity#onPause +onPause()} 调用之间。在这段时间,Activity 位于屏幕上的所有其他 Activity 之前,并具有用户输入焦点。 +Activity 可频繁转入和转出前台—例如,当设备转入休眠状态或出现对话框时,系统会调用 {@link android.app.Activity#onPause onPause()}。 + +由于此状态可能经常发生转变,因此这两个方法中应采用适度轻量级的代码,以避免因转变速度慢而让用户等待。 +

    • +
    + +

    图 1 说明了这些循环以及 Activity 在状态转变期间可能经过的路径。矩形表示回调方法,当 Activity 在不同状态之间转变时,您可以实现这些方法来执行操作。 + +

    + + +

    图 1. Activity 生命周期。

    + +

    表 1 列出了相同的生命周期回调方法,其中对每一种回调方法做了更详细的描述,并说明了每一种方法在 Activity 整个生命周期内的位置,包括在回调方法完成后系统能否终止 Activity。 + + +

    + +

    表 1. Activity 生命周期回调方法汇总表。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    方法 描述 是否能事后终止? 后接
    {@link android.app.Activity#onCreate onCreate()}首次创建 Activity 时调用。 + 您应该在此方法中执行所有正常的静态设置— +创建视图、将数据绑定到列表等等。系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态(请参阅后文的保存 Activity 状态)。 + + + +

    始终后接 {@code onStart()}。

    {@code onStart()}
        {@link android.app.Activity#onRestart +onRestart()}在 Activity 已停止并即将再次启动前调用。 + +

    始终后接 {@code onStart()}

    {@code onStart()}
    {@link android.app.Activity#onStart onStart()}在 Activity 即将对用户可见之前调用。 +

    如果 Activity 转入前台,则后接 {@code onResume()},如果 Activity 转入隐藏状态,则后接 {@code onStop()}。 +

    {@code onResume()}

    {@code onStop()}
        {@link android.app.Activity#onResume onResume()}在 Activity 即将开始与用户进行交互之前调用。 +此时,Activity 处于 Activity 堆栈的顶层,并具有用户输入焦点。 + +

    始终后接 {@code onPause()}。

    {@code onPause()}
    {@link android.app.Activity#onPause onPause()}当系统即将开始继续另一个 Activity 时调用。 +此方法通常用于确认对持久性数据的未保存更改、停止动画以及其他可能消耗 CPU 的内容,诸如此类。 + +它应该非常迅速地执行所需操作,因为它返回后,下一个 Activity 才能继续执行。 + +

    如果 Activity 返回前台,则后接 {@code onResume()},如果 Activity 转入对用户不可见状态,则后接 {@code onStop()}。 + +

    {@code onResume()}

    {@code onStop()}
    {@link android.app.Activity#onStop onStop()}Activity 对用户不再可见时调用。如果 Activity 被销毁,或另一个 Activity(一个现有 Activity 或新 Activity)继续执行并将其覆盖,就可能发生这种情况。 + + +

    如果 Activity 恢复与用户的交互,则后接 {@code onRestart()},如果 Activity 被销毁,则后接 +{@code onDestroy()}。 +

    {@code onRestart()}

    {@code onDestroy()}
    {@link android.app.Activity#onDestroy +onDestroy()}在 Activity 被销毁前调用。这是 Activity 将收到的最后调用。 +当 Activity 结束(有人调用 Activity 上的 {@link android.app.Activity#finish + finish()}),或系统为节省空间而暂时销毁该 Activity 实例时,可能会调用它。 + +您可以通过 + {@link + android.app.Activity#isFinishing isFinishing()} 方法区分这两种情形。
    + +

    名为“是否能事后终止?”的列表示系统是否能在不执行另一行 Activity 代码的情况下,在方法返回后随时终止承载 Activity 的进程。 + +有三个方法带有“是”标记:({@link +android.app.Activity#onPause +onPause()}、{@link android.app.Activity#onStop onStop()} 和 {@link android.app.Activity#onDestroy +onDestroy()})。由于 {@link android.app.Activity#onPause onPause()} +是这三个方法中的第一个,因此 Activity 创建后,{@link android.app.Activity#onPause onPause()} +必定成为最后调用的方法,然后才能终止进程—如果系统在紧急情况下必须恢复内存,则可能不会调用 +{@link +android.app.Activity#onStop onStop()} 和 +{@link android.app.Activity#onDestroy onDestroy()}。因此,您应该使用 {@link android.app.Activity#onPause onPause()} +向存储设备写入至关重要的持久性数据(例如用户编辑)。不过,您应该对 +{@link android.app.Activity#onPause onPause()} +调用期间必须保留的信息有所选择,因为该方法中的任何阻止过程都会妨碍向下一个 Activity 的转变并拖慢用户体验。 +

    + +

    是否能在事后终止?列中标记为“否”的方法可从系统调用它们的一刻起防止承载 Activity 的进程被终止。 +因此,在从 {@link android.app.Activity#onPause onPause()} +返回的时间到 {@link android.app.Activity#onResume onResume()} +被调用的时间,系统可以终止 Activity。在 {@link android.app.Activity#onPause onPause()} +被再次调用并返回前,将无法再次终止 Activity。

    + +

    注:根据表 +1 +中的定义属于技术上无法“终止”的 Activity 仍可能被系统终止—但这种情况只有在无任何其他资源的极端情况下才会发生。进程和线程处理文档对可能会终止 Activity 的情况做了更详尽的阐述。 + +

    + + +

    保存 Activity 状态

    + +

    管理 Activity 生命周期的引言部分简要提及,当 Activity 暂停或停止时,Activity 的状态会得到保留。 + +确实如此,因为当 Activity 暂停或停止时,{@link android.app.Activity} +对象仍保留在内存中 — 有关其成员和当前状态的所有信息仍处于 Activity 状态。 +因此,用户在 Activity 内所做的任何更改都会得到保留,这样一来,当 Activity 返回前台(当它“继续”)时,这些更改仍然存在。 + +

    + +

    不过,当系统为了恢复内存而销毁某项 Activity 时,{@link +android.app.Activity} +对象也会被销毁,因此系统在继续 Activity 时根本无法让其状态保持完好,而是必须在用户返回Activity时重建 {@link android.app.Activity} +对象。但用户并不知道系统销毁 Activity 后又对其进行了重建,因此他们很可能认为 Activity 状态毫无变化。 + +在这种情况下,您可以实现另一个回调方法对有关 Activity 状态的信息进行保存,以确保有关 Activity 状态的重要信息得到保留:{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}。 + +

    + +

    系统会先调用 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()},然后再使 Activity 变得易于销毁。系统会向该方法传递一个 +{@link android.os.Bundle},您可以在其中使用 +{@link +android.os.Bundle#putString putString()} 和 {@link +android.os.Bundle#putInt putInt()} 等方法以名称-值对形式保存有关 Activity 状态的信息。然后,如果系统终止您的应用进程,并且用户返回您的 Activity,则系统会重建该 Activity,并将 +{@link android.os.Bundle} 同时传递给 +{@link android.app.Activity#onCreate onCreate()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}。您可以使用上述任一方法从 +{@link android.os.Bundle} +提取您保存的状态并恢复该 Activity 状态。如果没有状态信息需要恢复,则传递给您的 {@link +android.os.Bundle} +是空值(如果是首次创建该 Activity,就会出现这种情况)。

    + + +

    图 2. 在两种情况下,Activity 重获用户焦点时可保持状态完好:系统在销毁 Activity 后重建 Activity,Activity 必须恢复之前保存的状态;系统停止 Activity 后继续执行 Activity,并且 Activity 状态保持完好。 + + +

    + +

    注:无法保证系统会在销毁您的 Activity 前调用 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},因为存在不需要保存状态的情况(例如用户使用“返回” + +按钮离开您的 Activity 时,因为用户的行为是在显式关闭 Activity)。 + +如果系统调用 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},它会在调用 {@link +android.app.Activity#onStop onStop()} 之前,并且可能会在调用 {@link android.app.Activity#onPause +onPause()} 之前进行调用。

    + +

    不过,即使您什么都不做,也不实现 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},{@link android.app.Activity} +类的 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 默认实现也会恢复部分 Activity 状态。具体地讲,默认实现会为布局中的每个 +{@link +android.view.View} 调用相应的 {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} +方法,让每个视图都能提供有关自身的应保存信息。Android +框架中几乎每个小工具都会根据需要实现此方法,以便在重建 Activity 时自动保存和恢复对 UI +所做的任何可见更改。例如,{@link android.widget.EditText} +小工具保存用户输入的任何文本,{@link android.widget.CheckBox} +小工具保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小工具提供一个唯一的 ID(通过 {@code android:id} +属性)。如果小工具没有 ID,则系统无法保存其状态。 +

    + + + +

    尽管 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} +的默认实现会保存有关您的Activity UI +的有用信息,您可能仍需替代它以保存更多信息。例如,您可能需要保存在 Activity 生命周期内发生了变化的成员值(它们可能与 +UI 中恢复的值有关联,但默认情况下系统不会恢复储存这些 UI +值的成员)。

    + +

    由于 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的默认实现有助于保存 UI 的状态, +因此如果您为了保存更多状态信息而重写该方法,应始终先调用 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +的超类实现,然后再执行任何操作。同样,如果您替代 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} +方法,也应调用它的超类实现,以便默认实现能够恢复视图状态。

    + +

    注:由于无法保证系统会调用 +{@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},因此您只应利用它来记录 Activity 的瞬态(UI +的状态)—切勿使用它来存储持久性数据,而应使用 {@link +android.app.Activity#onPause onPause()} +在用户离开 Activity 后存储持久性数据(例如应保存到数据库的数据)。

    + +

    您只需旋转设备,让屏幕方向发生变化,就能有效地测试您的应用的状态恢复能力。 +当屏幕方向变化时,系统会销毁并重建 Activity,以便应用可供新屏幕配置使用的备用资源。 + +单凭这一理由,您的 Activity 在重建时能否完全恢复其状态就显得非常重要,因为用户在使用应用时经常需要旋转屏幕。 + +

    + + +

    处理配置变更

    + +

    有些设备配置可能会在运行时发生变化(例如屏幕方向、键盘可用性及语言)。 +发生此类变化时,Android 会重建运行中的 Activity(系统调用 +{@link android.app.Activity#onDestroy},然后立即调用 {@link +android.app.Activity#onCreate onCreate()})。此行为旨在通过利用您提供的备用资源(例如适用于不同屏幕方向和屏幕尺寸的不同布局)自动重新加载您的应用来帮助它适应新配置。 + + +

    + +

    如果您对 Activity 进行了适当设计,让它能够按以上所述处理屏幕方向变化带来的重启并恢复 Activity 状态,那么在遭遇 Activity 生命周期中的其他意外事件时,您的应用将具有更强的适应性。 + +

    + +

    正如上文所述,处理此类重启的最佳方法 +是利用 {@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}(或 {@link +android.app.Activity#onCreate onCreate()})保存并恢复 Activity 的状态。

    + +

    如需了解有关运行时发生的配置变更以及应对方法的详细信息,请阅读处理运行时变更指南。 + +

    + + + +

    协调 Activity

    + +

    当一个 Activity 启动另一个 Activity 时,它们都会体验到生命周期转变。第一个 Activity 暂停并停止(但如果它在后台仍然可见,则不会停止)时,系统会创建另一个 Activity。 + +如果这些 Activity 共用保存到磁盘或其他地方的数据,必须了解的是,在创建第二个 Activity 前,第一个 Activity 不会完全停止。更确切地说,启动第二个 Activity 的过程与停止第一个 Activity 的过程存在重叠。 + + +

    + +

    生命周期回调的顺序经过明确定义,当两个 Activity 位于同一进程,并且由一个 Activity 启动另一个 Activity 时,其定义尤其明确。 +以下是当 Activity A +启动 Activity B 时一系列操作的发生顺序:

    + +
      +
    1. Activity A 的 {@link android.app.Activity#onPause onPause()} 方法执行。
    2. + +
    3. Activity B 的 {@link android.app.Activity#onCreate onCreate()}、{@link +android.app.Activity#onStart onStart()} 和 {@link android.app.Activity#onResume onResume()} +方法依次执行。(Activity B 现在具有用户焦点。)
    4. + +
    5. 然后,如果 Activity A 在屏幕上不再可见,则其 {@link +android.app.Activity#onStop onStop()} 方法执行。
    6. +
    + +

    您可以利用这种可预测的生命周期回调顺序管理从一个 Activity 到另一个 Activity 的信息转变。 +例如,如果您必须在第一个 Activity 停止时向数据库写入数据,以便下一个 Activity 能够读取该数据,则应在 +{@link android.app.Activity#onPause onPause()} +而不是 {@link +android.app.Activity#onStop onStop()} 执行期间向数据库写入数据。

    + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/bound-services.jd b/docs/html-intl/intl/zh-cn/guide/components/bound-services.jd new file mode 100644 index 0000000000000000000000000000000000000000..ed6aaf6ecbf85ec3689785f2e1c87588122ed6a4 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=绑定服务 +parent.title=服务 +parent.link=services.html +@jd:body + + +
    +
      +

      本文内容

      +
        +
      1. 基础知识
      2. +
      3. 创建绑定服务 +
          +
        1. 扩展 Binder 类
        2. +
        3. 使用 Messenger
        4. +
        +
      4. +
      5. 绑定到服务
      6. +
      7. 管理绑定服务的生命周期
      8. +
      + +

      关键类

      +
        +
      1. {@link android.app.Service}
      2. +
      3. {@link android.content.ServiceConnection}
      4. +
      5. {@link android.os.IBinder}
      6. +
      + +

      示例

      +
        +
      1. {@code + RemoteService}
      2. +
      3. {@code + LocalService}
      4. +
      + +

      另请参阅

      +
        +
      1. 服务
      2. +
      +
    + + +

    绑定服务是客户端-服务器接口中的服务器。绑定服务可让组件(例如 Activity)绑定到服务、发送请求、接收响应,甚至执行进程间通信 (IPC)。 + +绑定服务通常只在为其他应用组件服务时处于活动状态,不会无限期在后台运行。 +

    + +

    本文向您介绍如何创建绑定服务,包括如何绑定到来自其他应用组件的服务。 +不过,您还应参阅服务文档,了解有关一般服务的更多信息,例如:如何利用服务传送通知、如何将服务设置为在前台运行等等。 + +

    + + +

    基础知识

    + +

    绑定服务是 {@link android.app.Service} +类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 {@link android.app.Service#onBind onBind()} +回调方法。该方法返回的 +{@link android.os.IBinder} +对象定义了客户端用来与服务进行交互的编程接口。

    + + + +

    客户端可通过调用 {@link android.content.Context#bindService +bindService()} 绑定到服务。调用时,它必须提供 {@link +android.content.ServiceConnection} 的实现,后者会监控与服务的连接。{@link +android.content.Context#bindService bindService()} 方法会立即无值返回,但当 +Android +系统创建客户端与服务之间的连接时,会调用 {@link +android.content.ServiceConnection} 上的 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()},向客户端传递用来与服务通信的 +{@link android.os.IBinder}。

    + +

    多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 +{@link android.app.Service#onBind onBind()} +方法来检索 {@link android.os.IBinder}。系统随后无需再次调用 +{@link android.app.Service#onBind onBind()},便可将同一 {@link android.os.IBinder} 传递至任何其他绑定的客户端。

    + +

    当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 +{@link android.content.Context#startService startService()} 也启动了该服务)。

    + +

    当您实现绑定服务时,最重要的环节是定义您的 +{@link android.app.Service#onBind onBind()} 回调方法返回的接口。您可以通过几种不同的方法定义服务的 +{@link android.os.IBinder} +接口,下文对这些方法逐一做了阐述。

    + + + +

    创建绑定服务

    + +

    创建提供绑定的服务时,您必须提供 {@link android.os.IBinder},用以提供客户端用来与服务进行交互的编程接口。 +您可以通过三种方法定义接口: +

    + +
    +
    扩展 Binder 类
    +
    如果服务是供您的自有应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展 +{@link android.os.Binder} +类并从 {@link android.app.Service#onBind onBind()} +返回它的一个实例来创建接口。客户端收到 {@link android.os.Binder} +后,可利用它直接访问 {@link android.os.Binder} 实现中乃至 {@link android.app.Service} +中可用的公共方法。 +

    如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 +不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。 +

    + +
    使用 Messenger
    +
    如需让接口跨不同的进程工作,则可使用 +{@link android.os.Messenger} 为服务创建接口。服务可以这种方式定义对应于不同类型 +{@link +android.os.Message} 对象的 {@link android.os.Handler}。此 {@link android.os.Handler} +是 {@link android.os.Messenger} 的基础,后者随后可与客户端分享一个 {@link android.os.IBinder},从而让客户端能利用 {@link +android.os.Message} +对象向服务发送命令。此外,客户端还可定义自有 +{@link android.os.Messenger},以便服务回传消息。 +

    这是执行进程间通信 (IPC) 的最简单方法,因为 {@link +android.os.Messenger} +会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。

    +
    + +
    使用 AIDL
    +
    AIDL(Android +接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 +IPC。之前采用 {@link android.os.Messenger} 的方法实际上是以 AIDL +作为其底层结构。如上所述,{@link android.os.Messenger} +会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。不过,如果您想让服务同时处理多个请求,则可直接使用 +AIDL。 +在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。 +

    如需直接使用 +AIDL,您必须创建一个定义编程接口的 {@code .aidl} 文件。Android SDK +工具利用该文件生成一个实现接口并处理 IPC +的抽象类,您随后可在服务内对其进行扩展。

    +
    +
    + +

    注:大多数应用“都不会”使用 +AIDL +来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL +并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 +AIDL,请参阅 AIDL +文档。

    + + + + +

    扩展 Binder 类

    + +

    如果您的服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 +{@link android.os.Binder} +类,让您的客户端通过该类直接访问服务中的公共方法。

    + +

    注:此方法只有在客户端和服务位于同一应用和进程内这一最常见的情况下方才有效。 +例如,对于需要将 Activity 绑定到在后台播放音乐的自有服务的音乐应用,此方法非常有效。 + +

    + +

    以下是具体的设置方法:

    +
      +
    1. 在您的服务中,创建一个可满足下列任一要求的 {@link android.os.Binder} 实例: +
        +
      • 包含客户端可调用的公共方法
      • +
      • 返回当前 {@link android.app.Service} +实例,其中包含客户端可调用的公共方法
      • +
      • 或返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法 +
      • +
      +
    2. 从 {@link +android.app.Service#onBind onBind()} 回调方法返回此 {@link android.os.Binder} 实例。
    3. +
    4. 在客户端中,从 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回调方法接收 +{@link android.os.Binder},并使用提供的方法调用绑定服务。
    5. +
    + +

    注:之所以要求服务和客户端必须在同一应用内,是为了便于客户端转换返回的对象和正确调用其 +API。服务和客户端还必须在同一进程内,因为此方法不执行任何跨进程编组。 + +

    + +

    例如,以下这个服务可让客户端通过 +{@link android.os.Binder} 实现访问服务中的方法:

    + +
    +public class LocalService extends Service {
    +    // Binder given to clients
    +    private final IBinder mBinder = new LocalBinder();
    +    // Random number generator
    +    private final Random mGenerator = new Random();
    +
    +    /**
    +     * Class used for the client Binder.  Because we know this service always
    +     * runs in the same process as its clients, we don't need to deal with IPC.
    +     */
    +    public class LocalBinder extends Binder {
    +        LocalService getService() {
    +            // Return this instance of LocalService so clients can call public methods
    +            return LocalService.this;
    +        }
    +    }
    +
    +    @Override
    +    public IBinder onBind(Intent intent) {
    +        return mBinder;
    +    }
    +
    +    /** method for clients */
    +    public int getRandomNumber() {
    +      return mGenerator.nextInt(100);
    +    }
    +}
    +
    + +

    {@code LocalBinder} 为客户端提供 {@code getService()} 方法,以检索 {@code LocalService} +的当前实例。这样,客户端便可调用服务中的公共方法。 +例如,客户端可调用服务中的 {@code getRandomNumber()}。

    + +

    点击按钮时,以下这个 Activity 会绑定到 {@code LocalService} 并调用 +{@code getRandomNumber()}:

    + +
    +public class BindingActivity extends Activity {
    +    LocalService mService;
    +    boolean mBound = false;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +    }
    +
    +    @Override
    +    protected void onStart() {
    +        super.onStart();
    +        // Bind to LocalService
    +        Intent intent = new Intent(this, LocalService.class);
    +        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    +    }
    +
    +    @Override
    +    protected void onStop() {
    +        super.onStop();
    +        // Unbind from the service
    +        if (mBound) {
    +            unbindService(mConnection);
    +            mBound = false;
    +        }
    +    }
    +
    +    /** Called when a button is clicked (the button in the layout file attaches to
    +      * this method with the android:onClick attribute) */
    +    public void onButtonClick(View v) {
    +        if (mBound) {
    +            // Call a method from the LocalService.
    +            // However, if this call were something that might hang, then this request should
    +            // occur in a separate thread to avoid slowing down the activity performance.
    +            int num = mService.getRandomNumber();
    +            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
    +        }
    +    }
    +
    +    /** Defines callbacks for service binding, passed to bindService() */
    +    private ServiceConnection mConnection = new ServiceConnection() {
    +
    +        @Override
    +        public void onServiceConnected(ComponentName className,
    +                IBinder service) {
    +            // We've bound to LocalService, cast the IBinder and get LocalService instance
    +            LocalBinder binder = (LocalBinder) service;
    +            mService = binder.getService();
    +            mBound = true;
    +        }
    +
    +        @Override
    +        public void onServiceDisconnected(ComponentName arg0) {
    +            mBound = false;
    +        }
    +    };
    +}
    +
    + +

    上例说明了客户端如何使用 {@link android.content.ServiceConnection} 的实现和 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} +回调绑定到服务。下文更详细介绍了绑定到服务的过程。 +

    + +

    注:上例并未显式取消与服务的绑定,但所有客户端都应在适当的时间(例如当 Activity 暂停时)取消绑定。 +

    + +

    如需查看更多示例代码,请参阅 ApiDemos 中的 {@code +LocalService.java} 类和 {@code +LocalServiceActivities.java} 类。

    + + + + + +

    使用 Messenger

    + + + +

    如需让服务与远程进程通信,则可使用 {@link android.os.Messenger} +为您的服务提供接口。利用此方法,您无需使用 +AIDL 便可执行进程间通信 (IPC)。

    + +

    以下是 {@link android.os.Messenger} 的使用方法摘要:

    + +
      +
    • 服务实现一个 +{@link android.os.Handler},由其接收来自客户端的每个调用的回调
    • +
    • {@link android.os.Handler} 用于创建 {@link android.os.Messenger} +对象(对 {@link android.os.Handler} 的引用)
    • +
    • {@link android.os.Messenger} 创建一个 {@link android.os.IBinder},服务通过 {@link android.app.Service#onBind onBind()} +使其返回客户端
    • +
    • 客户端使用 {@link android.os.IBinder} +将 {@link android.os.Messenger}(引用服务的 {@link android.os.Handler})实例化,然后使用后者将 {@link android.os.Message} +对象发送给服务
    • +
    • 服务在其 {@link +android.os.Handler} 中(具体地讲,是在 {@link android.os.Handler#handleMessage +handleMessage()} 方法中)接收每个 {@link android.os.Message}
    • +
    + + +

    这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”({@link android.os.Message} +对象)是服务在其 {@link android.os.Handler} +中接收的。

    + +

    以下是一个使用 {@link android.os.Messenger} 接口的简单服务示例:

    + +
    +public class MessengerService extends Service {
    +    /** Command to the service to display a message */
    +    static final int MSG_SAY_HELLO = 1;
    +
    +    /**
    +     * Handler of incoming messages from clients.
    +     */
    +    class IncomingHandler extends Handler {
    +        @Override
    +        public void handleMessage(Message msg) {
    +            switch (msg.what) {
    +                case MSG_SAY_HELLO:
    +                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
    +                    break;
    +                default:
    +                    super.handleMessage(msg);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Target we publish for clients to send messages to IncomingHandler.
    +     */
    +    final Messenger mMessenger = new Messenger(new IncomingHandler());
    +
    +    /**
    +     * When binding to the service, we return an interface to our messenger
    +     * for sending messages to the service.
    +     */
    +    @Override
    +    public IBinder onBind(Intent intent) {
    +        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
    +        return mMessenger.getBinder();
    +    }
    +}
    +
    + +

    请注意,服务就是在 {@link android.os.Handler} +的 {@link android.os.Handler#handleMessage handleMessage()} 方法中接收传入的 +{@link android.os.Message},并根据 {@link android.os.Message#what} 成员决定下一步操作。

    + +

    客户端只需根据服务返回的 {@link +android.os.IBinder} 创建一个 {@link android.os.Messenger},然后利用 {@link +android.os.Messenger#send send()} 发送一条消息。例如,以下就是一个绑定到服务并向服务传递 +{@code MSG_SAY_HELLO} 消息的简单 Activity:

    + +
    +public class ActivityMessenger extends Activity {
    +    /** Messenger for communicating with the service. */
    +    Messenger mService = null;
    +
    +    /** Flag indicating whether we have called bind on the service. */
    +    boolean mBound;
    +
    +    /**
    +     * Class for interacting with the main interface of the service.
    +     */
    +    private ServiceConnection mConnection = new ServiceConnection() {
    +        public void onServiceConnected(ComponentName className, IBinder service) {
    +            // This is called when the connection with the service has been
    +            // established, giving us the object we can use to
    +            // interact with the service.  We are communicating with the
    +            // service using a Messenger, so here we get a client-side
    +            // representation of that from the raw IBinder object.
    +            mService = new Messenger(service);
    +            mBound = true;
    +        }
    +
    +        public void onServiceDisconnected(ComponentName className) {
    +            // This is called when the connection with the service has been
    +            // unexpectedly disconnected -- that is, its process crashed.
    +            mService = null;
    +            mBound = false;
    +        }
    +    };
    +
    +    public void sayHello(View v) {
    +        if (!mBound) return;
    +        // Create and send a message to the service, using a supported 'what' value
    +        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
    +        try {
    +            mService.send(msg);
    +        } catch (RemoteException e) {
    +            e.printStackTrace();
    +        }
    +    }
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +    }
    +
    +    @Override
    +    protected void onStart() {
    +        super.onStart();
    +        // Bind to the service
    +        bindService(new Intent(this, MessengerService.class), mConnection,
    +            Context.BIND_AUTO_CREATE);
    +    }
    +
    +    @Override
    +    protected void onStop() {
    +        super.onStop();
    +        // Unbind from the service
    +        if (mBound) {
    +            unbindService(mConnection);
    +            mBound = false;
    +        }
    +    }
    +}
    +
    + +

    请注意,此示例并未说明服务如何对客户端作出响应。如果您想让服务作出响应,则还需要在客户端中创建一个 +{@link android.os.Messenger}。然后,当客户端收到 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 回调时,会向服务发送一条 +{@link android.os.Message},并在其 +{@link android.os.Messenger#send send()} 方法的 {@link android.os.Message#replyTo} +参数中包含客户端的 {@link android.os.Messenger}。

    + +

    如需查看如何提供双向消息传递的示例,请参阅 {@code +MessengerService.java}(服务)和 {@code +MessengerServiceActivities.java}(客户端)示例。

    + + + + + +

    绑定到服务

    + +

    应用组件(客户端)可通过调用 {@link android.content.Context#bindService bindService()} +绑定到服务。Android +系统随后调用服务的 {@link android.app.Service#onBind +onBind()} 方法,该方法返回用于与服务交互的 {@link android.os.IBinder}。

    + +

    绑定是异步的。{@link android.content.Context#bindService +bindService()} 会立即返回,“绝对不会”使 {@link android.os.IBinder} +返回客户端。要接收 {@link android.os.IBinder},客户端必须创建一个 {@link +android.content.ServiceConnection} 实例,并将其传递给 {@link android.content.Context#bindService +bindService()}。{@link android.content.ServiceConnection} 包括一个回调方法,系统通过调用它来传递 +{@link android.os.IBinder}。

    + +

    注:只有 Activity、服务和内容提供程序可以绑定到服务—您无法从广播接收器绑定到服务。 +

    + +

    因此,要想从您的客户端绑定到服务,您必须:

    +
      +
    1. 实现 {@link android.content.ServiceConnection}。 +

      您的实现必须重写两个回调方法:

      +
      +
      {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
      +
      系统会调用该方法以传递服务的 {@link android.app.Service#onBind onBind()} +方法返回的 {@link android.os.IBinder}。
      +
      {@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}
      +
      Android +系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法。当客户端取消绑定时,系统“绝对不会”调用该方法。 +
      +
      +
    2. +
    3. 调用 {@link +android.content.Context#bindService bindService()} 以传递 {@link +android.content.ServiceConnection} 实现。
    4. +
    5. 当系统调用您的 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} +回调方法时,您可以使用接口定义的方法开始调用服务。
    6. +
    7. 要断开与服务的连接,请调用 {@link +android.content.Context#unbindService unbindService()}。 +

      当您的客户端被销毁时,它将取消与服务的绑定,但您应该始终在完成与服务的交互时或您的 Activity 暂停时取消绑定,以便服务能够在未被占用时关闭。 + +(下文更详细地阐述了绑定和取消绑定的适当时机。) +

      +
    8. +
    + +

    例如,以下代码段通过扩展 +Binder 类将客户端与上面创建的服务相连,因此它只需将返回的 {@link android.os.IBinder} +转换为 {@code LocalService} 类并请求 {@code +LocalService} 实例:

    + +
    +LocalService mService;
    +private ServiceConnection mConnection = new ServiceConnection() {
    +    // Called when the connection with the service is established
    +    public void onServiceConnected(ComponentName className, IBinder service) {
    +        // Because we have bound to an explicit
    +        // service that is running in our own process, we can
    +        // cast its IBinder to a concrete class and directly access it.
    +        LocalBinder binder = (LocalBinder) service;
    +        mService = binder.getService();
    +        mBound = true;
    +    }
    +
    +    // Called when the connection with the service disconnects unexpectedly
    +    public void onServiceDisconnected(ComponentName className) {
    +        Log.e(TAG, "onServiceDisconnected");
    +        mBound = false;
    +    }
    +};
    +
    + +

    客户端可通过将此 {@link android.content.ServiceConnection} 传递至 {@link android.content.Context#bindService bindService()} +绑定到服务。例如:

    + +
    +Intent intent = new Intent(this, LocalService.class);
    +bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    +
    + +
      +
    • {@link android.content.Context#bindService bindService()} +的第一个参数是一个 +{@link android.content.Intent},用于显式命名要绑定的服务(但 Intent 可能是隐式的)
    • +
    • 第二个参数是 {@link android.content.ServiceConnection} 对象
    • +
    • 第三个参数是一个指示绑定选项的标志。它通常应该是 {@link +android.content.Context#BIND_AUTO_CREATE},以便创建尚未激活的服务。 +其他可能的值为 {@link android.content.Context#BIND_DEBUG_UNBIND} +和 {@link android.content.Context#BIND_NOT_FOREGROUND},或 {@code 0}(表示无)。
    • +
    + + +

    附加说明

    + +

    以下是一些有关绑定到服务的重要说明:

    +
      +
    • 您应该始终捕获 +{@link android.os.DeadObjectException} 异常,它们是在连接中断时引发的。这是远程方法引发的唯一异常
    • +
    • 对象是跨进程计数的引用
    • +
    • 您通常应该在客户端生命周期的匹配引入 (bring-up) 和退出 (tear-down) 时刻期间配对绑定和取消绑定。 +例如: +
        +
      • 如果您只需要在 Activity 可见时与服务交互,则应在 {@link android.app.Activity#onStart onStart()} +期间绑定,在 {@link +android.app.Activity#onStop onStop()} 期间取消绑定。
      • +
      • 如果您希望 Activity 在后台停止运行状态下仍可接收响应,则可在 +{@link android.app.Activity#onCreate onCreate()} 期间绑定,在 {@link android.app.Activity#onDestroy onDestroy()} +期间取消绑定。请注意,这意味着您的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当您提高该进程的权重时,系统终止该进程的可能性会增加 + + +
      • +
      +

      注:通常情况下,切勿在 Activity 的 {@link android.app.Activity#onResume onResume()} +和 {@link +android.app.Activity#onPause onPause()} +期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,您应该使发生在这些转换期间的处理保持在最低水平。此外,如果您的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务。 + + +(Activity文档中介绍了这种有关 Activity 如何协调其生命周期的 Activity 转换。) + +

      +
    + +

    如需查看更多显示如何绑定到服务的示例代码,请参阅 ApiDemos 中的 {@code +RemoteService.java} 类。

    + + + + + +

    管理绑定服务的生命周期

    + +

    当服务与所有客户端之间的绑定全部取消时,Android +系统便会销毁服务(除非还使用 {@link android.app.Service#onStartCommand onStartCommand()} 启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理—Android +系统会根据它是否绑定到任何客户端代您管理。 +

    + +

    不过,如果您选择实现 {@link android.app.Service#onStartCommand +onStartCommand()} +回调方法,则您必须显式停止服务,因为系统现在已将服务视为已启动。在此情况下,服务将一直运行到其通过 {@link android.app.Service#stopSelf()} +自行停止,或其他组件调用 {@link +android.content.Context#stopService stopService()} +为止,无论其是否绑定到任何客户端。

    + +

    此外,如果您的服务已启动并接受绑定,则当系统调用您的 {@link android.app.Service#onUnbind onUnbind()} 方法时,如果您想在客户端下一次绑定到服务时接收 +{@link android.app.Service#onRebind +onRebind()} 调用(而不是接收 {@link +android.app.Service#onBind onBind()} 调用),则可选择返回 +{@code true}。{@link android.app.Service#onRebind +onRebind()} 返回空值,但客户端仍在其 {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回调中接收 +{@link android.os.IBinder}。下文图 1 +说明了这种生命周期的逻辑。

    + + + +

    图 1. 允许绑定的已启动服务的生命周期。 +

    + + +

    如需了解有关已启动服务生命周期的详细信息,请参阅服务文档。

    + + + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/fragments.jd b/docs/html-intl/intl/zh-cn/guide/components/fragments.jd new file mode 100644 index 0000000000000000000000000000000000000000..a4c2cbb824991f8bf5c566ec8914e0fc0509fbea --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=片段 +parent.title=Activity +parent.link=activities.html +@jd:body + +
    +
    +

    本文内容

    +
      +
    1. 设计原理
    2. +
    3. 创建片段 +
        +
      1. 添加用户界面
      2. +
      3. 向 Activity 添加片段
      4. +
      +
    4. +
    5. 管理片段
    6. +
    7. 执行片段事务
    8. +
    9. 与 Activity 通信 +
        +
      1. 创建对 Activity 的事件回调
      2. +
      3. 向操作栏添加项目
      4. +
      +
    10. +
    11. 处理片段生命周期 +
        +
      1. 与 Activity 生命周期协调一致
      2. +
      +
    12. +
    13. 示例
    14. +
    + +

    关键类

    +
      +
    1. {@link android.app.Fragment}
    2. +
    3. {@link android.app.FragmentManager}
    4. +
    5. {@link android.app.FragmentTransaction}
    6. +
    + +

    另请参阅

    +
      +
    1. 利用片段构建动态 UI
    2. +
    3. 支持平板电脑和手机 +
    4. +
    +
    +
    + +

    {@link android.app.Fragment} 表示 +{@link android.app.Activity} 中的行为或用户界面部分。您可以将多个片段组合在一个 Activity 中来构建多窗格 +UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或删除片段(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。 + + +

    + +

    片段必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 +例如,当 Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。 +不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除它们。 + +当您执行此类片段事务时,您也可以将其添加到由 Activity 管理的返回栈—Activity 中的每个返回栈条目都是一条已发生片段事务的记录。 + + +返回栈让用户可以通过按“返回”按钮撤消片段事务(后退)。 +

    + +

    当您将片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 {@link +android.view.ViewGroup} +内部,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为 +{@code <fragment>} +元素插入您的 Activity 布局中,或者通过将其添加到某个现有 +{@link android.view.ViewGroup},利用应用代码进行插入。不过,片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 +UI +的片段用作 Activity 的不可见工作线程。

    + +

    本文描述如何在开发您的应用时使用片段,包括将片段添加到 Activity 返回栈时如何保持其状态、如何与 Activity 及 Activity 中的其他片段共享事件、如何为 Activity 的操作栏发挥作用等等。 + + +

    + + +

    设计原理

    + +

    Android 在 Android 3.0(API 11 级)中引入了片段,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI +设计提供支持。由于平板电脑的屏幕比手机屏幕大得多,因此可用于组合和交换 +UI +组件的空间更大。利用片段实现此类设计时,您无需管理对视图层次结构的复杂更改。 +通过将 Activity 布局分成片段,您可以在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。 + +

    + +

    例如,新闻应用可以使用一个片段在左侧显示文章列表,使用另一个片段在右侧显示文章—两个片段并排显示在一个 Activity 中,每个片段都具有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。 + + +因此,用户不需要使用一个 Activity 来选择文章,然后使用另一个 Activity 来阅读文章,而是可以在同一个 Activity 内选择文章并进行阅读,如图 +1 +中的平板电脑布局所示。

    + +

    您应该将每个片段都设计为可重复使用的模块化 Activity 组件。也就是说,由于每个片段都会通过各自的生命周期回调来定义其自己的布局和行为,您可以将一个片段加入多个 Activity,因此,您应该采用可复用式设计,避免直接从某个片段直接操纵另一个片段。 + + +这特别重要,因为模块化片段让您可以通过更改片段的组合方式来适应不同的屏幕尺寸。 +在设计可同时支持平板电脑和手机的应用时,您可以在不同的布局配置中重复使用您的片段,以根据可用的屏幕空间优化用户体验。 + +例如,在手机上,如果不能在同一 Activity 内储存多个片段,可能必须利用单独片段来实现单窗格 +UI。 +

    + + +

    图 1. 有关由片段定义的两个 +UI +模块如何适应不同设计的示例:通过组合成一个 Activity 来适应平板电脑设计,通过单独片段来适应手机设计。

    + +

    例如—仍然以新闻应用为例—在平板电脑尺寸的设备上运行时,该应用可以在Activity +A 中嵌入两个片段。不过,在手机尺寸的屏幕上,没有足以储存两个片段的空间,因此Activity +A +只包括用于显示文章列表的片段,当用户选择文章时,它会启动Activity +B,其中包括用于阅读文章的第二个片段。因此,应用可通过重复使用不同组合的片段来同时支持平板电脑和手机,如图 +1 +所示。

    + +

    如需了解有关通过利用不同片段组合来适应不同屏幕配置这种方法设计应用的详细信息,请参阅支持平板电脑和手机指南。 +

    + + + +

    创建片段

    + +
    + +

    图 2. 片段的生命周期(其 Activity 运行时)。 +

    +
    + +

    要想创建片段,您必须创建 {@link android.app.Fragment} +的子类(或已有其子类)。{@link android.app.Fragment} 类的代码与 +{@link android.app.Activity} 非常相似。它包含与 Activity 类似的回调方法,如 +{@link android.app.Fragment#onCreate onCreate()}、{@link android.app.Fragment#onStart onStart()}、{@link android.app.Fragment#onPause onPause()} +和 {@link android.app.Fragment#onStop onStop()}。实际上,如果您要将现有 +Android +应用转换为使用片段,可能只需将代码从 Activity 的回调方法移入片段相应的回调方法中。 +

    + +

    通常,您至少应实现以下生命周期方法:

    + +
    +
    {@link android.app.Fragment#onCreate onCreate()}
    +
    系统会在创建片段时调用此方法。您应该在实现内初始化您想在片段暂停或停止后恢复时保留的必需片段组件。 + +
    +
    {@link android.app.Fragment#onCreateView onCreateView()}
    +
    系统会在片段首次绘制其用户界面时调用此方法。 +要想为您的片段绘制 +UI,您从此方法中返回的 {@link android.view.View} 必须是片段布局的根视图。如果片段未提供 +UI,您可以返回 null。
    +
    {@link android.app.Activity#onPause onPause()}
    +
    系统将此方法作为用户离开片段的第一个信号(但并不总是意味着此片段会被销毁)进行调用。 +您通常应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。 + +
    +
    + +

    大多数应用都应该至少为每个片段实现这三个方法,但您还应该使用几种其他回调方法来处理片段生命周期的各个阶段。 + +处理片段生命周期部分对所有生命周期回调方法做了更详尽的阐述。 +

    + + +

    您可能还想扩展几个子类,而不是 {@link +android.app.Fragment} 基类:

    + +
    +
    {@link android.app.DialogFragment}
    +
    显示浮动对话框。使用此类创建对话框可有效地替代使用 +{@link android.app.Activity} +类中的对话框帮助程序方法,因为您可以将片段对话框纳入由 Activity 管理的片段返回栈,从而使用户能够返回清除的片段。 +
    + +
    {@link android.app.ListFragment}
    +
    显示由适配器(如 {@link +android.widget.SimpleCursorAdapter})管理的一系列项目,类似于 {@link android.app.ListActivity}。它提供了几种管理列表视图的方法,如用于处理点击事件的 +{@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} +回调。
    + +
    {@link android.preference.PreferenceFragment}
    +
    以列表形式显示 {@link android.preference.Preference} 对象的层次结构,类似于 +{@link android.preference.PreferenceActivity}。这在为您的应用创建“设置” Activity 时很有用处。 +
    +
    + + +

    添加用户界面

    + +

    片段通常用作 Activity 用户界面的一部分,将其自己的布局融入 Activity。 +

    + +

    要想为片段提供布局,您必须实现 {@link +android.app.Fragment#onCreateView onCreateView()} 回调方法,Android +系统会在片段需要绘制其布局时调用该方法。您对此方法的实现返回的 +{@link android.view.View} 必须是片段布局的根视图。

    + +

    注:如果您的片段是 {@link +android.app.ListFragment} 的子类,则默认实现会从 +{@link android.app.Fragment#onCreateView onCreateView()} 返回一个 {@link android.widget.ListView},因此您无需实现它。

    + +

    要想从 {@link +android.app.Fragment#onCreateView onCreateView()} 返回布局,您可以通过 XML 中定义的布局资源来扩展布局。为帮助您执行此操作,{@link android.app.Fragment#onCreateView onCreateView()} +提供了一个 +{@link android.view.LayoutInflater} 对象。

    + +

    例如,以下这个 {@link android.app.Fragment} 子类从 {@code example_fragment.xml} +文件加载布局:

    + +
    +public static class ExampleFragment extends Fragment {
    +    @Override
    +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    +                             Bundle savedInstanceState) {
    +        // Inflate the layout for this fragment
    +        return inflater.inflate(R.layout.example_fragment, container, false);
    +    }
    +}
    +
    + + + +

    传递至 {@link android.app.Fragment#onCreateView +onCreateView()} 的 {@code container} +参数是您的片段布局将插入到的父 +{@link android.view.ViewGroup}(来自 Activity 的布局)。{@code savedInstanceState} +参数是在恢复片段时,提供上一片段实例相关数据的 +{@link android.os.Bundle}(处理片段生命周期部分对恢复状态做了详细阐述)。 +

    + +

    {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} +方法带有三个参数:

    +
      +
    • 您想要扩展的布局的资源 ID;
    • +
    • 将作为扩展布局父项的 {@link android.view.ViewGroup}。传递 {@code +container} +对系统向扩展布局的根视图(由其所属的父视图指定)应用布局参数具有重要意义;
    • +
    • 指示是否应该在扩展期间将扩展布局附加至 {@link +android.view.ViewGroup}(第二个参数)的布尔值。(在本例中,其值为 +false,因为系统已经将扩展布局插入 {@code +container}—传递 true 值会在最终布局中创建一个多余的视图组。)
    • +
    + +

    现在,您已经了解了如何创建提供布局的片段。接下来,您需要将该片段添加到您的 Activity 中。 +

    + + + +

    向 Activity 添加片段

    + +

    通常,片段向宿主 Activity 贡献一部分 +UI,作为 Activity 总体视图层次结构的一部分嵌入到 Activity 中。可以通过两种方式向 Activity 布局添加片段: +

    + +
      +
    • 在 Activity 的布局文件内声明片段 +

      在本例中,您可以将片段当作视图来为其指定布局属性。 +例如,以下是一个具有两个片段的 Activity 的布局文件: +

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:orientation="horizontal"
      +    android:layout_width="match_parent"
      +    android:layout_height="match_parent">
      +    <fragment android:name="com.example.news.ArticleListFragment"
      +            android:id="@+id/list"
      +            android:layout_weight="1"
      +            android:layout_width="0dp"
      +            android:layout_height="match_parent" />
      +    <fragment android:name="com.example.news.ArticleReaderFragment"
      +            android:id="@+id/viewer"
      +            android:layout_weight="2"
      +            android:layout_width="0dp"
      +            android:layout_height="match_parent" />
      +</LinearLayout>
      +
      +

      {@code <fragment>} 中的 {@code android:name} 属性指定要在布局中实例化的 {@link +android.app.Fragment} 类。

      + +

      当系统创建此 Activity 布局时,会实例化在布局中指定的每个片段,并为每个片段调用 +{@link android.app.Fragment#onCreateView onCreateView()} +方法,以检索每个片段的布局。系统会直接插入片段返回的 {@link android.view.View} 来替代 +{@code <fragment>} 元素。

      + +
      +

      注:每个片段都需要一个唯一的标识符,重启 Activity 时,系统可以使用该标识符来恢复片段(您也可以使用该标识符来捕获片段以执行某些事务,如将其删除)。 + +可以通过三种方式为片段提供 +ID:

      +
        +
      • 为 {@code android:id} 属性提供唯一 ID
      • +
      • 为 {@code android:tag} 属性提供唯一字符串
      • +
      • 如果您未给以上两个属性提供值,系统会使用容器视图的 ID +
      • +
      +
      +
    • + +
    • 或者通过编程方式将片段添加到某个现有 {@link android.view.ViewGroup} +

      您可以在 Activity 运行期间随时将片段添加到 Activity 布局中。您只需指定要将片段放入哪个 +{@link +android.view.ViewGroup}。

      +

      要想在您的 Activity 中执行片段事务(如添加、删除或替换片段),您必须使用 +{@link android.app.FragmentTransaction} 中的 API。您可以像下面这样从 +{@link android.app.Activity} 获取一个 {@link android.app.FragmentTransaction} 实例:

      + +
      +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}
      +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
      +
      + +

      然后,您可以使用 {@link +android.app.FragmentTransaction#add(int,Fragment) add()} +方法添加一个片段,指定要添加的片段以及将其插入哪个视图。例如:

      + +
      +ExampleFragment fragment = new ExampleFragment();
      +fragmentTransaction.add(R.id.fragment_container, fragment);
      +fragmentTransaction.commit();
      +
      + +

      传递到 {@link android.app.FragmentTransaction#add(int,Fragment) add()} +的第一个参数是 {@link android.view.ViewGroup},即应该放置片段的位置,由资源 +ID 指定,第二个参数是要添加的片段。

      +

      一旦您通过 +{@link android.app.FragmentTransaction} 做出了更改,就必须调用 +{@link android.app.FragmentTransaction#commit} 以使更改生效。

      +
    • +
    + + +

    添加没有 UI 的片段

    + +

    上例展示了如何向您的 Activity 添加片段以提供 +UI。不过,您还可以使用片段为 Activity 提供后台行为,而不显示额外 +UI。

    + +

    要想添加没有 UI 的片段,请使用 {@link +android.app.FragmentTransaction#add(Fragment,String)} 从 Activity 添加片段(为片段提供一个唯一的字符串“标记”,而不是视图 +ID)。这会添加片段,但由于它并不与 Activity 布局中的视图关联,因此不会收到对 +{@link +android.app.Fragment#onCreateView onCreateView()} 的调用。因此,您不需要实现该方法。

    + +

    并非只能为非 UI 片段提供字符串标记—您也可以为具有 UI +的片段提供字符串标记—但如果片段没有 +UI,则字符串标记将是标识它的唯一方式。如果您想稍后从 Activity 中获取片段,则需要使用 +{@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}。

    + +

    如需查看将没有 UI 的片段用作后台工作线程的示例 Activity,请参阅 {@code +FragmentRetainInstance.java} 示例,该示例包括在 SDK 示例(通过 +Android SDK 管理器提供)中,以 +<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java 形式位于您的系统中。

    + + + +

    管理片段

    + +

    要想管理您的 Activity 中的片段,您需要使用 {@link android.app.FragmentManager}。要想获取它,请从您的 Activity 调用 +{@link android.app.Activity#getFragmentManager()}。

    + +

    您可以使用 {@link android.app.FragmentManager} 执行的操作包括:

    + +
      +
    • 通过 {@link +android.app.FragmentManager#findFragmentById findFragmentById()}(对于在 Activity 布局中提供 UI +的片段)或 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段
    • +
    • 通过 {@link +android.app.FragmentManager#popBackStack()}(模拟用户发出的 Back 命令)将片段从返回栈中弹出
    • +
    • 通过 {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()} 注册一个侦听返回栈变化的侦听器
    • +
    + +

    如需了解有关这些方法以及其他方法的详细信息,请参阅 {@link +android.app.FragmentManager} 类文档。

    + +

    如上文所示,您也可以使用 {@link android.app.FragmentManager} +打开一个 +{@link android.app.FragmentTransaction},通过它来执行某些事务,如添加和删除片段。

    + + +

    执行片段事务

    + +

    在 Activity 中使用片段的一大优点是,可以根据用户行为通过它们执行添加、删除、替换以及其他操作。 +您提交给 Activity 的每组更改都称为事务,您可以使用 +{@link +android.app.FragmentTransaction} 中的 API 来执行一项事务。您也可以将每个事务保存到由 Activity 管理的返回栈内,从而让用户能够回退片段更改(类似于回退 Activity)。 + +

    + +

    您可以像下面这样从 {@link +android.app.FragmentManager} 获取一个 {@link android.app.FragmentTransaction} 实例:

    + +
    +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()};
    +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
    +
    + +

    每个事务都是您想要同时执行的一组更改。您可以使用 +{@link +android.app.FragmentTransaction#add add()}、{@link android.app.FragmentTransaction#remove remove()} 和 {@link android.app.FragmentTransaction#replace replace()} +等方法为给定事务设置您想要执行的所有更改。然后,要想将事务应用到 Activity,您必须调用 +{@link android.app.FragmentTransaction#commit()}。

    + + +

    不过,在您调用 {@link +android.app.FragmentTransaction#commit()} 之前,您可能想调用 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},以将事务添加到片段事务返回栈。 +该返回栈由 Activity 管理,允许用户通过按“返回” + 按钮返回上一片段状态。

    + +

    例如,以下示例说明了如何将一个片段替换成另一个片段,以及如何在返回栈中保留先前状态: +

    + +
    +// Create new fragment and transaction
    +Fragment newFragment = new ExampleFragment();
    +FragmentTransaction transaction = getFragmentManager().beginTransaction();
    +
    +// Replace whatever is in the fragment_container view with this fragment,
    +// and add the transaction to the back stack
    +transaction.replace(R.id.fragment_container, newFragment);
    +transaction.addToBackStack(null);
    +
    +// Commit the transaction
    +transaction.commit();
    +
    + +

    在上例中,{@code newFragment} +会替换目前在 {@code R.id.fragment_container} ID 所标识的布局容器中的任何片段(如有)。通过调用 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} +可将替换事务保存到返回栈,以便用户能够通过按“返回” +按钮撤消事务并回退到上一片段。

    + +

    如果您向事务添加了多个更改(如又一个 {@link +android.app.FragmentTransaction#add add()} 或 {@link android.app.FragmentTransaction#remove +remove()}),并且调用了 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},则在调用 +{@link android.app.FragmentTransaction#commit commit()} 前应用的所有更改都将作为单一事务添加到返回栈,并且“返回” +按钮会将它们一并撤消。

    + +

    向 {@link android.app.FragmentTransaction} +添加更改的顺序无关紧要,不过:

    +
      +
    • 您必须最后调用 {@link android.app.FragmentTransaction#commit()}
    • +
    • 如果您要向同一容器添加多个片段,则您添加片段的顺序将决定它们在视图层次结构中的出现顺序 +
    • +
    + +

    如果您没有在执行删除片段的事务时调用 {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()},则事务提交时该片段会被销毁,用户将无法回退到该片段。 +不过,如果您在删除片段时调用了 +{@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()},则系统会停止该片段,并在用户回退时将其恢复。 + +

    + +

    提示:对于每个片段事务,您都可以通过在提交前调用 +{@link android.app.FragmentTransaction#setTransition setTransition()} +来应用过渡动画。

    + +

    调用 {@link android.app.FragmentTransaction#commit()} +不会立即执行事务,而是在 Activity 的 UI +线程(“主”线程)可以执行该操作时再安排其在线程上运行。不过,如有必要,您也可以从 UI 线程调用 {@link +android.app.FragmentManager#executePendingTransactions()} 以立即执行 +{@link android.app.FragmentTransaction#commit()} 提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务。 +

    + +

    注意:您只能在 Activity保存其状态(用户离开 Activity)之前使用 {@link +android.app.FragmentTransaction#commit commit()} +提交事务。如果您试图在该时间点后提交,则会引发异常。 +这是因为如需恢复 Activity,则提交后的状态可能会丢失。 +对于丢失提交无关紧要的情况,请使用 {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}。

    + + + + +

    与 Activity 通信

    + +

    尽管 {@link android.app.Fragment} 是作为独立于 +{@link android.app.Activity} +的对象实现,并且可在多个 Activity 内使用,但片段的给定实例会直接绑定到包含它的 Activity。

    + +

    具体地说,片段可以通过 {@link +android.app.Fragment#getActivity()} +访问 {@link android.app.Activity} 实例,并轻松地执行在 Activity 布局中查找视图等任务。

    + +
    +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
    +
    + +

    同样地,您的 Activity 也可以使用 +{@link +android.app.FragmentManager#findFragmentById findFragmentById()} 或 {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()},通过从 {@link android.app.FragmentManager} 获取对 {@link android.app.Fragment} 的引用来调用片段中的方法。例如:

    + +
    +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
    +
    + + +

    创建对 Activity 的事件回调

    + +

    在某些情况下,您可能需要通过片段与 Activity 共享事件。执行此操作的一个好方法是,在片段内定义一个回调接口,并要求宿主 Activity 实现它。 + +当 Activity 通过该接口收到回调时,可以根据需要与布局中的其他片段共享这些信息。 +

    + +

    例如,如果一个新闻应用的 Activity 有两个片段 —一个用于显示文章列表(片段 A),另一个用于显示文章(片段 B)—,那么片段 A必须在列表项被选定后告知 Activity,以便它告知片段 B 显示该文章。 + +在本例中,{@code OnArticleSelectedListener} +接口在片段 A 内声明:

    + +
    +public static class FragmentA extends ListFragment {
    +    ...
    +    // Container Activity must implement this interface
    +    public interface OnArticleSelectedListener {
    +        public void onArticleSelected(Uri articleUri);
    +    }
    +    ...
    +}
    +
    + +

    然后,该片段的宿主 Activity 会实现 +{@code OnArticleSelectedListener} +接口并替代 {@code onArticleSelected()},将来自片段 A +的事件通知片段 B。为确保宿主 Activity 实现此界面,片段 A 的 {@link +android.app.Fragment#onAttach onAttach()} +回调方法(系统在向 Activity 添加片段时调用的方法)会通过转换传递到 +{@link android.app.Fragment#onAttach +onAttach()} 中的 {@link android.app.Activity} 来实例化 {@code OnArticleSelectedListener} 的实例:

    + +
    +public static class FragmentA extends ListFragment {
    +    OnArticleSelectedListener mListener;
    +    ...
    +    @Override
    +    public void onAttach(Activity activity) {
    +        super.onAttach(activity);
    +        try {
    +            mListener = (OnArticleSelectedListener) activity;
    +        } catch (ClassCastException e) {
    +            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
    +        }
    +    }
    +    ...
    +}
    +
    + +

    如果 Activity 未实现界面,则片段会引发 +{@link java.lang.ClassCastException}。实现时,{@code mListener} +成员会保留对 Activity 的 {@code OnArticleSelectedListener} 实现的引用,以便片段 A 可以通过调用 +{@code OnArticleSelectedListener} +界面定义的方法与 Activity 共享事件。例如,如果片段 A 是 +{@link android.app.ListFragment} +的一个扩展,则用户每次点击列表项时,系统都会调用片段中的 {@link android.app.ListFragment#onListItemClick +onListItemClick()},然后该方法会调用 {@code onArticleSelected()} +以与 Activity 共享事件:

    + +
    +public static class FragmentA extends ListFragment {
    +    OnArticleSelectedListener mListener;
    +    ...
    +    @Override
    +    public void onListItemClick(ListView l, View v, int position, long id) {
    +        // Append the clicked item's row ID with the content provider Uri
    +        Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
    +        // Send the event and Uri to the host activity
    +        mListener.onArticleSelected(noteUri);
    +    }
    +    ...
    +}
    +
    + +

    传递到 {@link +android.app.ListFragment#onListItemClick onListItemClick()} 的 {@code id} 参数是被点击项的行 +ID,即 Activity(或其他片段)用来从应用的 {@link +android.content.ContentProvider} 获取文章的 ID。

    + +

    内容提供程序文档中提供了有关内容提供程序用法的更多详情。 +

    + + + +

    向操作栏添加项目

    + +

    您的片段可以通过实现 +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()} 向 Activity 的选项菜单(并因此向操作栏)贡献菜单项。不过,为了使此方法能够收到调用,您必须在 +{@link +android.app.Fragment#onCreate(Bundle) onCreate()} 期间调用 {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()},以指示片段想要向选项菜单添加菜单项(否则,片段将不会收到对 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} +的调用)。

    + +

    您之后从片段添加到选项菜单的任何菜单项都将追加到现有菜单项之后。 +选定菜单项时,片段还会收到对 {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} +的回调。

    + +

    您还可以通过调用 {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()},在片段布局中注册一个视图来提供上下文菜单。用户打开上下文菜单时,片段会收到对 +{@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()} 的调用。当用户选择某个菜单项时,片段会收到对 {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()} 的调用。

    + +

    注:尽管您的片段会收到与其添加的每个菜单项对应的菜单项选定回调,但当用户选择菜单项时,Activity 会首先收到相应的回调。 + +如果 Activity 对菜单项选定回调的实现不会处理选定的菜单项,则系统会将事件传递到片段的回调。 +这适用于选项菜单和上下文菜单。 +

    + +

    如需了解有关菜单的详细信息,请参阅菜单操作栏开发者指南。

    + + + + +

    处理片段生命周期

    + +
    + +

    图 3. Activity 生命周期对片段生命周期的影响。 +

    +
    + +

    管理片段生命周期与管理 Activity 生命周期很相似。和 Activity 一样,片段也以三种状态存在: +

    + +
    +
    恢复
    +
    片段在运行中的 Activity 中可见。
    + +
    暂停
    +
    另一个 Activity 位于前台并具有焦点,但此片段所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。 + +
    + +
    停止
    +
    片段不可见。宿主 Activity 已停止,或片段已从 Activity 中删除,但已添加到返回栈。 +停止片段仍然处于活动状态(系统会保留所有状态和成员信息)。 +不过,它对用户不再可见,如果 Activity 被终止,它也会被终止。 +
    +
    + +

    同样与 Activity 一样,假使 Activity 的进程被终止,而您需要在重建 Activity 时恢复片段状态,您也可以使用 {@link +android.os.Bundle} +保留片段的状态。您可以在片段的 {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} 回调期间保存状态,并可在 +{@link android.app.Fragment#onCreate onCreate()}、{@link +android.app.Fragment#onCreateView onCreateView()} 或 {@link +android.app.Fragment#onActivityCreated onActivityCreated()} 期间恢复状态。如需了解有关保存状态的详细信息,请参阅Activity文档。 + +

    + +

    Activity 生命周期与片段生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 +默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈(以便用户通过“返回” + +按钮回退到Activity,任务和返回栈对此做了阐述)。不过,仅当您在删除片段的事务执行期间通过调用 +{@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} +显式请求保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。 +

    + +

    在其他方面,管理片段生命周期与管理 Activity 生命周期非常相似。 +因此,管理 Activity 生命周期的做法同样适用于片段。 +但您还需要了解 Activity 的生命周期对片段生命周期的影响。 +

    + +

    注意:如需 {@link android.app.Fragment} +内的某个 {@link android.content.Context} +对象,可以调用 {@link android.app.Fragment#getActivity()}。但要注意,请仅在片段附加到 Activity 时调用 +{@link android.app.Fragment#getActivity()}。如果片段尚未附加,或在其生命周期结束期间分离,则 +{@link android.app.Fragment#getActivity()} 将返回 null。

    + + +

    与 Activity 生命周期协调一致

    + +

    片段所在的 Activity 的生命周期会影响片段的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个片段的类似回调。 + +例如,当 Activity 收到 {@link android.app.Activity#onPause} 时,Activity 中的每个片段也会收到 +{@link android.app.Fragment#onPause}。

    + +

    不过,片段还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,以执行构建和销毁片段 UI 等操作。这些额外的回调方法是: + +

    + +
    +
    {@link android.app.Fragment#onAttach onAttach()}
    +
    在片段已与 Activity 关联时调用({@link +android.app.Activity} 传递到此方法内)。
    +
    {@link android.app.Fragment#onCreateView onCreateView()}
    +
    调用它可创建与片段关联的视图层次结构。
    +
    {@link android.app.Fragment#onActivityCreated onActivityCreated()}
    +
    在 Activity 的 {@link android.app.Activity#onCreate +onCreate()} 方法已返回时调用。
    +
    {@link android.app.Fragment#onDestroyView onDestroyView()}
    +
    在删除与片段关联的视图层次结构时调用。
    +
    {@link android.app.Fragment#onDetach onDetach()}
    +
    在取消片段与 Activity 的关联时调用。
    +
    + +

    图 3 +图示说明了受其宿主 Activity 影响的片段生命周期流。在该图中,您可以看到 Activity 的每个连续状态如何决定片段可以收到的回调方法。 +例如,当 Activity 收到其 {@link +android.app.Activity#onCreate onCreate()} 回调时,Activity 中的片段只会收到 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} 回调。

    + +

    一旦 Activity 达到恢复状态,您就可以意向 Activity 添加片段和删除其中的片段。 +因此,只有当 Activity 处于恢复状态时,片段的生命周期才能独立变化。 +

    + +

    不过,当 Activity 离开恢复状态时,片段会在 Activity 的推动下再次经历其生命周期。 +

    + + + + +

    示例

    + +

    为了将本文阐述的所有内容融会贯通,以下提供了一个示例,其中的 Activity 使用两个片段来创建一个双窗格布局。 +下面的 Activity 包括两个片段:一个用于显示莎士比亚戏剧标题列表,另一个用于从列表中选定戏剧时显示其摘要。 + +此外,它还展示了如何根据屏幕配置提供不同的片段配置。 +

    + +

    注:{@code +FragmentLayout.java} +中提供了此 Activity 的完整源代码。

    + +

    主 Activity 会在 {@link +android.app.Activity#onCreate onCreate()} 期间以常规方式应用布局:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +

    应用的布局为 {@code fragment_layout.xml}:

    + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +

    通过使用此布局,系统可在 Activity 加载布局时立即实例化 +{@code TitlesFragment}(列出戏剧标题),而 +{@link android.widget.FrameLayout}(用于显示戏剧摘要的片段所在位置)则会占用屏幕右侧的空间,但最初处于空白状态。 +正如您将在下文所见的那样,用户从列表中选择某个项目后,系统才会将片段放入 +{@link android.widget.FrameLayout}。

    + +

    不过,并非所有屏幕配置都具有足够的宽度,可以并排显示戏剧列表和摘要。 +因此,以上布局仅用于横向屏幕配置(布局保存在 +{@code res/layout-land/fragment_layout.xml})。

    + +

    因此,当屏幕纵向显示时,系统会应用以下布局(保存在 +{@code res/layout/fragment_layout.xml}):

    + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +

    此布局仅包括 {@code TitlesFragment}。这意味着,当设备纵向显示时,只有戏剧标题列表可见。 +因此,当用户在此配置中点击某个列表项时,应用会启动一个新 Activity 来显示摘要,而不是加载另一个片段。 + +

    + +

    接下来,您可以看到如何在片段类中实现此目的。第一个片段是 {@code +TitlesFragment},它显示莎士比亚戏剧标题列表。该片段扩展了 {@link +android.app.ListFragment},并依靠它来处理大多数列表视图工作。

    + +

    当您检查此代码时,请注意,用户点击列表项时可能会出现两种行为:系统可能会创建并显示一个新片段,从而在同一活动中显示详细信息(将片段添加到 +{@link +android.widget.FrameLayout}),也可能会启动一个新活动(在该活动中可显示片段),具体取决于这两个布局中哪一个处于活动状态。 +

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +

    第二个片段 {@code DetailsFragment} 显示从 +{@code TitlesFragment} 的列表中选择的项目的戏剧摘要:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +

    从 {@code TitlesFragment} 类中重新调用,如果用户点击某个列表项,且当前布局“根本不”包括 {@code R.id.details} +视图(即 +{@code DetailsFragment} 所属视图),则应用会启动 {@code DetailsActivity} +Activity 以显示该项目的内容。

    + +

    以下是 {@code DetailsActivity},它简单地嵌入了 +{@code DetailsFragment},以在屏幕为纵向时显示所选的戏剧摘要:

    + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +

    请注意,如果配置为横向,则此 Activity 会自行完成,以便主 Activity 可以接管并沿 {@code TitlesFragment} +显示 +{@code DetailsFragment}。如果用户在纵向显示时启动 +{@code DetailsActivity},但随后旋转为横向(这会重启当前 Activity),就可能出现这种情况。

    + + +

    如需查看使用片段的更多示例(以及本示例的完整源文件),请参阅 + +ApiDemos(可从示例 SDK 组件下载)中提供的 API Demos 示例应用。

    + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/fundamentals.jd b/docs/html-intl/intl/zh-cn/guide/components/fundamentals.jd new file mode 100644 index 0000000000000000000000000000000000000000..4ff22b64acbefd38f59de9c702ed8235f5d18620 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=应用基础知识 +@jd:body + +
    + +
    + +

    Android 应用采用 Java 编程语言编写。Android SDK +工具将您的代码—连同任何数据和资源文件—编译到一个 APK: Android 软件包,即带有 +{@code .apk} 后缀的存档文件中。一个 APK 文件包含 +Android 应用的所有内容,它是基于 Android 系统的设备用来安装应用的文件。

    + +

    安装到设备后,每个 Android 应用都运行在自己的安全沙箱内:

    + +
      +
    • Android 操作系统是一种多用户 Linux +系统,其中的每个应用都是一位不同的用户;
    • + +
    • 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID(该 ID +仅由系统使用,应用并不知晓)。系统为应用中的所有文件设置权限,使得只有分配给该应用的用户 ID +才能访问这些文件;
    • + +
    • 每个进程都具有自己的虚拟机 +(VM),因此应用代码是在与其他应用隔离的环境中运行;
    • + +
    • 默认情况下,每个应用都在其自己的 Linux 进程内运行。Android +会在需要执行任何应用组件时启动该进程,然后在不再需要该进程或系统必须为其他应用恢复内存时关闭该进程。 +
    • +
    + +

    Android 系统可以通过这种方式实现最小权限原则。也就是说,默认情况下,每个应用都只能访问执行其工作所需的组件,而不能访问其他组件。 + +这样便营造出一个非常安全的环境,在这个环境中,应用无法访问系统中其未获得权限的部分。 +

    + +

    不过,应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务: +

    + +
      +
    • 可以安排两个应用共享同一 Linux 用户 +ID,在这种情况下,它们能够相互访问彼此的文件。为了节省系统资源,可以安排具有相同用户 ID +的应用在同一 Linux 进程中运行,并共享同一 +VM(应用还必须使用相同的证书签署);
    • +
    • 应用可以请求访问设备数据(如用户的联系人、短信、可装入存储装置 +[SD 卡]、相机、蓝牙等)的权限。所有应用权限都必须由用户在安装时授予。 +
    • +
    + +

    以上内容阐述了有关 Android 应用在系统内存在方式的基础知识。本文的其余部分将向您介绍以下内容: +

    +
      +
    • 定义应用的核心框架组件
    • +
    • 您用来声明组件和应用必需设备功能的清单文件 +
    • +
    • 与应用代码分离并允许您的应用针对各种设备配置适当优化其行为的资源 +
    • +
    + + + +

    应用组件

    + +

    应用组件是 +Android +应用的基本构建基块。每个组件都是一个不同的点,系统可以通过它进入您的应用。并非所有组件都是用户的实际入口点,有些组件相互依赖,但每个组件都以独立实体形式存在,并发挥特定作用—每个组件都是唯一的构建基块,有助于定义应用的总体行为。 + +

    + +

    共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。 +

    + +

    以下便是这四种应用组件类型:

    + +
    + +
    Activity
    + +
    Activity表示具有用户界面的单一屏幕。例如,电子邮件应用可能具有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。 + +尽管这些 Activity 通过协作在电子邮件应用中形成了一种具有凝聚力的用户体验,但每一个 Activity 都独立于其他 Activity 而存在。 + +因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。 +例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。 + + +

    Activity 作为 +{@link android.app.Activity} +的子类实现,您可以在Activity开发者指南中了解有关它的更多详情。

    +
    + + +
    服务
    + +
    服务 是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。 +服务不提供用户界面。 +例如,当用户位于其他应用中时,服务可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与 Activity 的交互。 + +诸如 Activity 等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。 + + +

    服务作为 +{@link android.app.Service} +的子类实现,您可以在服务开发者指南中了解有关它的更多详情。

    +
    + + +
    内容提供程序
    + +
    内容提供程序 管理一组共享的应用数据。您可以将数据存储在文件系统、SQLite +数据库、Web +上或您的应用可以访问的任何其他永久性存储位置。其他应用可以通过内容提供程序查询数据,甚至修改数据(如果内容提供程序允许)。 +例如,Android +系统可提供管理用户联系人信息的内容提供程序。因此,任何具有适当权限的应用都可以查询内容提供程序的某一部分(如 +{@link +android.provider.ContactsContract.Data}),以读取和写入有关特定人员的信息。 + +

    内容提供程序也适用于读取和写入您的应用不共享的私有数据。 +例如,记事本示例应用使用内容提供程序来保存笔记。 +

    + +

    内容提供程序作为 {@link android.content.ContentProvider} +的子类实现,并且必须实现让其他应用能够执行事务的一组标准 +API。如需了解详细信息,请参阅内容提供程序开发者指南。 +

    +
    + + +
    广播接收器
    + +
    广播接收器 是一种用于响应系统范围广播通知的组件。 +许多广播都是由系统发起的—例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可以发起广播—例如,通知其他应用某些数据已下载至设备,并且可供其使用。 + + +尽管广播接收器不会显示用户界面,但它们可以创建状态栏通知,在发生广播事件时提醒用户。 + +但广播接收器更常见的用途只是作为通向其他组件的“通道”,设计用于执行极少量的工作。 +例如,它可能会基于事件发起一项服务来执行某项工作。 + + +

    广播接收器作为 {@link android.content.BroadcastReceiver} +的子类实现,并且每条广播都作为 {@link android.content.Intent} 对象进行传递。如需了解详细信息,请参阅 +{@link android.content.BroadcastReceiver} 类。

    +
    + +
    + + + +

    Android +系统设计的独特之处在于,任何应用都可以启动其他应用的组件。例如,如果您想让用户使用设备的相机拍摄照片,很可能有另一个应用可以执行该操作,那么您的应用就可以利用该应用,而不是开发一个 Activity 来自行拍摄照片。 + +您不需要集成甚至链接到该相机应用的代码,而是只需在拍摄照片的相机应用中启动该 Activity。 + + +完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,就好像相机真正是您应用的组成部分。 +

    + +

    当系统启动某个组件时,会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。 +例如,如果您的应用启动相机应用中拍摄照片的 Activity,则该 Activity 会在属于相机应用的进程,而不是您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android +应用并没有单一入口点(例如,没有 +{@code main()} +功能)。 +

    + +

    由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件,但 +Android +系统却可以。因此,要想启动其他应用中的组件,您必须向系统传递一则消息,说明您想启动特定组件的 + Intent。 +系统随后便会为您启动该组件。

    + + +

    启动组件

    + +

    四种组件类型中的三种—Activity、服务和广播接收器—通过名为 + Intent +的异步消息进行启动。 Intent 会在运行时将各个组件相互绑定(您可以将 Intent 视为从其他组件请求操作的信使),无论组件属于您的应用还是其他应用。 + +

    + +

    Intent 使用 {@link android.content.Intent} +对象创建,它定义的消息用于启动特定组件或特定类型的组件— Intent 可以是显式的,也可以是隐式的。 +

    + +

    对于 Activity 和服务, Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 +URI(以及正在启动的组件可能需要了解的信息)。 +例如, Intent 传达的请求可以是启动一个显示图像或打开网页的 Activity。 +在某些情况下,您可以启动 Activity 来接收结果,在这种情况下,Activity 也会在 {@link android.content.Intent} +中返回结果(例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您 — 返回 Intent 包括指向所选联系人的 +URI)。 + +

    + +

    对于广播接收器, Intent 只会定义要广播的通知(例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串)。 + +

    + +

    Intent 不会启动另一个组件类型 - 内容提供程序,后者会在成为 +{@link android.content.ContentResolver} 的请求目标时启动。内容解析程序通过内容提供程序处理所有直接事务,使得通过提供程序执行事务的组件可以无需执行事务,而是改为在 +{@link +android.content.ContentResolver} +对象上调用方法。这会在内容提供程序与请求信息的组件之间留出一个抽象层(以确保安全)。 +

    + +

    每种类型的组件有不同的启动方法:

    +
      +
    • 您可以通过将 {@link android.content.Intent} +传递到 {@link android.content.Context#startActivity +startActivity()} 或 +{@link android.app.Activity#startActivityForResult startActivityForResult()}(当您想让 Activity 返回结果时)来启动 Activity(或为其安排新任务);
    • +
    • 您可以通过将 +{@link android.content.Intent} 传递到 {@link android.content.Context#startService +startService()} 来启动服务(或对执行中的服务下达新指令)。或者,您也可以通过将 {@link android.content.Intent} 传递到 +{@link android.content.Context#bindService bindService()} 来绑定到该服务;
    • +
    • 您可以通过将 {@link android.content.Intent} 传递到 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、{@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} 或 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()} 等方法来发起广播;
    • +
    • 您可以通过在 {@link android.content.ContentResolver} 上调用 {@link +android.content.ContentProvider#query query()} 来对内容提供程序执行查询。
    • +
    + +

    如需了解有关 Intent 用法的详细信息,请参阅 Intent 和 Intent 过滤器文档。 +以下文档中还提供了有关启动特定组件的详细信息: +Activity服务、{@link +android.content.BroadcastReceiver} 和内容提供程序

    + + +

    清单文件

    + +

    在 Android +系统启动应用组件之前,系统必须通过读取应用的 {@code AndroidManifest.xml} +文件(“清单”文件)确认组件存在。您的应用必须在此文件中声明其所有组件,该文件必须位于应用项目目录的根目录中。 +

    + +

    除了声明应用的组件外,清单文件还有许多其他作用,如: +

    +
      +
    • 确定应用需要的任何用户权限,如互联网访问权限或对用户联系人的读取权限 +
    • +
    • 根据应用使用的 +API,声明应用所需的最低API 级别
    • +
    • 声明应用使用或需要的硬件和软件功能,如相机、蓝牙服务或多点触摸屏幕 +
    • +
    • 应用需要链接的 API 库(Android 框架 +API 除外),如 Google Maps API +库
    • +
    • 其他功能
    • +
    + + +

    声明组件

    + +

    清单文件的主要任务是告知系统有关应用组件的信息。例如,清单文件可以想下面这样声明 Activity: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<manifest ... >
    +    <application android:icon="@drawable/app_icon.png" ... >
    +        <activity android:name="com.example.project.ExampleActivity"
    +                  android:label="@string/example_label" ... >
    +        </activity>
    +        ...
    +    </application>
    +</manifest>
    + +

    <application> +元素中,{@code android:icon} +属性指向标识应用的图标所对应的资源。

    + +

    <activity> +元素中,{@code android:name} 属性指定 {@link +android.app.Activity} 子类的完全限定类名,{@code android:label} +属性指定用作 Activity 的用户可见标签的字符串。

    + +

    您必须通过以下方式声明所有应用组件:

    + + +

    您包括在源代码中,但未在清单文件中声明的 Activity、服务和内容提供程序对系统不可见,因此也永远不会运行。 +不过,广播接收器可以在清单文件中声明或在代码中动态创建(如 +{@link android.content.BroadcastReceiver} +对象)并通过调用 +{@link android.content.Context#registerReceiver registerReceiver()} +在系统中注册。

    + +

    如需了解有关如何为您的应用构建清单文件的详细信息,请参阅 +AndroidManifest.xml 文件文档。

    + + + +

    声明组件功能

    + +

    如上文启动组件中所述,您可以使用 +{@link android.content.Intent} 来启动 Activity、服务和广播接收器。您可以通过在 Intent 中显式命名目标组件(使用组件类名)来执行此操作。 +不过,Intent 的真正强大之处在于隐式 Intent 概念。 +隐式 Intent 的作用无非是描述要执行的操作类型(还可选择描述您想执行的操作所针对的数据),让系统能够在设备上找到可执行该操作的组件,并启动该组件。 + + +如果有多个组件可以执行 Intent 所描述的操作,则由用户选择使用哪一个组件。 +

    + +

    系统通过将接收到的 Intent 与设备上的其他应用的清单文件中提供的 Intent 过滤器进行比较来确定可以响应 Intent 的组件。 + +

    + +

    当您在应用的清单文件中声明 Activity 时,可以选择性地加入声明 Activity 功能的 Intent 过滤器,以便响应来自其他应用的 Intent。 + +您可以通过将 +{@code +<intent-filter>} 元素作为组件声明元素的子项进行添加来为您的组件声明 Intent 过滤器。

    + +

    例如,如果您开发的一个电子邮件应用包含一个用于撰写新电子邮件的 Activity,则可以像下面这样声明一个 Intent 过滤器来响应“send” Intent(以发送新电子邮件): +

    +
    +<manifest ... >
    +    ...
    +    <application ... >
    +        <activity android:name="com.example.project.ComposeEmailActivity">
    +            <intent-filter>
    +                <action android:name="android.intent.action.SEND" />
    +                <data android:type="*/*" />
    +                <category android:name="android.intent.category.DEFAULT" />
    +            </intent-filter>
    +        </activity>
    +    </application>
    +</manifest>
    +
    + +

    然后,如果另一个应用创建了一个包含 +{@link +android.content.Intent#ACTION_SEND} 操作的 Intent,并将其传递到 {@link android.app.Activity#startActivity +startActivity()},则系统可能会启动您的 Activity,以便用户能够草拟并发送电子邮件。

    + +

    如需了解有关创建 Intent 过滤器的详细信息,请参阅 Intent 和 Intent 过滤器文档。 +

    + + + +

    声明应用要求

    + +

    基于 Android +系统的设备多种多样,并非所有设备都提供相同的特性和功能。为防止将您的应用安装在缺少应用所需特性的设备上,您必须通过在清单文件中声明设备和软件要求,为您的应用支持的设备类型明确定义一个配置文件。 + + +其中的大多数声明只是为了提供信息,系统不会读取它们,但 +Google Play +等外部服务会读取它们,以便当用户在其设备中搜索应用时为用户提供过滤功能。

    + +

    例如,如果您的应用需要相机,并使用 Android 2.1(API 7 级)中引入的 +API,您应该像下面这样在清单文件中以要求形式声明这些信息:

    + +
    +<manifest ... >
    +    <uses-feature android:name="android.hardware.camera.any"
    +                  android:required="true" />
    +    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    +    ...
    +</manifest>
    +
    + +

    现在,没有相机且 +Android 版本低于 2.1 的设备将无法从 Google Play 安装您的应用。

    + +

    不过,您也可以声明您的应用使用相机,但并不要求必须使用。 +在这种情况下,您的应用必须将 {@code required} +属性设置为 +{@code "false"},并在运行时检查设备是否具有相机,然后根据需要禁用任何相机功能。

    + +

    设备兼容性文档中提供了有关如何管理应用与不同设备兼容性的详细信息。 + +

    + + + +

    应用资源

    + +

    Android +应用并非只包含代码—它还需要与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。例如,您应该通过 +XML +文件定义 Activity 用户界面的动画、菜单、样式、颜色和布局。使用应用资源能够在不修改代码的情况下轻松地更新应用的各种特性,并可通过提供备用资源集让您能够针对各种设备配置(如不同的语言和屏幕尺寸)优化您的应用。 + + +

    + +

    对于您的 Android 项目中包括的每一项资源,SDK +构建工具都会定义一个唯一的整型 ID,您可以利用它来引用应用代码或 XML +中定义的其他资源中的资源。例如,如果您的应用包含一个名为 {@code +logo.png} 的图像文件(保存在 {@code res/drawable/} 目录中),则 SDK 工具会生成一个名为 +{@code R.drawable.logo} 的资源 +ID,您可以利用它来引用该图像并将其插入您的用户界面。

    + +

    提供与源代码分离的资源的其中一个最重要优点在于,您可以提供针对不同设备配置的备用资源。 + +例如,通过在 XML 中定义 UI +字符串,您可以将字符串翻译为其他语言,并将这些字符串保存在单独的文件中。然后,Android +系统会根据向资源目录名称追加的语言限定符(如为法语字符串值追加 {@code res/values-fr/})和用户的语言设置,对您的 +UI +应用相应的语言字符串。

    + +

    Android 支持许多不同的备用资源限定符。限定符是一种加入到资源目录名称中,用来定义这些资源适用的设备配置的简短字符串。 + +再举一例,您应该经常会根据设备的屏幕方向和尺寸为 Activity 创建不同的布局。 + +例如,当设备屏幕为纵向(长型)时,您可能想要一种垂直排列按钮的布局;但当屏幕为横向(宽型)时,应按水平方向排列按钮。 + +要想根据方向更改布局,您可以定义两种不同的布局,然后对每个布局的目录名称应用相应的限定符。 + +然后,系统会根据当前设备方向自动应用相应的布局。 +

    + +

    如需了解有关可以在应用中包括的不同资源类型以及如何针对不同设备配置创建备用资源的详细信息,请阅读提供资源。 +

    + + + +
    +
    +

    继续阅读以下方面的内容:

    +
    +
    Intent 和 Intent 过滤器 +
    +
    有关如何使用 {@link android.content.Intent} API 来启动应用组件(如 Activity 和服务)以及如何使您的应用组件可供其他应用使用的信息。 + +
    +
    Activity
    +
    有关如何创建 {@link android.app.Activity} 类实例的信息,该类可在您的应用内提供一个具有用户界面的独立屏幕。 +
    +
    提供资源
    +
    有关如何通过适当构建 Android 应用来使应用资源与应用代码分离的信息,包括如何针对特定设备配置提供备用资源。 + + +
    +
    +
    +
    +

    您可能还对以下内容感兴趣:

    +
    +
    设备兼容性
    +
    有关 Android 在不同设备类型上工作方式的信息,并介绍了如何针对不同设备优化您的应用,或如何限制您的应用在不同设备上的可用性。 + +
    +
    系统权限
    +
    有关 Android 如何通过一种权限系统来限制应用对特定 API 访问权限的信息,该系统要求征得用户同意,才允许您的应用使用这些 API。 +
    +
    +
    +
    + diff --git a/docs/html-intl/intl/zh-cn/guide/components/index.jd b/docs/html-intl/intl/zh-cn/guide/components/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..53e81849c550e2006670cd92336b1203bb9b4e6a --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=应用组件 +page.landing=true +page.landing.intro=利用 Android应用框架,您可以使用一组可重复使用的组件创建丰富的创新应用。此部分阐述您可以如何构建用于定义应用构建基块的组件,以及如何使用 Intent 将这些组件连接在一起。 +page.metaDescription=利用 Android应用框架,您可以使用一组可重复使用的组件创建丰富的创新应用。此部分阐述您可以如何构建用于定义应用构建基块的组件,以及如何使用 Intent 将这些组件连接在一起。 +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +
    + + + + + +
    diff --git a/docs/html-intl/intl/zh-cn/guide/components/intents-filters.jd b/docs/html-intl/intl/zh-cn/guide/components/intents-filters.jd new file mode 100644 index 0000000000000000000000000000000000000000..1c11a5b9609570bf3ee07104fb4235b984b829f9 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Intent 和 Intent 过滤器 +page.tags="IntentFilter" +@jd:body + + + + + + +

    {@link android.content.Intent} +是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 + Intent +可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

    + +
      +
    • 启动 Activity: +

      {@link android.app.Activity} 表示应用中的一个屏幕。通过将 +{@link android.content.Intent} 传递给 {@link android.content.Context#startActivity startActivity()},您可以启动新的 +{@link android.app.Activity} 实例。{@link android.content.Intent} +描述了要启动的 Activity,并携带了任何必要的数据。

      + +

      如果您希望在 Activity 完成后收到结果,请调用 +{@link android.app.Activity#startActivityForResult +startActivityForResult()}。在 Activity 的 {@link +android.app.Activity#onActivityResult onActivityResult()} 回调中,您的 Activity 将结果作为单独的 +{@link android.content.Intent} +对象接收。如需了解详细信息,请参阅Activity指南。

    • + +
    • 启动服务: +

      {@link android.app.Service} +是一个不使用用户界面而在后台执行操作的组件。通过将 +{@link android.content.Intent} +传递给 {@link android.content.Context#startService startService()},您可以启动服务执行一次性操作(例如,下载文件)。{@link android.content.Intent} +描述了要启动的服务,并携带了任何必要的数据。

      + +

      如果服务旨在使用客户端-服务器接口,则通过将 {@link android.content.Intent} 传递给 +{@link +android.content.Context#bindService bindService()},您可以从其他组件绑定到此服务。如需了解详细信息,请参阅服务指南。

    • + +
    • 传递广播: +

      广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 +{@link android.content.Intent} +传递给 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、{@link +android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()} 或 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()},您可以将广播传递给其他应用。

      +
    • +
    + + + + +

    Intent 类型

    + +

    Intent 分为两种类型:

    + +
      +
    • 显式 + Intent :按名称(完全限定类名)指定要启动的组件。通常,您会在自己的应用中使用显式 Intent +来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,启动新 Activity 以响应用户操作,或者启动服务以在后台下载文件。 + +
    • + +
    • 隐式 + Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理它。例如,如需在地图上向用户显示位置,则可以使用隐式 + Intent,请求另一具有此功能的应用在地图上显示指定的位置。 +
    • +
    + +

    创建显式 Intent 启动 Activity 或服务时,系统将立即启动 +{@link android.content.Intent} 对象中指定的应用组件。

    + +
    + +

    图 1. 隐式 + Intent 如何通过系统传递以启动其他 Activity 的图解:[1] Activity A 创建包含操作描述的 +{@link android.content.Intent},并将其传递给 {@link +android.content.Context#startActivity startActivity()}。[2] +Android 系统搜索所有应用中与 Intent 匹配的 Intent 过滤器。找到匹配项之后,[3] 该系统通过调用匹配 Activity(Activity +B)的 {@link +android.app.Activity#onCreate onCreate()} 方法并将其传递给 {@link android.content.Intent},以此启动匹配 Activity。 +

    +
    + +

    创建隐式 Intent +时,Android 系统通过将 Intent 的内容与在设备上其他应用的清单文件中声明的 + Intent 过滤器进行比较,从而找到要启动的相应组件。{@link android.content.Intent}如果 Intent 与 Intent +过滤器匹配,则系统将启动该组件,并将其传递给对象。如果多个 + Intent 过滤器兼容,则系统会显示一个对话框,支持用户选取要使用的应用。

    + +

    Intent +过滤器是应用清单文件中的一个表达式,它指定该组件要接收的 Intent +类型。例如,通过为 Activity 声明 Intent 过滤器,您可以使其他应用能够直接使用某一特定类型的 + Intent 启动 Activity。同样,如果您没有为 Activity 声明任何 + Intent 过滤器,则 Activity 只能通过显式 + Intent 启动。

    + +

    警告:为了确保应用的安全性,启动 +{@link android.app.Service} 时,请始终使用显式 + Intent,且不要为服务声明 Intent 过滤器。使用隐式 + Intent 启动服务存在安全隐患,因为您无法确定哪些服务将响应 + Intent,且用户无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 + Intent +调用 {@link android.content.Context#bindService bindService()},系统会抛出异常。

    + + + + + +

    构建 Intent

    + +

    {@link android.content.Intent} +对象携带了 Android +系统用来确定要启动哪个组件的信息(例如,准确的组件名称或应当接收该 + Intent 的组件类别),以及收件人组件为了正确执行操作而使用的信息(例如,要采取的操作以及要处理的数据)。

    + + +

    {@link android.content.Intent} 中包含的主要信息如下:

    + +
    + +
    组件名称
    +
    要启动的组件名称。 + +

    这是可选项,但也是构建显式 + Intent 的一项重要信息,这意味着 + Intent 应当仅传递给由组件名称定义的应用组件。如果没有组件名称,则 + Intent 是隐式的,且系统将根据其他 + Intent 信息(例如,以下所述的操作、数据和类别)决定哪个组件应当接收 Intent。因此,如需在应用中启动特定的组件,则应指定该组件的名称。 +

    + +

    注意:启动 {@link android.app.Service} 时,您应 +始终指定组件名称。否则,您无法确定哪项服务会响应 + Intent,且用户无法看到哪项服务已启动。

    + +

    {@link android.content.Intent} +的这一字段是 +{@link android.content.ComponentName} +对象,您可以使用目标组件的完全限定类名指定此对象,其中包括应用的软件包名称。例如,{@code com.example.ExampleActivity}。您可以使用 {@link +android.content.Intent#setComponent setComponent()}、{@link android.content.Intent#setClass +setClass()}、{@link android.content.Intent#setClassName(String, String) setClassName()} 或 +{@link android.content.Intent} 构造函数设置组件名称。

    + +
    + +

    操作
    +
    指定要执行的通用操作(例如,“查看”或“选取”)的字符串。 + +

    对于广播 + Intent,这是指已发生且正在报告的操作。操作在很大程度上决定了其余 Intent 的构成,特别是数据和 +extra 中包含的内容。 + +

    您可以指定自己的操作,供 + Intent 在您的应用内使用(或者供其他应用在您的应用中调用组件)。但是,您通常应该使用由 +{@link android.content.Intent} 类或其他框架类定义的操作常量。以下是一些用于启动 Activity 的常见操作: +

    + +
    +
    {@link android.content.Intent#ACTION_VIEW}
    +
    如果您拥有一些某项 Activity 可向用户显示的信息(例如,要使用图库应用查看的照片;或者要使用地图应用查找的地址),请使用 + Intent +将此操作与 {@link +android.content.Context#startActivity startActivity()} 结合使用。
    + +
    {@link android.content.Intent#ACTION_SEND}
    +
    这也称为“共享” Intent。如果您拥有一些用户可通过其他应用(例如,电子邮件应用或社交共享应用)共享的数据,则应使用 Intent 中将此操作与 +{@link +android.content.Context#startActivity startActivity()} 结合使用。
    +
    + +

    有关更多定义通用操作的常量,请参阅{@link android.content.Intent}类引用。 +其他操作在 +Android 框架中的其他位置定义。例如,对于在系统的设置应用中打开特定屏幕的操作,将在 {@link android.provider.Settings} +中定义。

    + +

    您可以使用 {@link android.content.Intent#setAction +setAction()} 或 {@link android.content.Intent} 构造函数为 Intent 指定操作。

    + +

    如果定义自己的操作,请确保将应用的软件包名称作为前缀。 +例如:

    +
    static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
    +
    + +
    数据
    +
    引用待操作数据和/或该数据 +MIME 类型的 URI({@link android.net.Uri} 对象)。提供的数据类型通常由 Intent 的操作决定。例如,如果操作是 +{@link android.content.Intent#ACTION_EDIT},则数据应包含待编辑文档的 +URI。 + +

    创建 + Intent 时,除了指定 +URI +以外,指定数据类型(其 +MIME 类型)往往也很重要。例如,能够显示图像的Activity可能无法播放音频文件,即便 URI 格式十分类似时也是如此。因此,指定数据的 +MIME 类型有助于 Android +系统找到接收 Intent 的最佳组件。但有时,MIME 类型可以从 URI 中推断得出,特别当数据是 +{@code content:} URI 时尤其如此。这表明数据位于设备中,且由 +{@link android.content.ContentProvider} 控制,这使得数据 MIME 类型对系统可见。

    + +

    要仅设置数据 URI,请调用 +{@link android.content.Intent#setData setData()}。要仅设置 MIME 类型,请调用 {@link android.content.Intent#setType setType()}。如有必要,您可以使用 +{@link +android.content.Intent#setDataAndType setDataAndType()} 同时显式设置二者。

    + +

    警告:若要同时设置 URI 和 MIME 类型,请勿调用 +{@link android.content.Intent#setData setData()} 和 +{@link android.content.Intent#setType setType()},因为它们会互相抵消彼此的值。请始终使用 +{@link android.content.Intent#setDataAndType setDataAndType()} 同时设置 +URI 和 MIME 类型。

    +
    + +

    类别
    +
    一个包含应处理 Intent +组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 + Intent 中,但大多数 + Intent 均不需要类别。以下是一些常见类别: + +
    +
    {@link android.content.Intent#CATEGORY_BROWSABLE}
    +
    目标 Activity 允许本身通过 Web +浏览器启动,以显示链接引用的数据,如图像或电子邮件。 +
    +
    {@link android.content.Intent#CATEGORY_LAUNCHER}
    +
    该 Activity 是任务的初始 Activity,在系统的应用启动器中列出。 + +
    +
    + +

    有关类别的完整列表,请参阅 +{@link android.content.Intent} 类描述。

    + +

    您可以使用 {@link android.content.Intent#addCategory addCategory()} 指定类别。

    +
    +
    + + +

    以上列出的这些属性(组件名称、操作、数据和类别)表示 + Intent 的既定特征。通过读取这些属性,Android +系统能够解析应当启动哪个应用组件。

    + +

    但是,Intent +也有可能会一些携带不影响其如何解析为应用组件的信息。Intent 还可以提供:

    + +
    +
    Extra
    +
    携带完成请求操作所需的附加信息的键值对。正如某些操作使用特定类型的数据 +URI 一样,有些操作也使用特定的附加数据。 + +

    您可以使用各种 +{@link android.content.Intent#putExtra putExtra()} 方法添加附加数据,每种方法均接受两个参数:键名和值。您还可以创建一个包含所有附加数据的 {@link android.os.Bundle} +对象,然后使用 {@link +android.content.Intent#putExtras putExtras()} 将 +{@link android.os.Bundle} 插入 {@link android.content.Intent} 中。

    + +

    例如,使用 +{@link android.content.Intent#ACTION_SEND} 创建用于发送电子邮件的 Intent 时,可以使用 +{@link android.content.Intent#EXTRA_EMAIL} 键指定“目标”收件人,并使用 +{@link android.content.Intent#EXTRA_SUBJECT} 键指定“主题”。

    + +

    {@link android.content.Intent} 类将为标准化的数据类型指定多个 {@code EXTRA_*} +常量。如需声明自己的附加数据 +键(对于应用接收的 + Intent ),请确保将应用的软件包名称作为前缀。例如:

    +
    static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
    +
    + +
    标志
    +
    在 {@link android.content.Intent} 类中定义的、充当 + Intent 元数据的标志。标志可以指示 Android +系统如何启动 Activity(例如,Activity 应属于哪个 +任务 +),以及启动之后如何处理(例如,它是否属于最近的 Activity 列表)。 + +

    如需了解详细信息,请参阅 {@link android.content.Intent#setFlags setFlags()} 方法。

    +
    + +
    + + + + +

    显式 Intent 示例

    + +

    显式 + Intent 是指用于启动某个特定应用组件(例如,应用中的某个特定 Activity 或服务)的 Intent。要创建显式 Intent,请为 +{@link android.content.Intent} 对象定义组件名称。Intent +的所有其他属性均为可选属性。

    + +

    例如,如果在应用中构建了一个名为 +{@code DownloadService}、旨在从 Web 中下载文件的服务,则可使用以下代码启动该服务:

    + +
    +// Executed in an Activity, so 'this' is the {@link android.content.Context}
    +// The fileUrl is a string URL, such as "http://www.example.com/image.png"
    +Intent downloadIntent = new Intent(this, DownloadService.class);
    +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl));
    +startService(downloadIntent);
    +
    + +

    {@link android.content.Intent#Intent(Context,Class)} +构造函数分别为应用和组件提供 {@link android.content.Context} 和 +{@link java.lang.Class} 对象。因此,此 + Intent 将显式启动该应用中的 {@code DownloadService} 类。

    + +

    如需了解有关构建和启动服务的详细信息,请参阅服务指南。 +

    + + + + +

    隐式 Intent 示例

    + +

    隐式 + Intent 指定能够在可以执行相应操作的设备上调用任何应用的操作。如果您的应用无法执行该操作而其他应用可以,且您希望用户选取要使用的应用,则使用隐式 + Intent 非常有用。

    + +

    例如,如果您希望用户与他人共享您的内容,请使用 +{@link android.content.Intent#ACTION_SEND} +操作创建 Intent,并添加指定共享内容的 Extra。使用该 + Intent 调用 +{@link android.content.Context#startActivity startActivity()} 时,用户可以选取共享内容所使用的应用。

    + +

    警告:用户可能没有任何应用处理您发送到 +{@link android.content.Context#startActivity +startActivity()} 的隐式 Intent。如果出现这种情况,则调用将会失败,且应用会崩溃。要验证 Activity 是否会接收 + Intent,请对 {@link android.content.Intent} 对象调用 {@link android.content.Intent#resolveActivity +resolveActivity()}。如果结果为非空,则至少有一个应用能够处理该 + Intent,且可以安全调用 +{@link android.content.Context#startActivity startActivity()}。如果结果为空,则不应使用该 + Intent。如有可能,您应禁用发出该 + Intent 的功能。

    + + +
    +// Create the text message with a string
    +Intent sendIntent = new Intent();
    +sendIntent.setAction(Intent.ACTION_SEND);
    +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
    +sendIntent.setType("text/plain");
    +
    +// Verify that the intent will resolve to an activity
    +if (sendIntent.resolveActivity(getPackageManager()) != null) {
    +    startActivity(sendIntent);
    +}
    +
    + +

    注意:在这种情况下,系统并没有使用 URI,但已声明 + Intent 的数据类型,用于指定 Extra 携带的内容。

    + + +

    调用 {@link android.content.Context#startActivity startActivity()} +时,系统将检查已安装的所有应用,确定哪些应用能够处理这种 Intent(即:含 {@link android.content.Intent#ACTION_SEND} +操作并携带“文本/纯”数据的 + Intent )。如果只有一个应用能够处理,则该应用将立即打开并提供给 + Intent。如果多个 Activity 接受 + Intent,则系统将显示一个对话框,使用户能够选取要使用的应用。

    + + +
    + +

    图 2. 选择器对话框。

    +
    + +

    强制使用应用选择器

    + +

    如果有多个应用响应隐式 + Intent,则用户可以选择要使用的应用,并将其设置为该操作的默认选项。 +如果用户可能希望今后一直使用相同的应用执行某项操作(例如,打开网页时,用户往往倾向于仅使用一种 +Web +浏览器),则这一点十分有用。

    + +

    但是,如果多个应用可以响应 + Intent,且用户可能希望每次使用不同的应用,则应采用显式方式显示选择器对话框。选择器对话框要求用户选择每次操作要使用的应用(用户无法为该操作选择默认应用)。 + +例如,当应用使用 {@link +android.content.Intent#ACTION_SEND} +操作执行“共享”时,用户根据目前的状况可能需要使用另一不同的应用,因此应当始终使用选择器对话框,如图 2 中所示。

    + + + + +

    要显示选择器,请使用 {@link +android.content.Intent#createChooser createChooser()} 创建 {@link android.content.Intent},并将其传递给 {@link +android.app.Activity#startActivity startActivity()}。例如:

    + +
    +Intent sendIntent = new Intent(Intent.ACTION_SEND);
    +...
    +
    +// Always use string resources for UI text.
    +// This says something like "Share this photo with"
    +String title = getResources().getString(R.string.chooser_title);
    +// Create intent to show the chooser dialog
    +Intent chooser = Intent.createChooser(sendIntent, title);
    +
    +// Verify the original intent will resolve to at least one activity
    +if (sendIntent.resolveActivity(getPackageManager()) != null) {
    +    startActivity(chooser);
    +}
    +
    + +

    这将显示一个对话框,其中包含响应传递给 {@link +android.content.Intent#createChooser createChooser()} +方法的 Intent 的应用列表,并使用提供的文本作为对话框标题。

    + + + + + + + + + +

    接收隐式 Intent

    + +

    要公布应用可以接收哪些隐式 + Intent,请在清单文件中使用 {@code <intent-filter>} +元素为每个应用组件声明一个或多个 Intent 过滤器。每个 + Intent 过滤器均根据 Intent 的操作、数据和类别指定自身接受的 + Intent 类型。仅当隐式 + Intent 可以通过 Intent 过滤器之一传递时,系统才会将该 Intent 传递给应用组件。

    + +

    注意:显式 + Intent 始终会传递给其目标,无论组件声明的 Intent 过滤器如何均是如此。

    + +

    应用组件应当为自身可执行的每个独特作业声明单独的过滤器。例如,图像库应用中的一个 Activity 可能会有两个过滤器,分别用于查看图像和编辑图像。 + +当 Activity 启动时,它将检查 +{@link android.content.Intent} 并根据 +{@link android.content.Intent} 中的信息决定具体的行为(例如,是否显示编辑器控件)。

    + +

    每个 + Intent 过滤器均由应用清单文件中的 {@code <intent-filter>} +元素定义,并嵌套在相应的应用组件(例如,{@code <activity>} +元素)中。在 {@code <intent-filter>} +内部,您可以使用以下三个元素中的一个或多个指定要接受的 + Intent 类型:

    + +
    +
    {@code <action>}
    +
    在 {@code name} 属性中,声明接受的 Intent 操作。该值必须是操作的文本字符串值,而不是类常量。 +
    +
    {@code <data>}
    +
    使用一个或多个指定 +数据 +URI(schemehostportpath 等)各个方面和 MIME 类型的属性,声明接受的数据类型。
    +
    {@code <category>}
    +
    在 {@code name} 属性中,声明接受的 Intent 类别。该值必须是操作的文本字符串值,而不是类常量。 + + +

    注意:为了接收隐式 + Intent,必须将 +{@link android.content.Intent#CATEGORY_DEFAULT} 类别包括在 Intent 过滤器中。方法 +{@link android.app.Activity#startActivity startActivity()} 和 +{@link android.app.Activity#startActivityForResult startActivityForResult()} 将按照已申明 {@link android.content.Intent#CATEGORY_DEFAULT} 类别的方式处理所有 + Intent。 + 如果未在 Intent 过滤器中声明此类别,则隐式 + Intent 不会解析为您的 Activity。

    +
    +
    + +

    例如,以下是一个使用 Intent 过滤器进行的 Activity 声明,当数据类型为文本时,系统将接收 +{@link android.content.Intent#ACTION_SEND} Intent :

    + +
    +<activity android:name="ShareActivity">
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="text/plain"/>
    +    </intent-filter>
    +</activity>
    +
    + +

    您可以创建一个包括多个 +{@code <action>}{@code <data>} +或 +{@code <category>} +实例的过滤器。创建时,仅需确定组件能够处理这些过滤器元素的任何及所有组合即可。 +

    + +

    如需仅以操作、数据和类别类型的特定组合来处理多种 Intent,则需创建多个 + Intent 过滤器。

    + + + + +

    系统通过将 + Intent 与所有这三个元素进行比较,根据过滤器测试隐式 Intent。隐式 Intent 若要传递给组件,必须通过所有这三项测试。如果 + Intent 甚至无法匹配其中任何一项测试,则 Android +系统不会将其传递给组件。但是,由于一个组件可能有多个 + Intent +过滤器,因此未能通过某一组件过滤器的 Intent 可能会通过另一过滤器。如需了解有关系统如何解析 Intent 的详细信息,请参阅下文的 + Intent 解析部分。

    + +

    警告:为了避免无意中运行不同应用的 +{@link android.app.Service},请始终使用显式 Intent 启动您自己的服务,且不必为该服务声明 + Intent 过滤器。

    + +

    注意: +对于所有 Activity,您必须在清单文件中声明 + Intent 过滤器。但是,广播接收器的过滤器可以通过调用 +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()} 动态注册。稍后,您可以使用 {@link +android.content.Context#unregisterReceiver unregisterReceiver()} 注销该接收器。这样一来,应用便可仅在应用运行时的某一指定时间段内侦听特定的广播。 + +

    + + + + + + + +

    过滤器示例

    + +

    为了更好地了解一些 + Intent 过滤器的行为,我们一起来看看从社交共享应用的清单文件中截取的以下片段。

    + +
    +<activity android:name="MainActivity">
    +    <!-- This activity is the main entry, should appear in app launcher -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +</activity>
    +
    +<activity android:name="ShareActivity">
    +    <!-- This activity handles "SEND" actions with text data -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="text/plain"/>
    +    </intent-filter>
    +    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    +    <intent-filter>
    +        <action android:name="android.intent.action.SEND"/>
    +        <action android:name="android.intent.action.SEND_MULTIPLE"/>
    +        <category android:name="android.intent.category.DEFAULT"/>
    +        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
    +        <data android:mimeType="image/*"/>
    +        <data android:mimeType="video/*"/>
    +    </intent-filter>
    +</activity>
    +
    + +

    第一个 Activity {@code MainActivity} +是应用的主要入口点。当用户最初使用启动器图标启动应用时,该 Activity 将打开:

    +
      +
    • {@link android.content.Intent#ACTION_MAIN} 操作指示这是主要入口点,且不要求输入任何 + Intent 数据。
    • +
    • {@link android.content.Intent#CATEGORY_LAUNCHER} +类别指示此 Activity 的图标应放入系统的应用启动器。如果 +{@code <activity>} +元素未使用 {@code icon} 指定图标,则系统将使用 {@code <application>} 元素中的图标。
    • +
    +

    这两个元素必须配对使用,Activity 才会显示在应用启动器中。

    + +

    第二个 Activity {@code ShareActivity} +旨在便于共享文本和媒体内容。尽管用户可以通过从 {@code MainActivity} 导航进入此 Activity,但也可以从发出隐式 + Intent(与两个 + Intent 过滤器之一匹配)的另一应用中直接进入 {@code ShareActivity}。

    + +

    注意:MIME 类型 +{@code +application/vnd.google.panorama360+jpg} 是一个指定全景照片的特殊 +数据类型,您可以使用 Google +全景 API 对其进行处理。

    + + + + + + + + + + + + + +

    使用待定 Intent

    + +

    {@link android.app.PendingIntent} 对象是 {@link +android.content.Intent} 对象的包装器。{@link android.app.PendingIntent} +的主要目的是授权外部应用使用包含的 +{@link android.content.Intent},就像是它从您应用本身的进程中执行的一样。 +

    + +

    待定 Intent 的主要用例包括:

    +
      +
    • 声明用户使用您的通知执行操作时所要执行的 + Intent(Android 系统的 {@link android.app.NotificationManager} +执行 {@link android.content.Intent})。 +
    • 声明用户使用您的 +应用小工具执行操作时要执行的 + Intent(主屏幕应用执行 {@link android.content.Intent})。 +
    • 声明未来某一特定时间要执行的 Intent(Android +系统的 {@link android.app.AlarmManager} 执行 {@link android.content.Intent})。 +
    + +

    由于每个 {@link android.content.Intent} +对象均设计为由特定类型的应用组件进行处理({@link android.app.Activity}、{@link android.app.Service} 或 +{@link android.content.BroadcastReceiver}),因此还必须基于相同的考虑因素创建 +{@link android.app.PendingIntent}。使用待定 + Intent 时,应用不会使用调用(如 {@link android.content.Context#startActivity +startActivity()})执行该 Intent。相反,通过调用相应的创建器方法创建 +{@link android.app.PendingIntent} 时,您必须声明所需的组件类型:

    + +
      +
    • {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()},适用于启动 +{@link android.app.Activity} 的 {@link android.content.Intent}。
    • +
    • {@link android.app.PendingIntent#getService PendingIntent.getService()},适用于启动 +{@link android.app.Service} 的 {@link android.content.Intent}。
    • +
    • {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()},适用于启动 +{@link android.content.BroadcastReceiver} 的 {@link android.content.Intent}。
    • +
    + +

    除非您的应用正在从其他应用中接收待定 + Intent,否则上述用于创建 {@link android.app.PendingIntent} +的方法可能是您所需的唯一 {@link android.app.PendingIntent} 方法。

    + +

    每种方法均会提取当前的应用 {@link android.content.Context}、您要包装的 +{@link android.content.Intent} 以及一个或多个指定应如何使用该 Intent 的标志(例如,是否可以多次使用该 + Intent)。

    + +

    如需了解有关使用待定 + Intent 的详细信息,请参阅通知 +和应用小工具 API 指南等手册中每个相应用例的相关文档。

    + + + + + + + +

    Intent 解析

    + + +

    当系统收到隐式 + Intent 以启动 Activity 时,它根据以下三个方面将该 Intent 与 Intent 过滤器进行比较,搜索该 Intent 的最佳 Activity:

    + +
      +
    • Intent 操作 +
    • Intent 数据(URI 和数据类型) +
    • Intent 类别 +
    + +

    下文根据如何在应用的清单文件中声明 + Intent 过滤器,描述 Intent 如何与相应的组件匹配。

    + + +

    操作测试

    + +

    要指定接受的 Intent 操作, Intent 过滤器既可以不声明任何 +{@code +<action>} 元素,也可以声明多个此类元素。例如:

    + +
    +<intent-filter>
    +    <action android:name="android.intent.action.EDIT" />
    +    <action android:name="android.intent.action.VIEW" />
    +    ...
    +</intent-filter>
    +
    + +

    要通过此过滤器,您在 {@link android.content.Intent} +中指定的操作必须与过滤器中列出的某一操作匹配。

    + +

    如果该过滤器未列出任何操作,则 + Intent 没有任何匹配项,因此所有 Intent 均无法通过测试。但是,如果 +{@link android.content.Intent} +未指定操作,则会通过测试(只要过滤器至少包含一个操作)。

    + + + +

    类别测试

    + +

    要指定接受的 Intent 类别, Intent 过滤器既可以不声明任何 +{@code +<category>} 元素,也可以声明多个此类元素。例如:

    + +
    +<intent-filter>
    +    <category android:name="android.intent.category.DEFAULT" />
    +    <category android:name="android.intent.category.BROWSABLE" />
    +    ...
    +</intent-filter>
    +
    + +

    若要 Intent 通过类别测试,则 {@link android.content.Intent} +中的每个类别均必须与过滤器中的类别匹配。反之则未必然,Intent +过滤器声明的类别可以超出 {@link android.content.Intent} +中指定的数量,且 {@link android.content.Intent} 仍会通过测试。因此,不含类别的 + Intent 应当始终会通过此测试,无论过滤器中声明何种类别均是如此。

    + +

    注意: +Android +会自动将 {@link android.content.Intent#CATEGORY_DEFAULT} +类别应用于传递给 {@link +android.content.Context#startActivity startActivity()} 和 {@link +android.app.Activity#startActivityForResult startActivityForResult()} 的所有隐式 + Intent。因此,如需 Activity 接收隐式 Intent,则必须将 {@code "android.intent.category.DEFAULT"} 的类别包括在其 Intent 过滤器中(如上文的 +{@code <intent-filter>} 示例所示)。

    + + + +

    数据测试

    + +

    要指定接受的 Intent 数据, Intent 过滤器既可以不声明任何 +{@code +<data>} 元素,也可以声明多个此类元素。例如:

    + +
    +<intent-filter>
    +    <data android:mimeType="video/mpeg" android:scheme="http" ... />
    +    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
    +    ...
    +</intent-filter>
    +
    + +

    每个 <data> +元素均可指定 URI 结构和数据类型(MIME 介质类型)。URI 的每个部分均包含单独的 +{@code scheme}、{@code host}、{@code port} +和 {@code path} 属性: +

    + +

    {@code <scheme>://<host>:<port>/<path>}

    + +

    +例如: +

    + +

    {@code content://com.example.project:200/folder/subfolder/etc}

    + +

    在此 URI 中,架构是 {@code content},主机是 +{@code com.example.project},端口是 {@code 200},路径是 {@code folder/subfolder/etc}。 +

    + +

    {@code <data>} +元素中,上述每个属性均为可选,但存在线性依赖关系:

    +
      +
    • 如果未指定架构,则会忽略主机。
    • +
    • 如果未指定主机,则会忽略端口。
    • +
    • 如果未指定架构和主机,则会忽略路径。
    • +
    + +

    将 + Intent 中的 URI 与过滤器中的 URI 规范进行比较时,它仅与过滤器中包含的部分 URI 进行比较。例如:

    +
      +
    • 如果过滤器仅指定架构,则具有该架构的所有 +URI 均与该过滤器匹配。
    • +
    • 如果过滤器指定架构和权限、但未指定路径,则具有相同架构和权限的所有 URI +都会通过过滤器,无论其路径如何均是如此。
    • +
    • 如果过滤器指定架构、权限和路径,则仅具有相同架构、权限和路径 +的 URI 才会通过过滤器。
    • +
    + +

    注意:路径规范可以包含星号通配符 +(*),因此仅需部分匹配路径名即可。

    + +

    数据测试会将 Intent 中的 URI 和 MIME +类型与过滤器中指定的 URI 和 MIME 类型进行比较。规则如下: +

    + +
      +
    1. 仅当过滤器未指定任何 URI 或 MIME 类型时,不含 URI 和 MIME 类型的 + Intent 才会通过测试。
    2. + +
    3. 对于包含 URI、但不含 MIME 类型(既未显式声明,也无法通过 +URI 推断得出)的 Intent,仅当其 URI +与过滤器的 URI 格式匹配、且过滤器同样未指定 MIME 类型时,才会通过测试。
    4. + +
    5. 仅当过滤器列出相同的 MIME 类型且未指定 URI 格式时,包含 MIME +类型、但不含 URI 的 Intent 才会通过测试。
    6. + +
    7. 仅当 MIME 类型与过滤器中列出的类型匹配时,包含 +URI 和 MIME 类型(通过显式声明,或可以通过 +URI 推断得出)的 Intent 才会通过测试的 MIME 类型部分。如果 Intent 的 URI 与过滤器中的 +URI 匹配,或者如果 Intent 具有 {@code content:} +或 {@code file:} URI +且过滤器未指定 URI,则 Intent 会通过测试的 URI 部分。换而言之,如果过滤器仅列出 MIME 类型,则假定组件支持 +{@code content:} 和 {@code file:} 数据。

    8. +
    + +

    +最后一条规则,即规则 +(d),反映了期望组件能够从文件中或内容提供商处获得本地数据。因此,其过滤器可以仅列出数据类型,而不必显式命名 +{@code content:} 和 +{@code file:} +架构。这是一个典型的案例。例如,下文中的 {@code <data>} +元素向 +Android 指出,组件可从内容提供商处获得并显示图像数据: +

    + +
    +<intent-filter>
    +    <data android:mimeType="image/*" />
    +    ...
    +</intent-filter>
    + +

    +由于大部分可用数据均由内容提供商分发,因此指定数据类型(而非 +URI)的过滤器也许最为常见。 +

    + +

    +另一常见的配置是具有架构和数据类型的过滤器。例如,下文中的 {@code <data>} +元素向 +Android +指出,组件可从网络中检索视频数据以执行操作: +

    + +
    +<intent-filter>
    +    <data android:scheme="http" android:type="video/*" />
    +    ...
    +</intent-filter>
    + + + +

    Intent 匹配

    + +

    通过 + Intent +过滤器匹配 Intent,这不仅有助于发现要激活的目标组件,还有助于发现设备上组件集的相关信息。例如,主页应用通过使用指定 +{@link android.content.Intent#ACTION_MAIN} 操作和 +{@link android.content.Intent#CATEGORY_LAUNCHER} 类别的 + Intent 过滤器查找所有 Activity,以此填充应用启动器。

    + +

    您的应用可以采用类似的方式使用 + Intent 匹配。{@link android.content.pm.PackageManager} 提供了一整套 {@code query...()} +方法来返回所有能够接受特定 Intent +的组件。此外,它还提供了一系列类似的 {@code resolve...()} 方法来确定响应 Intent +的最佳组件。例如,{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} +将返回能够执行那些作为参数传递的 + Intent 的所有 Activity 列表,而 +{@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} +则可返回类似的服务列表。这两种方法均不会激活组件,而只是列出能够响应的组件。对于广播接收器,有一种类似的方法: +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()}。 +

    + + + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/loaders.jd b/docs/html-intl/intl/zh-cn/guide/components/loaders.jd new file mode 100644 index 0000000000000000000000000000000000000000..d8427b0cd109e2e75449fe82c65fade39677f6ef --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=加载器 +parent.title=Activity +parent.link=activities.html +@jd:body +
    +
    +

    本文内容

    +
      +
    1. Loader API 摘要
    2. +
    3. 在应用中使用加载器 +
        +
      1. +
      2. 启动加载器
      3. +
      4. 重启加载器
      5. +
      6. 使用 LoaderManager 回调
      7. +
      +
    4. +
    5. 示例 +
        +
      1. 更多示例
      2. +
      +
    6. +
    + +

    关键类

    +
      +
    1. {@link android.app.LoaderManager}
    2. +
    3. {@link android.content.Loader}
    4. + +
    + +

    相关示例

    +
      +
    1. +LoaderCursor
    2. +
    3. +LoaderThrottle
    4. +
    +
    +
    + +

    Android 3.0 中引入了加载器,支持轻松在 Activity 或片段中异步加载数据。 +加载器具有以下特征:

    +
      +
    • 可用于每个 {@link android.app.Activity} 和 {@link +android.app.Fragment}。
    • +
    • 支持异步加载数据。
    • +
    • 监控其数据源并在内容变化时传递新结果。 +
    • +
    • 在某一配置更改后重建加载器时,会自动重新连接上一个加载器的 Cursor。 +因此,它们无需重新查询其数据。 +
    • +
    + +

    Loader API 摘要

    + +

    在应用中使用加载器时,可能会涉及到多个类和接口。 +下表汇总了这些类和接口:

    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    类/接口描述
    {@link android.app.LoaderManager}一种与 {@link android.app.Activity} 或 {@link android.app.Fragment} 相关联的的抽象类,用于管理一个或多个 {@link +android.content.Loader} 实例。 +这有助于应用管理与 +{@link android.app.Activity} +或 {@link android.app.Fragment} 生命周期相关联的、运行时间较长的操作。它最常见的用法是与 +{@link android.content.CursorLoader} +一起使用,但应用可自由写入其自己的加载器,用于加载其他类型的数据。 +
    +
    + 每个 Activity 或片段中只有一个 {@link android.app.LoaderManager}。但一个 {@link android.app.LoaderManager} +可以有多个加载器。
    {@link android.app.LoaderManager.LoaderCallbacks}一种回调接口,用于客户端与 {@link +android.app.LoaderManager} 进行交互。例如,您可使用 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +回调方法创建新的加载器。
    {@link android.content.Loader}一种执行异步数据加载的抽象类。这是加载器的基类。 +您通常会使用 {@link +android.content.CursorLoader},但您也可以实现自己的子类。加载器处于Activity状态时,应监控其数据源并在内容变化时传递新结果。 + +
    {@link android.content.AsyncTaskLoader}提供 {@link android.os.AsyncTask} 来执行工作的抽象加载器。
    {@link android.content.CursorLoader}{@link android.content.AsyncTaskLoader} +的子类,它将查询 {@link android.content.ContentResolver} 并返回一个 {@link +android.database.Cursor}。此类采用标准方式为查询 Cursor 实现 {@link +android.content.Loader} +协议。它是以 {@link android.content.AsyncTaskLoader} +为基础而构建,在后台线程中执行 Cursor 查询,因此不会阻塞应用的 UI。使用此加载器是从 +{@link +android.content.ContentProvider} +异步加载数据的最佳方式,而不用通过片段或 Activity 的 API 来执行托管查询。
    + +

    上表中的类和接口是您在应用中用于实现加载器的基本组件。 +并非您创建的每个加载器都要用到上述所有类和接口。但是,为了初始化加载器以及实现一个 {@link android.content.Loader} +类(如 {@link +android.content.CursorLoader}),您始终需要要引用 +{@link +android.app.LoaderManager}。下文将为您展示如何在应用中使用这些类和接口。 +

    + +

    在应用中使用加载器

    +

    此部分描述如何在 Android 应用中使用加载器。使用加载器的应用通常包括: +

    +
      +
    • {@link android.app.Activity} 或 {@link android.app.Fragment}。
    • +
    • {@link android.app.LoaderManager} 的实例。
    • +
    • 一个 {@link android.content.CursorLoader},用于加载由 {@link +android.content.ContentProvider} 支持的数据。您也可以实现自己的 +{@link android.content.Loader} +或 {@link android.content.AsyncTaskLoader} 子类,从其他源中加载数据。
    • +
    • 一个 +{@link android.app.LoaderManager.LoaderCallbacks} +实现。您可以使用它来创建新加载器,并管理对现有加载器的引用。
    • +
    • 一种显示加载器数据的方法,如 {@link +android.widget.SimpleCursorAdapter}。
    • +
    • 使用 +{@link android.content.CursorLoader} 时的数据源,如 {@link android.content.ContentProvider}。
    • +
    +

    启动加载器

    + +

    {@link android.app.LoaderManager} 可在 {@link android.app.Activity} 或 +{@link android.app.Fragment} 内管理一个或多个 {@link +android.content.Loader} 实例。每个 Activity 或片段只有一个 {@link +android.app.LoaderManager}。

    + +

    通常,您会使用 Activity 的 {@link +android.app.Activity#onCreate onCreate()} 方法或片段的 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} +方法初始化 {@link android.content.Loader}。您执行操作如下: +

    + +
    // Prepare the loader.  Either re-connect with an existing one,
    +// or start a new one.
    +getLoaderManager().initLoader(0, null, this);
    + +

    {@link android.app.LoaderManager#initLoader initLoader()} +方法采用以下参数:

    +
      +
    • 用于标识加载器的唯一 ID。在此示例中,ID 为 0。
    • +
    • 在构建时提供给加载器的可选参数(在此示例中为 null +)。
    • + +
    • {@link android.app.LoaderManager.LoaderCallbacks} 实现, +{@link android.app.LoaderManager} 将调用此实现来报告加载器事件。在此示例中,本地类实现 +{@link +android.app.LoaderManager.LoaderCallbacks} +接口,因此它会将引用 {@code this} 传递给自己。
    • +
    +

    {@link android.app.LoaderManager#initLoader initLoader()} +调用确保加载器已初始化且处于Activity状态。这可能会出现两种结果:

    +
      +
    • 如果 ID +指定的加载器已存在,则将重复使用上次创建的加载器。
    • +
    • 如果 +ID 指定的加载器不存在,则 {@link android.app.LoaderManager#initLoader initLoader()} 将触发 +{@link android.app.LoaderManager.LoaderCallbacks} +方法 {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。在此方法中,您可以实现代码以实例化并返回新加载器。有关详细介绍,请参阅 onCreateLoader 部分。 +
    • +
    +

    无论何种情况,给定的 +{@link android.app.LoaderManager.LoaderCallbacks} +实现均与加载器相关联,且将在加载器状态变化时调用。如果在调用时,调用程序处于启动状态,且请求的加载器已存在并生成了数据,则系统将立即调用 +{@link +android.app +LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +(在 {@link android.app.LoaderManager#initLoader initLoader()} +期间),因此您必须为此做好准备。有关此回调的详细介绍,请参阅 +onLoadFinished

    + +

    请注意,{@link android.app.LoaderManager#initLoader initLoader()} +方法将返回已创建的 +{@link android.content.Loader},但您不必捕获其引用。{@link android.app.LoaderManager} +将自动管理加载器的生命周期。{@link android.app.LoaderManager} +将根据需要启动和停止加载,并维护加载器的状态及其相关内容。 +这意味着您很少直接与加载器进行交互(有关使用加载器方法调整加载器行为的示例,请参阅 +LoaderThrottle +示例)。当特殊事件发生时,您通常会使用 +{@link +android.app.LoaderManager.LoaderCallbacks} +方法干预加载进程。有关此主题的详细介绍,请参阅使用 LoaderManager 回调

    + +

    重启加载器

    + +

    当您使用 +{@link android.app.LoaderManager#initLoader initLoader()} +时(如上所述),它将使用含有指定 ID 的现有加载器(如有)。如果没有,则它会创建一个。但有时,您想放弃这些旧数据并重新开始。 +

    + +

    要放弃旧数据,请使用 {@link +android.app.LoaderManager#restartLoader restartLoader()}。例如,当用户的查询更改时,此 {@link android.widget.SearchView.OnQueryTextListener} +实现将重启加载器。 +加载器需要重启,以便它能够使用修订后的搜索筛选器执行新查询: +

    + +
    +public boolean onQueryTextChanged(String newText) {
    +    // Called when the action bar search text has changed.  Update
    +    // the search filter, and restart the loader to do a new query
    +    // with this filter.
    +    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    +    getLoaderManager().restartLoader(0, null, this);
    +    return true;
    +}
    + +

    使用 LoaderManager 回调

    + +

    {@link android.app.LoaderManager.LoaderCallbacks} 是一个支持客户端与 +{@link android.app.LoaderManager} 交互的回调接口。

    +

    加载器(特别是 +{@link android.content.CursorLoader})在停止运行后,仍需保留其数据。这样,应用即可保留 Activity 或片段的 +{@link android.app.Activity#onStop +onStop()} 和 +{@link android.app.Activity#onStart onStart()} +方法中的数据。当用户返回应用时,无需等待它重新加载这些数据。您可使用 +{@link android.app.LoaderManager.LoaderCallbacks} +方法了解何时创建新加载器,并告知应用何时停止使用加载器的数据。

    + +

    {@link android.app.LoaderManager.LoaderCallbacks} +包括以下方法:

    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}:针对指定的 +ID 进行实例化并返回新的 {@link android.content.Loader} +
    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +:将在先前创建的加载器完成加载时调用 +
    +
      +
    • {@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}: +将在先前创建的加载器重置且其数据因此不可用时调用 + +
    • +
    +

    下文更详细地描述了这些方法。

    + +

    onCreateLoader

    + +

    当您尝试访问加载器时(例如,通过 +{@link +android.app.LoaderManager#initLoader initLoader()}),该方法将检查是否已存在由该 ID 指定的加载器。如果没有,它将触发 {@link +android.app.LoaderManager.LoaderCallbacks} 方法 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。在此方法中,您可以创建新加载器。 +通常,这将是 {@link +android.content.CursorLoader},但您也可以实现自己的 {@link +android.content.Loader} 子类。

    + +

    在此示例中,{@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +回调方法创建了 {@link android.content.CursorLoader}。您必须使用其构造函数方法来构建 +{@link android.content.CursorLoader}。该方法需要对 +{@link +android.content.ContentProvider} 执行查询时所需的一系列完整信息。具体地说,它需要:

    +
      +
    • uri:用于检索内容的 URI
    • +
    • projection:要返回的列的列表。传递 +null 时,将返回所有列,这样会导致效率低下
    • +
    • selection:一种用于声明要返回哪些行的过滤器,其格式为 +SQL WHERE 子句(WHERE 本身除外)。传递 +null 时,将为指定的 URI 返回所有行
    • +
    • selectionArgs:您可以在 selection 中包含 ?s,它将按照在 selection 中显示的顺序替换为 +selectionArgs +中的值。该值将绑定为字串符
    • +
    • sortOrder:行的排序依据,其格式为 SQL +ORDER BY 子句(ORDER BY 自身除外)。传递 null + 时,将使用默认排序顺序(可能并未排序)
    • +
    +

    例如:

    +
    + // If non-null, this is the current filter the user has provided.
    +String mCurFilter;
    +...
    +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +    // This is called when a new Loader needs to be created.  This
    +    // sample only has one Loader, so we don't care about the ID.
    +    // First, pick the base URI to use depending on whether we are
    +    // currently filtering.
    +    Uri baseUri;
    +    if (mCurFilter != null) {
    +        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
    +                  Uri.encode(mCurFilter));
    +    } else {
    +        baseUri = Contacts.CONTENT_URI;
    +    }
    +
    +    // Now create and return a CursorLoader that will take care of
    +    // creating a Cursor for the data being displayed.
    +    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
    +            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
    +            + Contacts.DISPLAY_NAME + " != '' ))";
    +    return new CursorLoader(getActivity(), baseUri,
    +            CONTACTS_SUMMARY_PROJECTION, select, null,
    +            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    +}
    +

    onLoadFinished

    + +

    当先前创建的加载器完成加载时,将调用此方法。该方法必须在为此加载器提供的最后一个数据释放之前调用。 + +此时,您应移除所有使用的旧数据(因为它们很快会被释放),但不要自行释放这些数据,因为这些数据归其加载器所有,其加载器会处理它们。 + +

    + + +

    当加载器发现应用不再使用这些数据时,即会释放它们。 +例如,如果数据是来自 {@link +android.content.CursorLoader} 的一个 Cursor,则您不应手动对其调用 {@link +android.database.Cursor#close close()}。如果 Cursor 放置在 +{@link android.widget.CursorAdapter} 中,则应使用 {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} 方法,使旧 +{@link android.database.Cursor} 不会关闭。例如:

    + +
    +// This is the Adapter being used to display the list's data.
    SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in.  (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}
    + +

    onLoaderReset

    + +

    此方法将在先前创建的加载器重置且其数据因此不可用时调用。 +通过此回调,您可以了解何时将释放数据,因而能够及时移除其引用。 +  

    +

    此实现调用值为 null +的{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}: +

    + +
    +// This is the Adapter being used to display the list's data.
    +SimpleCursorAdapter mAdapter;
    +...
    +
    +public void onLoaderReset(Loader<Cursor> loader) {
    +    // This is called when the last Cursor provided to onLoadFinished()
    +    // above is about to be closed.  We need to make sure we are no
    +    // longer using it.
    +    mAdapter.swapCursor(null);
    +}
    + + +

    示例

    + +

    以下是一个 +{@link +android.app.Fragment} 完整实现示例。它展示了一个 {@link android.widget.ListView},其中包含针对联系人内容提供程序的查询结果。它使用 {@link +android.content.CursorLoader} 管理提供程序的查询。

    + +

    应用如需访问用户联系人(正如此示例中所示),其清单文件必须包括权限 +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}。 +

    + +
    +public static class CursorLoaderListFragment extends ListFragment
    +        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
    +
    +    // This is the Adapter being used to display the list's data.
    +    SimpleCursorAdapter mAdapter;
    +
    +    // If non-null, this is the current filter the user has provided.
    +    String mCurFilter;
    +
    +    @Override public void onActivityCreated(Bundle savedInstanceState) {
    +        super.onActivityCreated(savedInstanceState);
    +
    +        // Give some text to display if there is no data.  In a real
    +        // application this would come from a resource.
    +        setEmptyText("No phone numbers");
    +
    +        // We have a menu item to show in action bar.
    +        setHasOptionsMenu(true);
    +
    +        // Create an empty adapter we will use to display the loaded data.
    +        mAdapter = new SimpleCursorAdapter(getActivity(),
    +                android.R.layout.simple_list_item_2, null,
    +                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
    +                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
    +        setListAdapter(mAdapter);
    +
    +        // Prepare the loader.  Either re-connect with an existing one,
    +        // or start a new one.
    +        getLoaderManager().initLoader(0, null, this);
    +    }
    +
    +    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    +        // Place an action bar item for searching.
    +        MenuItem item = menu.add("Search");
    +        item.setIcon(android.R.drawable.ic_menu_search);
    +        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    +        SearchView sv = new SearchView(getActivity());
    +        sv.setOnQueryTextListener(this);
    +        item.setActionView(sv);
    +    }
    +
    +    public boolean onQueryTextChange(String newText) {
    +        // Called when the action bar search text has changed.  Update
    +        // the search filter, and restart the loader to do a new query
    +        // with this filter.
    +        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    +        getLoaderManager().restartLoader(0, null, this);
    +        return true;
    +    }
    +
    +    @Override public boolean onQueryTextSubmit(String query) {
    +        // Don't care about this.
    +        return true;
    +    }
    +
    +    @Override public void onListItemClick(ListView l, View v, int position, long id) {
    +        // Insert desired behavior here.
    +        Log.i("FragmentComplexList", "Item clicked: " + id);
    +    }
    +
    +    // These are the Contacts rows that we will retrieve.
    +    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
    +        Contacts._ID,
    +        Contacts.DISPLAY_NAME,
    +        Contacts.CONTACT_STATUS,
    +        Contacts.CONTACT_PRESENCE,
    +        Contacts.PHOTO_ID,
    +        Contacts.LOOKUP_KEY,
    +    };
    +    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +        // This is called when a new Loader needs to be created.  This
    +        // sample only has one Loader, so we don't care about the ID.
    +        // First, pick the base URI to use depending on whether we are
    +        // currently filtering.
    +        Uri baseUri;
    +        if (mCurFilter != null) {
    +            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
    +                    Uri.encode(mCurFilter));
    +        } else {
    +            baseUri = Contacts.CONTENT_URI;
    +        }
    +
    +        // Now create and return a CursorLoader that will take care of
    +        // creating a Cursor for the data being displayed.
    +        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
    +                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
    +                + Contacts.DISPLAY_NAME + " != '' ))";
    +        return new CursorLoader(getActivity(), baseUri,
    +                CONTACTS_SUMMARY_PROJECTION, select, null,
    +                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    +    }
    +
    +    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    +        // Swap the new cursor in.  (The framework will take care of closing the
    +        // old cursor once we return.)
    +        mAdapter.swapCursor(data);
    +    }
    +
    +    public void onLoaderReset(Loader<Cursor> loader) {
    +        // This is called when the last Cursor provided to onLoadFinished()
    +        // above is about to be closed.  We need to make sure we are no
    +        // longer using it.
    +        mAdapter.swapCursor(null);
    +    }
    +}
    +

    更多示例

    + +

    ApiDemos +中还提供了一些不同的示例,阐述如何使用加载器:

    +
      +
    • LoaderCursor:上述代码段的完整版本 + +
    • +
    • LoaderThrottle:此示例显示当数据变化时,如何使用限制来减少内容提供程序的查询次数 +
    • +
    + +

    有关下载和安装 SDK +示例的信息,请参阅获取示例

    + diff --git a/docs/html-intl/intl/zh-cn/guide/components/processes-and-threads.jd b/docs/html-intl/intl/zh-cn/guide/components/processes-and-threads.jd new file mode 100644 index 0000000000000000000000000000000000000000..c88ecf4b81e813d5bd63aa5c552172feb9af16a2 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=进程和线程 +page.tags=生命周期,后台 + +@jd:body + +
    +
    + +

    本文内容

    +
      +
    1. 进程 +
        +
      1. 进程生命周期
      2. +
      +
    2. +
    3. 线程 +
        +
      1. 工作线程
      2. +
      3. 线程安全方法
      4. +
      +
    4. +
    5. 进程间通信
    6. +
    + +
    +
    + +

    当某个应用组件启动且该应用没有运行其他任何组件时,Android +系统会使用单个执行线程为应用启动新的 +Linux 进程。默认情况下,同一应用的所有组件在相同的进程和线程(称为“主”线程)中运行。 +如果某个应用组件启动且该应用已存在进程(因为存在该应用的其他组件),则该组件会在此进程内启动并使用相同的执行线程。 + +但是,您可以安排应用中的其他组件在单独的进程中运行,并为任何进程创建额外的线程。 + +

    + +

    本文档介绍进程和线程在 Android 应用中的工作方式。

    + + +

    进程

    + +

    默认情况下,同一应用的所有组件均在相同的进程中运行,且大多数应用都不会改变这一点。 +但是,如果您发现需要控制某个组件所属的进程,则可在清单文件中执行此操作。 +

    + +

    各类组件元素的清单文件条目—{@code +<activity>}{@code +<service>}{@code +<receiver>}{@code +<provider>}—均支持 +{@code android:process} 属性,此属性可以指定该组件应在哪个进程运行。您可以设置此属性,使每个组件均在各自的进程中运行,或者使一些组件共享一个进程,而其他组件则不共享。 +此外,您还可以设置 {@code android:process},使不同应用的组件在相同的进程中运行,但前提是这些应用共享相同的 Linux 用户 ID 并使用相同的证书进行签署。 + + +

    + +

    此外,{@code +<application>} 元素还支持 {@code android:process} +属性,以设置适用于所有组件的默认值。

    + +

    如果内存不足,而其他为用户提供更紧急服务的进程又需要内存时,Android +可能会决定在某一时刻关闭某一进程。在被终止进程中运行的应用组件也会随之销毁。 +当这些组件需要再次运行时,系统将为它们重启进程。 +

    + +

    决定终止哪个进程时,Android +系统将权衡它们对用户的相对重要程度。例如,相对于托管可见 Activity 的进程而言,它更有可能关闭托管屏幕上不再可见的 Activity 进程。 +因此,是否终止某个进程的决定取决于该进程中所运行组件的状态。 +下面,我们介绍决定终止进程所用的规则。 +

    + + +

    进程生命周期

    + +

    Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。 +为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 + + +必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。 + +

    + +

    重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程): + +

    + +
      +
    1. 前台进程 +

      用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程: +

      + +
        +
      • 托管用户正在交互的 {@link android.app.Activity}(已调用 {@link +android.app.Activity} 的 {@link android.app.Activity#onResume onResume()} +方法)
      • + +
      • 托管某个 {@link android.app.Service},后者绑定到用户正在交互的 Activity +
      • + +
      • 托管正在“前台”运行的 +{@link android.app.Service}(服务已调用 {@link android.app.Service#startForeground startForeground()}) + +
      • 托管正执行一个生命周期回调的 +{@link android.app.Service}({@link android.app.Service#onCreate onCreate()}、{@link android.app.Service#onStart +onStart()} 或 {@link android.app.Service#onDestroy onDestroy()})
      • + +
      • 托管正执行其 {@link + android.content.BroadcastReceiver#onReceive onReceive()} 方法的 {@link android.content.BroadcastReceiver}
      • +
      + +

      通常,在任意给定时间前台进程都为数不多。只有在内在不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 +此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。 + +

    2. + +
    3. 可见进程 +

      没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 +如果一个进程满足以下任一条件,即视为可见进程: +

      + +
        +
      • 托管不在前台、但仍对用户可见的 +{@link android.app.Activity}(已调用其 {@link android.app.Activity#onPause onPause()} 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况 + +
      • + +
      • 托管绑定到可见(或前台)Activity 的 +{@link android.app.Service}
      • +
      + +

      可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。 +

      +
    4. + +
    5. 服务进程 +

      正在运行已使用 {@link +android.content.Context#startService startService()} +方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。 + + +

      +
    6. + +
    7. 后台进程 +

      包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 +{@link android.app.Activity#onStop onStop()} 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 + + +通常会有很多后台进程在运行,因此它们会保存在 +LRU +(最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 + + +有关保存和恢复状态的信息,请参阅Activity文档。 +

      +
    8. + +
    9. 空进程 +

      不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 + +为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。 +

      +
    10. +
    + + +

    根据进程中当前活动组件的重要程度,Android +会将进程评定为它可能达到的最高级别。例如,如果某进程托管着服务和可见 Activity,则会将此进程评定为可见进程,而不是服务进程。 +

    + +

    此外,一个进程的级别可能会因其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。 + +例如,如果进程 A +中的内容提供程序为进程 B 中的客户端提供服务,或者如果进程 A 中的服务绑定到进程 B 中的组件,则进程 A 始终被视为至少与进程 +B 同样重要。

    + +

    由于运行服务的进程其级别高于托管后台 Activity 的进程,因此启动长时间运行操作的 Activity 最好为该操作启动服务,而不是简单地创建工作线程,当操作有可能比 Activity 更加持久时尤要如此。例如,正在将图片上传到网站的 Activity 应该启动服务来执行上传,这样一来,即使用户退出 Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论 Activity 发生什么情况,该操作至少具备“服务进程”优先级。 + + + + + +同理,广播接收器也应使用服务,而不是简单地将耗时冗长的操作放入线程中。 +

    + + + + +

    线程

    + +

    应用启动时,系统会为应用创建一个名为“主线程”的执行线程。 +此线程非常重要,因为它负责将事件分派给相应的用户界面小工具,其中包括绘图事件。 +此外,它也是应用与 +Android UI 工具包组件(来自 {@link +android.widget} 和 {@link android.view} 软件包的组件)进行交互的线程。因此,主线程有时也称为 +UI 线程。

    + +

    系统绝对不会为每个组件实例创建单独的线程。运行于同一进程的所有组件均在 +UI +线程中实例化,并且对每个组件的系统调用均由该线程进行分派。因此,响应系统回调的方法(例如,报告用户操作的 +{@link android.view.View#onKeyDown onKeyDown()} +或生命周期回调方法)始终在进程的 UI 线程中运行。

    + +

    例如,当用户触摸屏幕上的按钮时,应用的 +UI +线程会将触摸事件分派给小工具,而小工具反过来又设置其按下状态,并将无效请求发布到事件队列中。UI +线程从队列中取消该请求并通知小工具应该重绘自身。

    + +

    在应用执行繁重的任务以响应用户交互时,除非正确实施应用,否则这种单线程模式可能会导致性能低下。 +特别地,如果 +UI +线程需要处理所有任务,则执行耗时很长的操作(例如,网络访问或数据库查询)将会阻塞整个 +UI。一旦线程被阻塞,将无法分派任何事件,包括绘图事件。从用户的角度来看,应用显示为挂起。 +更糟糕的是,如果 UI +线程被阻塞超过几秒钟时间(目前大约是 5 秒钟),用户就会看到一个让人厌烦的“应用无响应”(ANR) +对话框。如果引起用户不满,他们可能就会决定退出并卸载此应用。 +

    + +

    此外,Android UI 工具包并非线程安全工具包。因此,您不得通过工作线程操纵 +UI,而只能通过 +UI 线程操纵用户界面。因此,Android 的单线程模式必须遵守两条规则:

    + +
      +
    1. 不要阻塞 UI 线程 +
    2. 不要在 UI 线程之外访问 Android UI 工具包 +
    + +

    工作线程

    + +

    根据上述单线程模式,要保证应用 UI +的响应能力,关键是不能阻塞 UI 线程。如果执行的操作不能很快完成,则应确保它们在单独的线程(“后台”或“工作”线程)中运行。 + +

    + +

    例如,以下代码演示了一个点击侦听器从单独的线程下载图像并将其显示在 +{@link android.widget.ImageView} 中:

    + +
    +public void onClick(View v) {
    +    new Thread(new Runnable() {
    +        public void run() {
    +            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
    +            mImageView.setImageBitmap(b);
    +        }
    +    }).start();
    +}
    +
    + +

    乍看起来,这段代码似乎运行良好,因为它创建了一个新线程来处理网络操作。 +但是,它违反了单线程模式的第二条规则:不要在 +UI 线程之外访问 Android UI 工具包—此示例从工作线程(而不是 UI 线程)修改了 {@link +android.widget.ImageView}。这可能导致出现不明确、不可预见的行为,但要跟踪此行为困难而又费时。 +

    + +

    为解决此问题,Android 提供了几种途径来从其他线程访问 UI +线程。以下列出了几种有用的方法:

    + +
      +
    • {@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}
    • +
    • {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}
    • +
    • {@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}
    • +
    + +

    例如,您可以通过使用 {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)} 方法修复上述代码:

    + +
    +public void onClick(View v) {
    +    new Thread(new Runnable() {
    +        public void run() {
    +            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
    +            mImageView.post(new Runnable() {
    +                public void run() {
    +                    mImageView.setImageBitmap(bitmap);
    +                }
    +            });
    +        }
    +    }).start();
    +}
    +
    + +

    现在,上述实现属于线程安全型:在单独的线程中完成网络操作,而在 UI 线程中操纵 +{@link android.widget.ImageView}。

    + +

    但是,随着操作日趋复杂,这类代码也会变得复杂且难以维护。 +要通过工作线程处理更复杂的交互,可以考虑在工作线程中使用 +{@link android.os.Handler} +处理来自 UI 线程的消息。当然,最好的解决方案或许是扩展 {@link android.os.AsyncTask} 类,此类简化了与 +UI 进行交互所需执行的工作线程任务。

    + + +

    使用 AsyncTask

    + +

    {@link android.os.AsyncTask} +允许对用户界面执行异步操作。它会先阻塞工作线程中的操作,然后在 UI +线程中发布结果,而无需您亲自处理线程和/或处理程序。

    + +

    要使用它,必须创建 {@link android.os.AsyncTask} 子类并实现 {@link +android.os.AsyncTask#doInBackground doInBackground()} +回调方法,该方法将在后台线程池中运行。要更新 UI,必须实现 {@link +android.os.AsyncTask#onPostExecute onPostExecute()} 以传递 {@link +android.os.AsyncTask#doInBackground doInBackground()} 返回的结果并在 UI 线程中运行,这样,您即可安全更新 UI。稍后,您可以通过从 UI 线程调用 +{@link android.os.AsyncTask#execute execute()} +来运行任务。

    + +

    例如,您可以通过以下方式使用 +{@link android.os.AsyncTask} 来实现上述示例:

    + +
    +public void onClick(View v) {
    +    new DownloadImageTask().execute("http://example.com/image.png");
    +}
    +
    +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    +    /** The system calls this to perform work in a worker thread and
    +      * delivers it the parameters given to AsyncTask.execute() */
    +    protected Bitmap doInBackground(String... urls) {
    +        return loadImageFromNetwork(urls[0]);
    +    }
    +    
    +    /** The system calls this to perform work in the UI thread and delivers
    +      * the result from doInBackground() */
    +    protected void onPostExecute(Bitmap result) {
    +        mImageView.setImageBitmap(result);
    +    }
    +}
    +
    + +

    现在 UI 是安全的,代码也得到简化,因为任务分解成了两部分:一部分应在工作线程内完成,另一部分应在 +UI 线程内完成。

    + +

    下面简要概述了 AsyncTask 的工作方法,但要全面了解如何使用此类,您应阅读 {@link android.os.AsyncTask} +参考文档:

    + +
      +
    • 可以使用泛型指定参数类型、进度值和任务最终值 +
    • +
    • 方法 +{@link android.os.AsyncTask#doInBackground doInBackground()} 会在工作线程上自动执行
    • +
    • {@link android.os.AsyncTask#onPreExecute onPreExecute()}、{@link +android.os.AsyncTask#onPostExecute onPostExecute()} 和 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} 均在 UI 线程中调用
    • +
    • {@link android.os.AsyncTask#doInBackground doInBackground()} 返回的值将发送到 +{@link android.os.AsyncTask#onPostExecute onPostExecute()}
    • +
    • 您可以随时在 {@link +android.os.AsyncTask#doInBackground doInBackground()} 中调用{@link android.os.AsyncTask#publishProgress publishProgress()},以在 UI 线程中执行 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()}
    • +
    • 您可以随时取消任何线程中的任务
    • +
    + +

    注意:使用工作线程时可能会遇到另一个问题,即:运行时配置变更(例如,用户更改了屏幕方向)导致 Activity 意外重启,这可能会销毁工作线程。 + +要了解如何在这种重启情况下坚持执行任务,以及如何在 Activity 被销毁时正确地取消任务,请参阅书架示例应用的源代码。 + +

    + + +

    线程安全方法

    + +

    在某些情况下,您实现的方法可能会从多个线程调用,因此编写这些方法时必须确保其满足线程安全的要求。 +

    + +

    这一点主要适用于可以远程调用的方法,如绑定服务中的方法。如果对 +{@link android.os.IBinder} +中所实现方法的调用源自运行 +{@link android.os.IBinder IBinder} +的同一进程,则该方法在调用方的线程中执行。但是,如果调用源自其他进程,则该方法将在从线程池选择的某个线程中执行(而不是在进程的 UI 线程中执行),线程池由系统在与 {@link android.os.IBinder +IBinder} 相同的进程中维护。例如,即使服务的 +{@link android.app.Service#onBind onBind()} 方法将从服务进程的 UI +线程调用,在 +{@link android.app.Service#onBind +onBind()} 返回的对象中实现的方法(例如,实现 RPC 方法的子类)仍会从线程池中的线程调用。由于一个服务可以有多个客户端,因此可能会有多个池线程在同一时间使用同一 +{@link android.os.IBinder IBinder} 方法。因此,{@link android.os.IBinder +IBinder} 方法必须实现为线程安全方法。

    + +

    同样,内容提供程序也可接收来自其他进程的数据请求。尽管 +{@link android.content.ContentResolver} 和 {@link android.content.ContentProvider} +类隐藏了如何管理进程间通信的细节,但响应这些请求的 {@link +android.content.ContentProvider} 方法({@link +android.content.ContentProvider#query query()}、{@link android.content.ContentProvider#insert +insert()}、{@link android.content.ContentProvider#delete delete()}、{@link +android.content.ContentProvider#update update()} 和 {@link android.content.ContentProvider#getType +getType()} 方法)将从内容提供程序所在进程的线程池中调用,而不是从进程的 +UI 线程调用。由于这些方法可能会同时从任意数量的线程调用,因此它们也必须实现为线程安全方法。 +

    + + +

    进程间通信

    + +

    Android 利用远程过程调用 +(RPC) 提供了一种进程间通信 +(IPC) +机制,通过这种机制,由 Activity 或其他应用组件调用的方法将(在其他进程中)远程执行,而所有结果将返回给调用方。这就要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。 + +然后,返回值将沿相反方向传输回来。 +Android 提供了执行这些 IPC +事务所需的全部代码,因此您只需集中精力定义和实现 RPC 编程接口即可。

    + +

    要执行 IPC,必须使用 {@link +android.content.Context#bindService bindService()} 将应用绑定到服务上。如需了解详细信息,请参阅服务开发者指南。

    + + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/recents.jd b/docs/html-intl/intl/zh-cn/guide/components/recents.jd new file mode 100644 index 0000000000000000000000000000000000000000..2bf1a5bd4ff35752c127d8399e086f615ab0ffce --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=概览屏幕 +page.tags="recents","overview" + +@jd:body + +
    +
    + +

    本文内容

    +
      +
    1. 将任务添加到概览屏幕 +
        +
      1. 使用 Intent 标志添加任务
      2. +
      3. 使用 Activity 属性添加任务
      4. +
      +
    2. +
    3. 删除任务 +
        +
      1. 使用 AppTask 类删除任务
      2. +
      3. 保留已完成的任务
      4. +
      +
    4. +
    + +

    关键类

    +
      +
    1. {@link android.app.ActivityManager.AppTask}
    2. +
    3. {@link android.content.Intent}
    4. +
    + +

    示例代码

    +
      +
    1. 以文档为中心的应用
    2. +
    + +
    +
    + +

    概览屏幕(也称为最新动态屏幕、最近任务列表或最近使用的应用)是一个系统级别 +UI,其中列出了最近访问过的Activity任务。 +用户可以浏览该列表并选择要恢复的任务,也可以通过滑动清除任务将其从列表中删除。 + +对于 +Android 5.0 版本(API 级别 21),包含多个文档的同一 Activity 的多个实例可能会以任务的形式显示在概览屏幕中。例如,Google Drive 可能对多个 +Google 文档中的每个文档均执行一个任务。每个文档均以任务的形式显示在概览屏幕中。 +

    + + +

    图 1. 显示了三个 +Google Drive 文档的概览屏幕,每个文档分别以一个单独的任务表示。

    + +

    通常,您应该允许系统定义任务和 Activity 在概览屏幕中的显示方法,并且无需修改此行为。不过,应用可以确定 Activity 在概览屏幕中的显示方式和时间。 + +您可以使用 {@link android.app.ActivityManager.AppTask} +类来管理任务,使用 +{@link android.content.Intent} +类的 Activity 标志来指定某 Activity 添加到概览屏幕或从中删除的时间。此外,您也可以使用 +<activity> 属性在清单文件中设置该行为。

    + +

    将任务添加到概览屏幕

    + +

    通过使用 {@link android.content.Intent} +类的标志添加任务,您可以更好地控制某文档在概览屏幕中打开或重新打开的时间和方式。使用 +<activity> +属性时,您可以选择始终在新任务中打开文档,或选择对文档重复使用现有任务。 +

    + +

    使用 Intent 标志添加任务

    + +

    为 Activity 创建新文档时,可调用 +{@link android.app.ActivityManager.AppTask} +类的 {@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +方法,以向其传递启动 Activity 的 Intent。要插入逻辑换行符以便系统将 Activity 视为新任务显示在概览屏幕中,可在启动 Activity 的 +{@link android.content.Intent} +的 {@link android.content.Intent#addFlags(int) addFlags()} 方法中传递 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +标志。

    + +

    注:{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +标志取代了 {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} +标志,后者自 Android 5.0(API 级别 21)起已弃用。

    + +

    如果在创建新文档时设置 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} +标志,则系统始终会以目标 Activity 作为根创建新任务。此设置允许同一文档在多个任务中打开。以下代码演示了主 Activity 如何执行此操作: +

    + +

    DocumentCentricActivity.java +

    +
    +public void createNewDocument(View view) {
    +      final Intent newDocumentIntent = newDocumentIntent();
    +      if (useMultipleTasks) {
    +          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    +      }
    +      startActivity(newDocumentIntent);
    +  }
    +
    +  private Intent newDocumentIntent() {
    +      boolean useMultipleTasks = mCheckbox.isChecked();
    +      final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
    +      newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
    +      newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
    +      return newDocumentIntent;
    +  }
    +
    +  private static int incrementAndGet() {
    +      Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
    +      return mDocumentCounter++;
    +  }
    +}
    +
    + +

    注:使用 {@code FLAG_ACTIVITY_NEW_DOCUMENT} +标志启动的 Activity 必须具有在清单文件中设置的 {@code android:launchMode="standard"} +属性值(默认)。

    + +

    当主 Activity 启动新 Activity 时,系统会搜遍现有任务,看看是否有任务的 Intent 与 Activity 的 Intent 组件名称和 Intent 数据相匹配。 +如果未找到任务或者 Intent 包含 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} +标志,则会以该 Activity 作为其根创建新任务。如果找到的话,则会将该任务转到前台并将新 + Intent +传递给 +{@link android.app.Activity#onNewIntent onNewIntent()}。新 Activity 将获得 Intent 并在概览屏幕中创建新文档,如下例所示:

    + +

    NewDocumentActivity.java +

    +
    +@Override
    +protected void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.activity_new_document);
    +    mDocumentCount = getIntent()
    +            .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
    +    mDocumentCounterTextView = (TextView) findViewById(
    +            R.id.hello_new_document_text_view);
    +    setDocumentCounterText(R.string.hello_new_document_counter);
    +}
    +
    +@Override
    +protected void onNewIntent(Intent intent) {
    +    super.onNewIntent(intent);
    +    /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
    +    is reused to create a new document.
    +     */
    +    setDocumentCounterText(R.string.reusing_document_counter);
    +}
    +
    + + +

    使用 Activity 属性添加任务

    + +

    此外,Activity 还可以在其清单文件中指定始终通过使用 +<activity> +属性 {@code android:documentLaunchMode} 进入新任务。 +此属性有四个值,会在用户使用该应用打开文档时产生以下效果: +

    + +
    +
    “{@code intoExisting}”
    +
    该 Activity 会对文档重复使用现有任务。这与不设置 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} +标志、但设置 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 标志所产生的效果相同,如上文的使用 Intent 标志添加任务中所述。
    + +
    “{@code always}”
    +
    该 Activity 为文档创建新任务,即便文档已打开也是如此。使用此值与同时设置 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +和 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 标志所产生的效果相同。
    + +
    “{@code none”}
    +
    该 Activity 不会为文档创建新任务。概览屏幕将按其默认方式对待此 Activity:为应用显示单个任务,该任务将从用户上次调用的任意 Activity 开始继续执行。 + +
    + +
    “{@code never}”
    +
    该 Activity 不会为文档创建新任务。设置此值会替代 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +和 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 标志的行为(如果在 + Intent +中设置了其中一个标志),并且概览屏幕将为应用显示单个任务,该任务将从用户上次调用的任意 Activity 开始继续执行。
    +
    + +

    注:对于除 {@code none} 和 {@code never} +以外的值,必须使用 {@code launchMode="standard"} 定义 Activity。如果未指定此属性,则使用 +{@code documentLaunchMode="none"}。

    + +

    删除任务

    + +

    默认情况下,在 Activity 结束后,文档任务会从概览屏幕中自动删除。 +您可以使用 {@link android.app.ActivityManager.AppTask} +类、{@link android.content.Intent} 标志或 +<activity> 属性替代此行为。

    + +

    通过将 +<activity> +属性 +{@code android:excludeFromRecents} 设置为 {@code true},您可以始终将任务从概览屏幕中完全排除。

    + +

    您可以通过将 +<activity> +属性 {@code android:maxRecents} +设置为整型值,设置应用能够包括在概览屏幕中的最大任务数。默认值为 16。达到最大任务数后,最近最少使用的任务将从概览屏幕中删除。 +{@code android:maxRecents} +的最大值为 50(内存不足的设备上为 25);小于 1 的值无效。

    + +

    使用 AppTask 类删除任务

    + +

    在于概览屏幕创建新任务的 Activity 中,您可以通过调用 +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} +方法指定何时删除该任务以及结束所有与之相关的 Activity。

    + +

    NewDocumentActivity.java +

    +
    +public void onRemoveFromRecents(View view) {
    +    // The document is no longer needed; remove its task.
    +    finishAndRemoveTask();
    +}
    +
    + +

    注:如下所述,使用 +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} +方法代替使用 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} +标记。

    + +

    保留已完成的任务

    + +

    若要将任务保留在概览屏幕中(即使其 Activity 已完成),可在启动 Activity 的 + Intent 的 {@link android.content.Intent#addFlags(int) addFlags()} 方法中传递 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 标志。

    + +

    DocumentCentricActivity.java +

    +
    +private Intent newDocumentIntent() {
    +    final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
    +    newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
    +      android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
    +    newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
    +    return newDocumentIntent;
    +}
    +
    + +

    要达到同样的效果,请将 +<activity> +属性 +{@code android:autoRemoveFromRecents} 设置为 {@code false}。文档 Activity 的默认值为 +{@code true},常规 Activity 的默认值为 {@code false}。如前所述,使用此属性替代 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 标志。

    + + + + + + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/services.jd b/docs/html-intl/intl/zh-cn/guide/components/services.jd new file mode 100644 index 0000000000000000000000000000000000000000..9a00e704fa38076e3d15e0c18582fb1c5e702f41 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=服务 +@jd:body + + + + +

    {@link android.app.Service} +是一个可以在后台执行长时间运行操作而不使用用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 + +此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 +例如,服务可以处理网络事务、播放音乐,执行文件 I/O +或与内容提供程序交互,而所有这一切均可在后台进行。 +

    + +

    服务基本上分为两种形式:

    + +
    +
    启动
    +
    当应用组件(如 Activity)通过调用 +{@link android.content.Context#startService startService()} 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 +已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。 + +操作完成后,服务会自行停止运行。 +
    +
    绑定
    +
    当应用组件通过调用 {@link +android.content.Context#bindService bindService()} 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) +跨进程执行这些操作。 +仅当与另一个应用组件绑定时,绑定服务才会运行。 +多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。 +
    +
    + +

    虽然本文档是分开概括讨论这两种服务,但是您的服务可以同时以这两种方式运行,也就是说,它既可以是启动服务(以无限期运行),也允许绑定。问题只是在于您是否实现了一组回调方法:{@link +android.app.Service#onStartCommand onStartCommand()}(允许组件启动服务)和 {@link +android.app.Service#onBind onBind()}(允许绑定服务)。 + +

    + +

    无论应用是处于启动状态还是绑定状态,抑或处于启动并且绑定状态,任何应用组件均可像使用活动那样通过调用 {@link android.content.Intent} 来使用服务(即使此服务来自另一应用)。 + +不过,您可以通过清单文件将服务声明为私有服务,并阻止其他应用访问。 +使用清单文件声明服务部分将对此做更详尽的阐述。 + +

    + +

    注意:服务在其托管进程的主线程中运行,它既创建自己的线程,也在单独的进程中运行(除非另行指定)。 + +这意味着,如果服务将执行任何 +CPU 密集型工作或阻止性操作(例如 MP3 +播放或联网),则应在服务内创建新线程来完成这项工作。通过使用单独的线程,可以降低发生“应用无响应”(ANR) +错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。 +

    + + +

    基础知识

    + + + +

    要创建服务,您必须创建 +{@link android.app.Service} 的子类(或使用它的一个现有子类)。在实现中,您需要重写一些回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务(如适用)。 + +应重写的最重要的回调方法包括:

    + +
    +
    {@link android.app.Service#onStartCommand onStartCommand()}
    +
    当另一个组件(如 Activity)通过调用 +{@link android.content.Context#startService +startService()} 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 +如果您实现此方法,则在服务工作完成后,需要由您通过调用 +{@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 来停止服务。(如果您只想提供绑定,则无需实现此方法。) +
    +
    {@link android.app.Service#onBind onBind()}
    +
    当另一个组件想通过调用 {@link android.content.Context#bindService +bindService()} +与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回 +{@link android.os.IBinder} 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。 +
    +
    {@link android.app.Service#onCreate()}
    +
    首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 +{@link android.app.Service#onStartCommand onStartCommand()} 或 +{@link android.app.Service#onBind onBind()} 之前)。如果服务已在运行,则不会调用此方法。 +
    +
    {@link android.app.Service#onDestroy()}
    +
    当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 + +这是服务接收的最后一个调用。
    +
    + +

    如果组件通过调用 {@link +android.content.Context#startService startService()} 启动服务(这会导致对 {@link +android.app.Service#onStartCommand onStartCommand()} 的调用),则服务将一直运行,直到服务使用 +{@link android.app.Service#stopSelf()} +自行停止运行,或由其他组件通过调用 {@link android.content.Context#stopService stopService()} 停止它为止。

    + +

    如果组件是通过调用 +{@link android.content.Context#bindService bindService()} 来创建服务(且调用 +{@link +android.app.Service#onStartCommand onStartCommand()}),则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。 +

    + +

    仅当内存过低且必须回收系统资源以供具有用户焦点的 Activity 使用时,Android +系统才会强制停止服务。如果将服务绑定到具有用户焦点的 Activity,则它不太可能会终止;如果将服务声明为在前台运行(稍后讨论),则它几乎永远不会终止。或者,如果服务已启动并要长时间运行,则系统会随着时间的推移降低服务在后台任务列表中的位置,而服务也将随之变得非常容易被终止;如果服务是启动服务,则您必须将其设计为能够妥善处理系统对它的重启。 + + + + +如果系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(不过这还取决于从 +{@link +android.app.Service#onStartCommand onStartCommand()} 返回的值,本文稍后会对此加以讨论)。如需了解有关系统会在何时销毁服务的详细信息,请参阅进程和线程文档。 + +

    + +

    在下文中,您将了解如何创建各类服务以及如何从其他应用组件使用服务。 +

    + + + +

    使用清单文件声明服务

    + +

    如同 Activity(以及其他组件)一样,您必须在应用的清单文件中声明所有服务。 +

    + +

    要声明服务,请添加 {@code <service>} +元素作为 {@code <application>} +元素的子元素。例如:

    + +
    +<manifest ... >
    +  ...
    +  <application ... >
    +      <service android:name=".ExampleService" />
    +      ...
    +  </application>
    +</manifest>
    +
    + +

    如需了解有关使用清单文件声明服务的详细信息,请参阅 +{@code <service>} 元素参考文档。

    + +

    您还可将其他属性包括在 +{@code <service>} +元素中,以定义一些特性,如启动服务及其运行所在进程所需的权限。{@code android:name} +属性是唯一必需的属性,用于指定服务的类名。应用一旦发布,即不应更改此类名,如若不然,可能会存在因依赖显式 Intent 启动或绑定服务而破坏代码的风险(请阅读博客文章Things That Cannot Change[不能更改的内容])。 + + + + +

    为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 {@link android.app.Service},且不要为服务声明 Intent 过滤器。 +启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 +{@link +android.content.Intent} +中排除相应的组件名称,但随后必须使用 {@link +android.content.Intent#setPackage setPackage()} + 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性。

    + +

    此外,还可以通过添加 +{@code android:exported} +属性并将其设置为 {@code "false"},确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此。 +

    + + + + +

    创建启动服务

    + +

    启动服务由另一个组件通过调用 {@link +android.content.Context#startService startService()} 启动,这会导致调用服务的 +{@link android.app.Service#onStartCommand onStartCommand()} 方法。

    + +

    服务启动之后,其生命周期即独立于启动它的组件,并且可以在后台无限期地运行,即使启动服务的组件已被销毁也不受影响。 + +因此,服务应通过调用 +{@link android.app.Service#stopSelf stopSelf()} 结束工作来自行停止运行,或者由另一个组件通过调用 +{@link android.content.Context#stopService stopService()} 来停止它。

    + +

    应用组件(如 Activity)可以通过调用 {@link +android.content.Context#startService startService()} 方法并传递 {@link android.content.Intent} 对象 +(指定服务并包含待使用服务的所有数据)来启动服务。服务通过 +{@link android.app.Service#onStartCommand +onStartCommand()} 方法接收此 {@link android.content.Intent}。

    + +

    例如,假设某 Activity 需要将一些数据保存到在线数据库中。该 Activity 可以启动一个协同服务,并通过向 +{@link +android.content.Context#startService startService()} 传递一个 Intent,为该服务提供要保存的数据。服务通过 +{@link +android.app.Service#onStartCommand onStartCommand()} 接收 Intent,连接到 Internet 并执行数据库事务。事务完成之后,服务会自行停止运行并随即被销毁。 +

    + +

    注意:默认情况下,服务与服务声明所在的应用运行于同一进程,而且运行于该应用的主线程中。 +因此,如果服务在用户与来自同一应用的 Activity 进行交互时执行密集型或阻止性操作,则会降低 Activity 性能。 + +为了避免影响应用性能,您应在服务内启动新线程。 +

    + +

    从传统上讲,您可以扩展两个类来创建启动服务:

    +
    +
    {@link android.app.Service}
    +
    这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。 + + +
    +
    {@link android.app.IntentService}
    +
    这是 +{@link android.app.Service} 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 +您只需实现 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} +方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。
    +
    + +

    下文介绍如何使用其中任一类实现服务。 +

    + + +

    扩展 IntentService 类

    + +

    由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 +{@link android.app.IntentService} +类实现服务也许是最好的选择。

    + +

    {@link android.app.IntentService} 执行以下操作:

    + +
      +
    • 创建默认的工作线程,用于在应用的主线程外执行传递给 {@link +android.app.Service#onStartCommand onStartCommand()} +的所有 Intent。
    • +
    • 创建工作队列,用于将一个 Intent 逐一传递给 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} +实现,这样您就永远不必担心多线程问题。
    • +
    • 在处理完所有启动请求后停止服务,因此您永远不必调用 +{@link android.app.Service#stopSelf}。
    • +
    • 提供 +{@link android.app.IntentService#onBind onBind()} 的默认实现(返回 null)。
    • +
    • 提供 {@link android.app.IntentService#onStartCommand +onStartCommand()} 的默认实现,可将 Intent 依次发送到工作队列和 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 实现。
    • +
    + +

    综上所述,您只需实现 +{@link +android.app.IntentService#onHandleIntent onHandleIntent()} 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)

    + +

    以下是 {@link android.app.IntentService} 的实现示例:

    + +
    +public class HelloIntentService extends IntentService {
    +
    +  /**
    +   * A constructor is required, and must call the super {@link android.app.IntentService#IntentService}
    +   * constructor with a name for the worker thread.
    +   */
    +  public HelloIntentService() {
    +      super("HelloIntentService");
    +  }
    +
    +  /**
    +   * The IntentService calls this method from the default worker thread with
    +   * the intent that started the service. When this method returns, IntentService
    +   * stops the service, as appropriate.
    +   */
    +  @Override
    +  protected void onHandleIntent(Intent intent) {
    +      // Normally we would do some work here, like download a file.
    +      // For our sample, we just sleep for 5 seconds.
    +      long endTime = System.currentTimeMillis() + 5*1000;
    +      while (System.currentTimeMillis() < endTime) {
    +          synchronized (this) {
    +              try {
    +                  wait(endTime - System.currentTimeMillis());
    +              } catch (Exception e) {
    +              }
    +          }
    +      }
    +  }
    +}
    +
    + +

    您只需要一个构造函数和一个 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 实现即可。

    + +

    如果您决定还重写其他回调方法(如 {@link +android.app.IntentService#onCreate onCreate()}、{@link +android.app.IntentService#onStartCommand onStartCommand()} 或 {@link +android.app.IntentService#onDestroy onDestroy()}),请确保调用超类实现,以便 +{@link android.app.IntentService} 能够妥善处理工作线程的生命周期。

    + +

    例如,{@link android.app.IntentService#onStartCommand onStartCommand()} +必须返回默认实现(即,如何将 Intent 传递给 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):

    + +
    +@Override
    +public int onStartCommand(Intent intent, int flags, int startId) {
    +    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    +    return super.onStartCommand(intent,flags,startId);
    +}
    +
    + +

    除 +{@link android.app.IntentService#onHandleIntent onHandleIntent()} 之外,您无需从中调用超类的唯一方法就是 {@link android.app.IntentService#onBind +onBind()}(仅当服务允许绑定时,才需要实现该方法)。

    + +

    在下一部分中,您将了解如何在扩展 +{@link android.app.Service} +基类时实现同类服务。该基类包含更多代码,但如需同时处理多个启动请求,则更适合使用该基类。

    + + +

    扩展服务类

    + +

    正如上一部分中所述,使用 +{@link android.app.IntentService} 显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展 +{@link android.app.Service} +类来处理每个 Intent。

    + +

    为了便于比较,以下提供了 {@link +android.app.Service} 类实现的代码示例,该类执行的工作与上述使用 {@link +android.app.IntentService} 的示例完全相同。也就是说,对于每个启动请求,它均使用工作线程执行作业,且每次仅处理一个请求。 +

    + +
    +public class HelloService extends Service {
    +  private Looper mServiceLooper;
    +  private ServiceHandler mServiceHandler;
    +
    +  // Handler that receives messages from the thread
    +  private final class ServiceHandler extends Handler {
    +      public ServiceHandler(Looper looper) {
    +          super(looper);
    +      }
    +      @Override
    +      public void handleMessage(Message msg) {
    +          // Normally we would do some work here, like download a file.
    +          // For our sample, we just sleep for 5 seconds.
    +          long endTime = System.currentTimeMillis() + 5*1000;
    +          while (System.currentTimeMillis() < endTime) {
    +              synchronized (this) {
    +                  try {
    +                      wait(endTime - System.currentTimeMillis());
    +                  } catch (Exception e) {
    +                  }
    +              }
    +          }
    +          // Stop the service using the startId, so that we don't stop
    +          // the service in the middle of handling another job
    +          stopSelf(msg.arg1);
    +      }
    +  }
    +
    +  @Override
    +  public void onCreate() {
    +    // Start up the thread running the service.  Note that we create a
    +    // separate thread because the service normally runs in the process's
    +    // main thread, which we don't want to block.  We also make it
    +    // background priority so CPU-intensive work will not disrupt our UI.
    +    HandlerThread thread = new HandlerThread("ServiceStartArguments",
    +            Process.THREAD_PRIORITY_BACKGROUND);
    +    thread.start();
    +
    +    // Get the HandlerThread's Looper and use it for our Handler
    +    mServiceLooper = thread.getLooper();
    +    mServiceHandler = new ServiceHandler(mServiceLooper);
    +  }
    +
    +  @Override
    +  public int onStartCommand(Intent intent, int flags, int startId) {
    +      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    +
    +      // For each start request, send a message to start a job and deliver the
    +      // start ID so we know which request we're stopping when we finish the job
    +      Message msg = mServiceHandler.obtainMessage();
    +      msg.arg1 = startId;
    +      mServiceHandler.sendMessage(msg);
    +
    +      // If we get killed, after returning from here, restart
    +      return START_STICKY;
    +  }
    +
    +  @Override
    +  public IBinder onBind(Intent intent) {
    +      // We don't provide binding, so return null
    +      return null;
    +  }
    +
    +  @Override
    +  public void onDestroy() {
    +    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
    +  }
    +}
    +
    + +

    正如您所见,与使用 {@link android.app.IntentService} 相比,这需要执行更多工作。

    + +

    但是,因为是由您自己处理对 {@link android.app.Service#onStartCommand +onStartCommand()} 的每个调用,因此可以同时执行多个请求。此示例并未这样做,但如果您希望如此,则可为每个请求创建一个新线程,然后立即运行这些线程(而不是等待上一个请求完成)。 + +

    + +

    请注意,{@link android.app.Service#onStartCommand onStartCommand()} +方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,{@link +android.app.IntentService} +的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 +{@link android.app.Service#onStartCommand onStartCommand()} +返回的值必须是以下常量之一:

    + +
    +
    {@link android.app.Service#START_NOT_STICKY}
    +
    如果系统在 +{@link android.app.Service#onStartCommand +onStartCommand()} 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。 +
    +
    {@link android.app.Service#START_STICKY}
    +
    如果系统在 +{@link android.app.Service#onStartCommand +onStartCommand()} 返回后终止服务,则会重建服务并调用 +{@link +android.app.Service#onStartCommand onStartCommand()},但绝对不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 +{@link android.app.Service#onStartCommand onStartCommand()}。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。 +
    +
    {@link android.app.Service#START_REDELIVER_INTENT}
    +
    如果系统在 {@link android.app.Service#onStartCommand +onStartCommand()} 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 +{@link +android.app.Service#onStartCommand onStartCommand()}。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。 +
    +
    +

    有关这些返回值的更多详细信息,请查阅每个常量链接的参考文档。 +

    + + + +

    启动服务

    + +

    您可以通过将 +{@link android.content.Intent}(指定要启动的服务)传递给 {@link +android.content.Context#startService startService()},从 Activity 或其他应用组件启动服务。Android 系统调用服务的 {@link +android.app.Service#onStartCommand onStartCommand()} 方法,并向其传递 {@link +android.content.Intent}。(切勿直接调用 {@link android.app.Service#onStartCommand +onStartCommand()}。)

    + +

    例如,Activity 可以结合使用显式 Intent 与 {@link android.content.Context#startService +startService()},启动上文中的示例服务 ({@code +HelloSevice}):

    + +
    +Intent intent = new Intent(this, HelloService.class);
    +startService(intent);
    +
    + +

    {@link android.content.Context#startService startService()} 方法将立即返回,且 +Android 系统调用服务的 {@link android.app.Service#onStartCommand +onStartCommand()} 方法。如果服务尚未运行,则系统会先调用 {@link +android.app.Service#onCreate onCreate()},然后再调用 {@link android.app.Service#onStartCommand +onStartCommand()}。

    + +

    如果服务亦未提供绑定,则使用 +{@link +android.content.Context#startService startService()} 传递的 Intent 是应用组件与服务之间唯一的通信模式。但是,如果您希望服务返回结果,则启动服务的客户端可以为广播创建一个 +{@link android.app.PendingIntent} +(使用 {@link android.app.PendingIntent#getBroadcast getBroadcast()}),并通过启动服务的 +{@link android.content.Intent} 传递给服务。然后,服务就可以使用广播传递结果。 +

    + +

    多个服务启动请求会导致多次对服务的 +{@link android.app.Service#onStartCommand onStartCommand()} 进行相应的调用。但是,要停止服务,只需一个服务停止请求(使用 +{@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()})即可。

    + + +

    停止服务

    + +

    启动服务必须管理自己的生命周期。也就是说,除非系统必须回收内存资源,否则系统不会停止或销毁服务,而且服务在 +{@link android.app.Service#onStartCommand onStartCommand()} +返回后会继续运行。因此,服务必须通过调用 +{@link android.app.Service#stopSelf stopSelf()} 自行停止运行,或者由另一个组件通过调用 +{@link android.content.Context#stopService stopService()} 来停止它。

    + +

    一旦请求使用 {@link android.app.Service#stopSelf stopSelf()} +或 {@link +android.content.Context#stopService stopService()} 停止服务,系统就会尽快销毁服务。

    + +

    但是,如果服务同时处理多个 +{@link +android.app.Service#onStartCommand onStartCommand()} +请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用 +{@link android.app.Service#stopSelf(int)} +确保服务停止请求始终基于最近的启动请求。也就说,在调用 {@link +android.app.Service#stopSelf(int)} 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给 {@link android.app.Service#onStartCommand onStartCommand()} +的 startId) +。然后,如果在您能够调用 {@link +android.app.Service#stopSelf(int)} 之前服务收到了新的启动请求, ID 就不匹配,服务也就不会停止。

    + +

    注意:为了避免浪费系统资源和消耗电池电量,应用必须在工作完成之后停止其服务。 +如有必要,其他组件可以通过调用 +{@link +android.content.Context#stopService stopService()} 来停止服务。即使为服务启用了绑定,一旦服务收到对 +{@link +android.app.Service#onStartCommand onStartCommand()} 的调用,您始终仍须亲自停止服务。

    + +

    如需了解有关服务生命周期的详细信息,请参阅下面有关管理服务生命周期的部分。

    + + + +

    创建绑定服务

    + +

    绑定服务允许应用组件通过调用 {@link +android.content.Context#bindService bindService()} 与其绑定,以便创建长期连接(通常不允许组件通过调用 +{@link +android.content.Context#startService startService()} 来启动它)。

    + +

    如需与 Activity 和其他应用组件中的服务进行交互,或者需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。 + +

    + +

    要创建绑定服务,必须实现 +{@link +android.app.Service#onBind onBind()} 回调方法以返回 {@link android.os.IBinder},用于定义与服务通信的接口。然后,其他应用组件可以调用 +{@link android.content.Context#bindService bindService()} +来检索该接口,并开始对服务调用方法。服务只用于与其绑定的应用组件,因此如果没有组件绑定到服务,则系统会销毁服务(您不必按通过 +{@link android.app.Service#onStartCommand onStartCommand()} +启动的服务那样来停止绑定服务)。 +

    + +

    要创建绑定服务,首先必须定义指定客户端如何与服务通信的接口。 +服务与客户端之间的这个接口必须是 +{@link android.os.IBinder} 的实现,并且服务必须从 +{@link android.app.Service#onBind +onBind()} 回调方法返回它。一旦客户端收到 +{@link android.os.IBinder},即可开始通过该接口与服务进行交互。

    + +

    多个客户端可以同时绑定到服务。客户端完成与服务的交互后,会调用 +{@link android.content.Context#unbindService unbindService()} 取消绑定。一旦没有客户端绑定到该服务,系统就会销毁它。 +

    + +

    有多种方法实现绑定服务,其实现比启动服务更为复杂,因此绑定服务将在有关绑定服务的单独文档中专门讨论。 + +

    + + + +

    向用户发送通知

    + +

    一旦运行起来,服务即可使用 Toast 通知状态栏通知来通知用户所发生的事件。

    + +

    Toast 通知是指出现在当前窗口的表面、片刻随即消失不见的消息,而状态栏通知则在状态栏提供内含消息的图标,用户可以选择该图标来采取操作(例如启动 Activity)。 + +

    + +

    通常,当某些后台工作已经完成(例如文件下载完成)且用户现在可以对其进行操作时,状态栏通知是最佳方法。 + +当用户从展开视图中选定通知时,通知即可启动 Activity(例如查看已下载的文件)。 +

    + +

    如需了解详细信息,请参阅 +Toast 通知状态栏通知开发者指南。

    + + + +

    在前台运行服务

    + +

    前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 +前台服务必须为状态栏提供通知,状态栏位于“正在进行”标题下方,这意味着除非服务停止或从前台删除,否则不能清除通知。 + + +

    + +

    例如,应该将从服务播放音乐的音乐播放器设置为在前台运行,这是因为用户明确意识到其操作。 + +状态栏中的通知可能表示正在播放的歌曲,并允许用户启动 Activity 来与音乐播放器进行交互。 +

    + +

    要请求让服务运行于前台,请调用 {@link +android.app.Service#startForeground startForeground()}。此方法取两个参数:唯一标识通知的整型数和状态栏的 +{@link +android.app.Notification}。例如:

    + +
    +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
    +        System.currentTimeMillis());
    +Intent notificationIntent = new Intent(this, ExampleActivity.class);
    +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    +notification.setLatestEventInfo(this, getText(R.string.notification_title),
    +        getText(R.string.notification_message), pendingIntent);
    +startForeground(ONGOING_NOTIFICATION_ID, notification);
    +
    + +

    注意:提供给 {@link +android.app.Service#startForeground startForeground()} 的整型 ID 不得为 0。

    + + +

    要从前台删除服务,请调用 +{@link android.app.Service#stopForeground stopForeground()}。此方法取一个布尔值,指示是否也删除状态栏通知。 +此方法绝对不会停止服务。 +但是,如果您在服务正在前台运行时将其停止,则通知也会被删除。 +

    + +

    如需了解有关通知的详细信息,请参阅创建状态栏通知。 +

    + + + +

    管理服务生命周期

    + +

    服务的生命周期比 Activity 的生命周期要简单得多。但是,密切关注如何创建和销毁服务反而更加重要,因为服务可以在用户没有意识到的情况下运行于后台。 + +

    + +

    服务生命周期(从创建到销毁)可以遵循两条不同的路径: +

    + +
      +
    • 启动服务 +

      该服务在其他组件调用 {@link +android.content.Context#startService startService()} 时创建,然后无限期运行,且必须通过调用 +{@link +android.app.Service#stopSelf() stopSelf()} 来自行停止运行。此外,其他组件也可以通过调用 +{@link android.content.Context#stopService +stopService()} 来停止服务。服务停止后,系统会将其销毁。

    • + +
    • 绑定服务 +

      该服务在另一个组件(客户端)调用 {@link +android.content.Context#bindService bindService()} 时创建。然后,客户端通过 +{@link android.os.IBinder} 接口与服务进行通信。客户端可以通过调用 +{@link android.content.Context#unbindService unbindService()} 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 +(服务不必自行停止运行。) +

    • +
    + +

    这两条路径并非完全独立。也就是说,您可以绑定到已经使用 +{@link android.content.Context#startService startService()} 启动的服务。例如,可以通过使用 +{@link android.content.Intent}(标识要播放的音乐)调用 {@link android.content.Context#startService +startService()} 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 +{@link +android.content.Context#bindService bindService()} +绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则 {@link +android.content.Context#stopService stopService()} 或 {@link android.app.Service#stopSelf +stopSelf()} 不会真正停止服务。

    + + +

    实现生命周期回调

    + +

    与 Activity 类似,服务也拥有生命周期回调方法,您可以实现这些方法来监控服务状态的变化并适时执行工作。 +以下框架服务展示了每种生命周期方法: +

    + +
    +public class ExampleService extends Service {
    +    int mStartMode;       // indicates how to behave if the service is killed
    +    IBinder mBinder;      // interface for clients that bind
    +    boolean mAllowRebind; // indicates whether onRebind should be used
    +
    +    @Override
    +    public void {@link android.app.Service#onCreate onCreate}() {
    +        // The service is being created
    +    }
    +    @Override
    +    public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) {
    +        // The service is starting, due to a call to {@link android.content.Context#startService startService()}
    +        return mStartMode;
    +    }
    +    @Override
    +    public IBinder {@link android.app.Service#onBind onBind}(Intent intent) {
    +        // A client is binding to the service with {@link android.content.Context#bindService bindService()}
    +        return mBinder;
    +    }
    +    @Override
    +    public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) {
    +        // All clients have unbound with {@link android.content.Context#unbindService unbindService()}
    +        return mAllowRebind;
    +    }
    +    @Override
    +    public void {@link android.app.Service#onRebind onRebind}(Intent intent) {
    +        // A client is binding to the service with {@link android.content.Context#bindService bindService()},
    +        // after onUnbind() has already been called
    +    }
    +    @Override
    +    public void {@link android.app.Service#onDestroy onDestroy}() {
    +        // The service is no longer used and is being destroyed
    +    }
    +}
    +
    + +

    注:与 Activity 生命周期回调方法不同,您不需要调用这些回调方法的超类实现。 +

    + + +

    图 2. 服务生命周期左图显示了使用 +{@link android.content.Context#startService +startService()} 所创建的服务的生命周期,右图显示了使用 +{@link android.content.Context#bindService bindService()} 所创建的服务的生命周期。

    + +

    通过实现这些方法,您可以监控服务生命周期的两个嵌套循环:

    + +
      +
    • 服务的整个生命周期从调用 {@link +android.app.Service#onCreate onCreate()} 开始起,到 {@link +android.app.Service#onDestroy} 返回时结束。与 Activity 类似,服务也在 +{@link android.app.Service#onCreate onCreate()} 中完成初始设置,并在 {@link +android.app.Service#onDestroy onDestroy()} 中释放所有剩余资源。例如,音乐播放服务可以在 {@link +android.app.Service#onCreate onCreate()} 中创建用于播放音乐的线程,然后在 {@link +android.app.Service#onDestroy onDestroy()} 中停止该线程。 + + +

      无论服务是通过 {@link android.content.Context#startService startService()} +还是 {@link +android.content.Context#bindService bindService()} 创建,都会为所有服务调用 {@link android.app.Service#onCreate onCreate()} 和 {@link android.app.Service#onDestroy +onDestroy()} 方法。

    • + +
    • 服务的有效生命周期从调用 {@link +android.app.Service#onStartCommand onStartCommand()} 或 {@link android.app.Service#onBind onBind()} +方法开始。每种方法均有 {@link +android.content.Intent} 对象,该对象分别传递到 {@link android.content.Context#startService +startService()} 或 {@link android.content.Context#bindService bindService()}。 +

      对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 +{@link android.app.Service#onStartCommand +onStartCommand()} 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 {@link +android.app.Service#onUnbind onUnbind()} 返回时结束。

      +
    • +
    + +

    注:尽管启动服务是通过调用 +{@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 来停止,但是该服务并无相应的回调(没有 {@code onStop()} +回调)。因此,除非服务绑定到客户端,否则在服务停止时,系统会将其销毁—{@link +android.app.Service#onDestroy onDestroy()} 是接收到的唯一回调。 +

    + +

    图 2 说明了服务的典型回调方法。尽管该图分开介绍通过 +{@link android.content.Context#startService startService()} 创建的服务和通过 +{@link android.content.Context#bindService bindService()} +创建的服务,但是请记住,不管启动方式如何,任何服务均有可能允许客户端与其绑定。因此,最初使用 +{@link android.app.Service#onStartCommand +onStartCommand()}(通过客户端调用 {@link android.content.Context#startService startService()})启动的服务仍可接收对 +{@link android.app.Service#onBind onBind()} 的调用(当客户端调用 +{@link android.content.Context#bindService bindService()} 时)。

    + +

    如需了解有关创建提供绑定的服务的详细信息,请参阅绑定服务文档,该文档的管理绑定服务的生命周期部分提供了有关 +{@link android.app.Service#onRebind onRebind()} +回调方法的更多信息。 +

    + + + diff --git a/docs/html-intl/intl/zh-cn/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/zh-cn/guide/components/tasks-and-back-stack.jd new file mode 100644 index 0000000000000000000000000000000000000000..07fdf6e80a027c98705f0060ad4a519fabc168ce --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=任务和返回栈 +parent.title=Activity +parent.link=activities.html +@jd:body + + + + +

    应用通常包含多个Activity。每个 Activity 均应围绕用户可以执行的特定操作设计,并且能够启动其他 Activity。 + +例如,电子邮件应用可能有一个 Activity 显示新邮件的列表。用户选择某邮件时,会打开一个新 Activity 以查看该邮件。 +

    + +

    一个 Activity 甚至可以启动设备上其他应用中存在的 Activity。例如,如果应用想要发送电子邮件,则可将 Intent 定义为执行“发送”操作并加入一些数据,如电子邮件地址和电子邮件。 + +然后,系统将打开其他应用中声明自己处理此类 + Intent 的 Activity。在这种情况下, Intent +是要发送电子邮件,因此将启动电子邮件应用的“撰写”Activity(如果多个 Activity 支持相同 + Intent,则系统会让用户选择要使用的 Activity)。发送电子邮件时,Activity 将恢复,看起来好像电子邮件 Activity 是您的应用的一部分。 +即使这两个 Activity 可能来自不同的应用,但是 +Android 仍会将 Activity 保留在相同的任务中,以维护这种无缝的用户体验。 +

    + +

    任务是指在执行特定作业时与用户交互的一系列 Activity。 +这些 Activity 按照各自的打开顺序排列在堆栈(即“返回栈”)中。 +

    + + + +

    设备主屏幕是大多数任务的起点。当用户触摸应用启动器中的图标(或主屏幕上的快捷键)时,该应用的任务将出现在前台。 + +如果应用不存在任务(应用最近未曾使用),则会创建一个新任务,并且该应用的“主”Activity 将作为堆栈中的根 Activity 打开。 + +

    + +

    当前 Activity 启动另一个 Activity 时,该新 Activity 会被推送到堆栈顶部,成为焦点所在。 +前一个 Activity 仍保留在堆栈中,但是处于停止状态。Activity 停止时,系统会保持其用户界面的当前状态。 +用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行(恢复其 UI 的前一状态)。 + + +堆栈中的 Activity 永远不会重新排列,仅推入和弹出堆栈:由当前 Activity 启动时推入堆栈;用户使用“返回”按钮退出时弹出堆栈。 + +因此,返回栈以“后进先出”对象结构运行。 + +图 1 通过时间线显示 Activity 之间的进度以及每个时间点的当前返回栈,直观呈现了这种行为。 + +

    + + +

    图 1. 显示任务中的每个新 Activity 如何向返回栈添加项目。 +用户按“返回”按钮时,当前 Activity 随即被销毁,而前一个 Activity 恢复执行。 + +

    + + +

    如果用户继续按“返回”,堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止(或者,返回任务开始时正在运行的任意 Activity)。 + + +当所有 Activity 均从堆栈中删除后,任务即不复存在。

    + +
    +

    图 2. 两个任务:任务 B +在前台接收用户交互,而任务 A 则在后台等待恢复。

    +
    +
    +

    图 3. 一个 Activity 将多次实例化。

    +
    + +

    任务是一个有机整体,当用户开始新任务或通过“主页”按钮转到主屏幕时,可以移动到“后台”。 +尽管在后台时,该任务中的所有 Activity 全部停止,但是任务的返回栈仍旧不变,也就是说,当另一个任务发生时,该任务仅仅失去焦点而已,如图 2 中所示。 + + +然后,任务可以返回到“前台”,用户就能够回到离开时的状态。 +例如,假设当前任务(任务 A)的堆栈中有三个 Activity,即当前 Activity 下方还有两个 Activity。 +用户先按“主页”按钮,然后从应用启动器启动新应用。 + +显示主屏幕时,任务 A +进入后台。新应用启动时,系统会使用自己的 Activity 堆栈为该应用启动一个任务(任务 +B)。与该应用交互之后,用户再次返回主屏幕并选择最初启动任务 A 的应用。现在,任务 A 出现在前台,其堆栈中的所有三个 Activity 保持不变,而位于堆栈顶部的 Activity 则会恢复执行。 + + + +此时,用户还可以通过转到主屏幕并选择启动该任务的应用(或者,通过从概览屏幕选择该应用的任务)切换回任务 B。这是 Android 系统中的一个多任务示例。 + + + +

    + +

    注:后台可以同时运行多个任务。但是,如果用户同时运行多个后台任务,则系统可能会开始销毁后台 Activity,以回收内存资源,从而导致 Activity 状态丢失。请参阅下面有关 Activity 状态的部分。 + + +

    + +

    由于返回栈中的 Activity 永远不会重新排列,因此如果应用允许用户从多个 Activity 中启动特定 Activity,则会创建该 Activity 的新实例并推入堆栈中(而不是将 Activity 的任一先前实例置于顶部)。 + + +因此,应用中的一个 Activity 可能会多次实例化(即使 Activity 来自不同的任务),如图 3 所示。 +因此,如果用户使用“返回”按钮向后导航,则会按 Activity 每个实例的打开顺序显示这些实例(每个实例的 UI 状态各不相同)。 + + +但是,如果您不希望 Activity 多次实例化,则可修改此行为。 +具体操作方法将在后面的管理任务部分中讨论。

    + + +

    Activity 和任务的默认行为总结如下:

    + +
      +
    • 当 Activity A 启动 Activity B 时,Activity A 将会停止,但系统会保留其状态(例如,滚动位置和已输入表单中的文本)。如果用户在处于 Activity B 时按“返回”按钮,则 Activity A 将恢复其状态,继续执行。 + + +
    • +
    • 用户通过按“主页”按钮离开任务时,当前 Activity 将停止且其任务会进入后台。 + +系统将保留任务中每个 Activity 的状态。如果用户稍后通过选择开始任务的启动器图标来恢复任务,则任务将出现在前台并恢复执行堆栈顶部的 Activity。 + +
    • +
    • 如果用户按“返回”按钮,则当前 Activity 会从堆栈弹出并被销毁。 + +堆栈中的前一个 Activity 恢复执行。销毁 Activity 时,系统绝对不会保留该 Activity 的状态。 +
    • +
    • 即使来自其他任务,Activity 也可以多次实例化。
    • +
    + + +
    +

    导航设计

    +

    如需了解有关 Android 应用导航工作方式的详细信息,请阅读 Android 设计的导航指南。

    +
    + + +

    保存 Activity 状态

    + +

    正如上文所述,当 Activity 停止时,系统的默认行为会保留其状态。 +这样一来,当用户导航回到上一个 Activity 时,其用户界面与用户离开时一样。 +但是,在 Activity 被销毁且必须重建时,您可以而且应当主动使用回调方法保留 Activity 的状态。 + +

    + +

    系统停止您的一个 Activity 时(例如,新 Activity 启动或任务转到前台),如果系统需要回收系统内存资源,则可能会完全销毁该 Activity。 + +发生这种情况时,有关该 Activity 状态的信息将会丢失。如果发生这种情况,系统仍会知道该 Activity 存在于返回栈中,但是当该 Activity 被置于堆栈顶部时,系统一定会重建 Activity(而不是恢复 Activity)。 + + +为了避免用户的工作丢失,您应主动通过在 Activity 中实现 +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +回调方法来保留工作。 +

    + +

    如需了解有关如何保存 Activity 状态的详细信息,请参阅Activity文档。 +

    + + + +

    管理任务

    + +

    Android 管理任务和返回栈的方式(如上所述,即:将所有连续启动的 Activity 放入同一任务和“后进先出”堆栈中)非常适用于大多数应用,而您不必担心 Activity 如何与任务关联或者如何存在于返回栈中。 + + +但是,您可能会决定要中断正常行为。 +也许您希望应用中的 Activity 在启动时开始新任务(而不是放置在当前任务中);或者,当启动 Activity 时,您希望将其现有实例上移一层(而不是在返回栈的顶部创建新实例);或者,您希望在用户离开任务时,清除返回栈中除根 Activity 以外的所有其他 Activity。 + + + +

    + +

    通过使用 {@code <activity>} 清单文件元素中的属性和传递给 {@link android.app.Activity#startActivity startActivity()} +的 Intent 中的标志,您可以执行所有这些操作以及其他操作。 + +

    + +

    在这一方面,您可以使用的主要 +{@code <activity>} 属性包括:

    + + + +

    您可以使用的主要 Intent 标志包括:

    + +
      +
    • {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
    • +
    • {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
    • +
    • {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
    • +
    + +

    在下文中,您将了解如何使用这些清单文件属性和 Intent +标志定义 Activity 与任务的关联方式,以及 Activity 在返回栈中的行为方式。

    + +

    此外,我们还单独介绍了有关如何在概览屏幕中显示和管理任务与 Activity 的注意事项。 +如需了解详细信息,请参阅概览屏幕。 +通常,您应该允许系统定义任务和 Activity 在概览屏幕中的显示方法,并且无需修改此行为。 +

    + +

    注意:大多数应用都不得中断 Activity 和任务的默认行为: +如果确定您的 Activity 必须修改默认行为,当使用“返回”按钮从其他 Activity 和任务导航回到该 Activity 时,请务必要谨慎并确保在启动期间测试该 Activity 的可用性。请确保测试导航行为是否有可能与用户的预期行为冲突。 + + +

    + + +

    定义启动模式

    + +

    启动模式允许您定义 Activity 的新实例如何与当前任务关联。 +您可以通过两种方法定义不同的启动模式:

    +
      +
    • 使用清单文件 +

      在清单文件中声明 Activity 时,您可以指定 Activity 在启动时应该如何与任务关联。 +

    • +
    • 使用 Intent 标志 +

      调用 {@link android.app.Activity#startActivity startActivity()} +时,可以在 {@link android.content.Intent} +中加入一个标志,用于声明新 Activity 如何(或是否)与当前任务关联。

    • +
    + +

    因此,如果 Activity +A 启动 Activity B,则 Activity B 可以在其清单文件中定义它应该如何与当前任务关联(如果可能),并且 Activity A 还可以请求 Activity B +应该如何与当前任务关联。如果这两个 Activity 均定义 Activity B +应该如何与任务关联,则 Activity A 的请求(如 Intent 中所定义)优先级要高于 Activity +B 的请求(如其清单文件中所定义)。

    + +

    注:某些适用于清单文件的启动 +模式不可用作 Intent 标志,同样,某些可用作 Intent +标志的启动模式无法在清单文件中定义。

    + + +

    使用清单文件

    + +

    在清单文件中声明 Activity 时,您可以使用 {@code <activity>} +元素的 {@code +launchMode} +属性指定 Activity 应该如何与任务关联。

    + +

    {@code +launchMode} +属性指定有关应如何将 Activity 启动到任务中的指令。您可以分配给 +launchMode +属性的启动模式共有四种:

    + +
    +
    {@code "standard"}(默认模式)
    +
    默认。系统在启动 Activity 的任务中创建 Activity 的新实例并向其传送 + Intent。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例。 +
    +
    {@code "singleTop"}
    +
    如果当前任务的顶部已存在 Activity 的一个实例,则系统会通过调用该实例的 +{@link +android.app.Activity#onNewIntent onNewIntent()} +方法向其传送 Intent,而不是创建 Activity 的新实例。Activity 可以多次实例化,而每个实例均可属于不同的任务,并且一个任务可以拥有多个实例(但前提是位于返回栈顶部的 Activity 并不是 Activity 的现有实例)。 + + +

    例如,假设任务的返回栈包含根 Activity A 以及 Activity B、C +和位于顶部的 D(堆栈是 A-B-C-D;D 位于顶部)。收到针对 D 类 Activity 的 Intent。如果 D +具有默认的 {@code "standard"} 启动模式,则会启动该类的新实例,且堆栈会变成 A-B-C-D-D。但是,如果 D 的启动模式是 {@code "singleTop"},则 D +的现有实例会通过 {@link +android.app.Activity#onNewIntent onNewIntent()} 接收 Intent,因为它位于堆栈的顶部;而堆栈仍为 +A-B-C-D。但是,如果收到针对 A 类 Activity 的 Intent,则会向堆栈添加 B 的新实例,即便其启动模式为 {@code "singleTop"} 也是如此。 + +

    +

    注:为某个 Activity 创建新实例时,用户可以按“返回”按钮返回到前一个 Activity。 +但是,当 Activity 的现有实例处理新 + Intent 时,则在新 Intent 到达 +{@link android.app.Activity#onNewIntent +onNewIntent()} +之前,用户无法按“返回”按钮返回到 Activity 的状态。 +

    +
    + +
    {@code "singleTask"}
    +
    系统创建新任务并实例化位于新任务底部的 Activity。但是,如果该 Activity 的一个实例已存在于一个单独的任务中,则系统会通过调用现有实例的 {@link +android.app.Activity#onNewIntent +onNewIntent()} 方法向其传送 + Intent,而不是创建新实例。一次只能存在 Activity 的一个实例。 + +

    注:尽管 Activity 在新任务中启动,但是用户按“返回”按钮仍会返回到前一个 Activity。 +

    +
    {@code "singleInstance"}。
    +
    与 +{@code "singleTask"} 相同,只是系统不会将任何其他 Activity 启动到包含实例的任务中。该 Activity 始终是其任务唯一仅有的成员;由此 Activity 启动的任何 Activity 均在单独的任务中打开。 +
    +
    + + +

    我们再来看另一示例,Android 浏览器 +应用声明 Web 浏览器 Activity 应始终在其自己的任务中打开(通过在 {@code <activity>} 元素中指定 {@code singleTask} +启动模式)。这意味着,如果您的应用发出打开 +Android 浏览器的 + Intent,则其 Activity 与您的应用位于不同的任务中。相反,系统会为浏览器启动新任务,或者如果浏览器 +已有任务正在后台运行,则会将该任务上移一层以处理新 + Intent。

    + +

    无论 Activity 是在新任务中启动,还是在与启动 Activity 相同的任务中启动,用户按“返回”按钮始终会转到前一个 Activity。 +但是,如果启动指定 +{@code singleTask} +启动模式的 Activity,则当某后台任务中存在该 Activity 的实例时,整个任务都会转移到前台。此时,返回栈包括上移到堆栈顶部的任务中的所有 Activity。 + +图 4 显示了这种情况。

    + + +

    图 4. 显示如何将启动模式为“singleTask”的 Activity 添加到返回栈。 +如果 Activity 已经是某个拥有自己的返回栈的后台任务的一部分,则整个返回栈也会上移到当前任务的顶部。 + +

    + +

    如需了解有关在清单文件中使用启动模式的详细信息,请参阅 +<activity> +元素文档,其中更详细地讨论了 {@code launchMode} +属性和可接受的值。

    + +

    注:使用 {@code launchMode} +属性为 Activity 指定的行为可由 Intent +附带的 Activity 启动标志替代,下文将对此进行讨论。

    + + + +

    使用 Intent 标志

    + +

    启动 Activity 时,您可以通过在传递给 {@link +android.app.Activity#startActivity startActivity()} 的 Intent +中加入相应的标志,修改 Activity 与其任务的默认关联方式。可用于修改默认行为的标志包括: +

    + +

    +

    {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
    +
    在新任务中启动 Activity。如果已为正在启动的 Activity 运行任务,则该任务会转到前台并恢复其最后状态,同时 Activity 会在 +{@link android.app.Activity#onNewIntent onNewIntent()} 中收到新 + Intent。 +

    正如前文所述,这会产生与 {@code "singleTask"} {@code launchMode} +值相同的行为。

    +
    {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
    +
    如果正在启动的 Activity 是当前 Activity(位于返回栈的顶部),则 +现有实例会接收对 {@link android.app.Activity#onNewIntent onNewIntent()} +的调用,而不是创建 Activity 的新实例。 +

    正如前文所述,这会产生与 {@code "singleTop"} {@code launchMode} +值相同的行为。

    +
    {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
    +
    如果正在启动的 Activity 已在当前任务中运行,则会销毁当前任务顶部的所有 Activity,并通过 {@link android.app.Activity#onNewIntent onNewIntent()} +将此 Intent 传递给 Activity 已恢复的实例(现在位于顶部),而不是启动该 Activity 的新实例。 + + +

    产生这种行为的 {@code launchMode} +属性没有值。

    +

    {@code FLAG_ACTIVITY_CLEAR_TOP} 通常与 +{@code FLAG_ACTIVITY_NEW_TASK} +结合使用。一起使用时,通过这些标志,可以找到其他任务中的现有 Activity,并将其放入可从中响应 Intent +的位置。

    +

    注:如果指定 Activity 的启动模式为 +{@code "standard"},则该 Activity 也会从堆栈中删除,并在其位置启动一个新实例,以便处理传入的 Intent。 + +这是因为当启动模式为 {@code "standard"} +时,将始终为新 Intent 创建新实例。

    +
    + + + + + + +

    处理关联

    + +

    “关联”指示 Activity 优先属于哪个任务。默认情况下,同一应用中的所有 Activity 彼此关联。 +因此,默认情况下,同一应用中的所有 Activity 优先位于相同任务中。 +不过,您可以修改 Activity 的默认关联。 +在不同应用中定义的 Activity 可以共享关联,或者可为在同一应用中定义的 Activity 分配不同的任务关联。 + +

    + +

    可以使用 {@code <activity>} +元素的 +{@code taskAffinity} 属性修改任何给定 Activity 的关联。

    + +

    {@code taskAffinity} +属性取字符串值,该值必须不同于 +在 +{@code <manifest>} +元素中声明的默认软件包名称,因为系统使用该名称标识应用的默认任务关联。 +

    + +

    在两种情况下,关联会起作用:

    +
      +
    • 启动 Activity 的 Intent 包含 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +标志。 + +

      默认情况下,新 Activity 会启动到调用 +{@link android.app.Activity#startActivity startActivity()} 的 Activity 任务中。它将推入与调用方相同的返回栈。 +但是,如果传递给 +{@link android.app.Activity#startActivity startActivity()} +的 Intent 包含 {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +标志,则系统会寻找其他任务来储存新 Activity。这通常是新任务,但未做强制要求。 +如果现有任务与新 Activity 具有相同关联,则会将 Activity 启动到该任务中。 +否则,将开始新任务。

      + +

      如果此标志导致 Activity 开始新任务,且用户按“主页”按钮离开,则必须为用户提供导航回任务的方式。 + +有些实体(如通知管理器)始终在外部任务中启动 Activity,而从不作为其自身的一部分启动 Activity,因此它们始终将 +{@code FLAG_ACTIVITY_NEW_TASK} 放入传递给 +{@link android.app.Activity#startActivity startActivity()} +的 Intent 中。请注意,如果 Activity 能够由可以使用此标志的外部实体调用,则用户可以通过独立方式返回到启动的任务,例如,使用启动器图标(任务的根 Activity 具有 +{@link android.content.Intent#CATEGORY_LAUNCHER} + Intent 过滤器;请参阅下面的启动任务部分)。 + +

      +
    • + +
    • Activity 将其 +{@code allowTaskReparenting} 属性设置为 {@code "true"}。 +

      在这种情况下,Activity 可以从其启动的任务移动到与其具有关联的任务(如果该任务出现在前台)。 +

      +

      例如,假设将报告所选城市天气状况的 Activity 定义为旅行应用的一部分。 +它与同一应用中的其他 Activity 具有相同的关联(默认应用关联),并允许利用此属性重定父级。当您的一个 Activity 启动天气预报 Activity 时,它最初所属的任务与您的 Activity 相同。 + + +但是,当旅行应用的任务出现在前台时,系统会将天气预报 Activity 重新分配给该任务并显示在其中。 +

      +
    • +
    + +

    提示:如果从用户的角度来看,一个 {@code .apk} +文件包含多个“应用”,则您可能需要使用 {@code taskAffinity} +属性将不同关联分配给与每个“应用”相关的 Activity。

    + + + +

    清理返回栈

    + +

    如果用户长时间离开任务,则系统会清除所有 Activity 的任务,根任务除外。 +当用户再次返回到任务时,仅恢复根 Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。 + +

    + +

    您可以使用下列几个 Activity 属性修改此行为:

    + +
    +
    alwaysRetainTaskState +
    +
    如果在任务的根 Activity 中将此属性设置为 {@code "true"},则不会发生刚才所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中。 + +
    + +
    clearTaskOnLaunch
    +
    如果在任务的根 Activity 中将此属性设置为 {@code "true"},则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 + +换而言之,它与 +{@code alwaysRetainTaskState} 正好相反。 +即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。 +
    + +
    finishOnTaskLaunch +
    +
    此属性类似于 +{@code clearTaskOnLaunch},但它对单个 Activity 起作用,而非整个任务。 +此外,它还有可能会导致任何 Activity 停止,包括根 Activity。 +设置为 {@code "true"} +时,Activity 仍是任务的一部分,但是仅限于当前会话。如果用户离开然后返回任务,则任务将不复存在。 +
    +
    + + + + +

    启动任务

    + +

    通过为 Activity 提供一个以 {@code "android.intent.action.MAIN"} +为指定操作、以{@code "android.intent.category.LAUNCHER"} +为指定类别的 Intent 过滤器,您可以将活动设置为任务的入口点。 +例如:

    + +
    +<activity ... >
    +    <intent-filter ... >
    +        <action android:name="android.intent.action.MAIN" />
    +        <category android:name="android.intent.category.LAUNCHER" />
    +    </intent-filter>
    +    ...
    +</activity>
    +
    + +

    此类 Intent 过滤器会使 Activity 的图标和标签显示在应用启动器中,让用户能够启动 Activity 并在启动之后随时返回到创建的任务中。 + + +

    + +

    第二个功能非常重要:用户必须能够在离开任务后,再使用此 Activity 启动器返回该任务。 +因此,只有在 Activity 具有 +{@link android.content.Intent#ACTION_MAIN} +和 {@link android.content.Intent#CATEGORY_LAUNCHER} +过滤器时,才应该使用将 Activity 标记为“始终启动任务”的两种启动模式,即 {@code "singleTask"} 和 +{@code "singleInstance"}。例如,我们可以想像一下如果缺少过滤器会发生什么情况: + Intent 启动一个 {@code "singleTask"} +Activity,从而启动一个新任务,并且用户花了些时间处理该任务。然后,用户按主页按钮。 +任务现已发送到后台,而且不可见。现在,用户无法返回到任务,因为该任务未显示在应用启动器中。 +

    + +

    如果您并不想用户能够返回到 Activity,对于这些情况,请将 +<activity> +元素的 +{@code finishOnTaskLaunch} +设置为 {@code "true"}(请参阅清理堆栈)。

    + +

    有关如何在概览屏幕中显示和管理任务与 Activity 的更多信息,请参阅概览屏幕。 + +

    + + diff --git a/docs/html-intl/intl/zh-cn/guide/index.jd b/docs/html-intl/intl/zh-cn/guide/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..01ab3478a0f602e5a7c86933a826a567fea6e9cc --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Android 简介 + +@jd:body + + + + +

    Android +提供了一个内容丰富的应用框架,支持您在 Java 语言环境中为移动设备开发创新应用和游戏。在左侧导航窗格列出的文档中,提供了有关如何使用各种 +Android API 开发应用的详细信息。

    + +

    如果您是 Android 应用开发新手,则需了解以下有关 +Android 应用框架的基本概念,这一点至关重要:

    + + +
    + +
    + +

    应用提供多个入口点

    + +

    Android +应用都是将各种可单独调用的不同组件加以组合构建而成。例如,组件可以是为用户界面提供一个屏幕的单个“Activity”,也可以是在后台独立执行工作的“服务”。 + +

    + +

    您可以使用 Intent 从一个组件启动另一个组件。甚至,您还可以启动不同应用中的组件,例如,启动地图应用中的 Activity 以显示地址。 +此模式可为单个应用提供多个入口点,并使任何应用均能够像用户“默认设置”一样处理其他应用可能调用的操作。 + +

    + + +

    了解详情

    + + +
    + + +
    + +

    应用可适应不同的设备

    + +

    Android +提供了一个自适应应用框架,您可以利用它为不同的设备配置提供独特的资源。例如,您可以针对不同的屏幕尺寸创建不同的 +XML +布局文件,系统将根据当前设备的屏幕尺寸确定要应用的布局。

    + +

    如有任何应用功能需要相机等特定的硬件,则可在运行时查询设备功能的可用性。 +如有必要,您还可以声明您的应用所必需的功能,使 +Google Play +商店等应用市场不得在不支持这些功能的设备上安装您的应用。

    + + +

    了解详情

    + + +
    + +
    + + + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/zh-cn/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 0000000000000000000000000000000000000000..c7ade4f392dbfd7679384f08471097a25e0b73f7 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=应用清单文件 +@jd:body + + + +

    + 每个应用的根目录中都必须包含一个 +AndroidManifest.xml 文件(且文件名精确无误)。 清单文件为 +Android 系统提供有关您的应用的基本信息,系统必须获得这些信息才能运行任意应用代码。 + + 此外,清单文件还可执行以下操作: +

    + +
      +
    • 为应用的 Java +软件包命名。软件包名称充当应用的唯一标识符
    • + +
    • 描述应用的各个组件,即:构成应用的 Activity、服务、广播接收器和内容提供程序。 + +为实现每个组件的类命名并发布其功能(例如,它们可以处理的 +{@link android.content.Intent +Intent} 消息)。根据这些声明,Android +系统可以了解这组件具体是什么,以及在什么条件下可以启动它们
    • + +
    • 确定将托管应用组件的进程
    • + +
    • 声明应用必须具备哪些权限才能访问 +API 中受保护的部分并与其他应用交互
    • + +
    • 还声明其他应用与该应用组件交互所需具备的权限 +
    • + +
    • 列出 {@link android.app.Instrumentation} +类,这些类可在应用运行期间提供分析和其他信息。这些声明只会在应用处在开发和测试阶段时出现在清单文件中;它们会在应用发布之前被删除 + +
    • + +
    • 声明应用所需的最低 Android API +级别
    • + +
    • 列出应用必须链接到的库
    • +
    + + +

    清单文件结构

    + +

    +下图显示了清单文件的通用结构及其可包含的每个元素。 +每个元素及其所有属性全部记录在一个单独的文件中。 +要查看有关任何元素的详细信息,请点击该图中或其后按字母顺序排列的元素列表中相应的元素名称,或者点击任何其他地方提到的相应元素名称。 + + + +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +
    +<manifest>
    +
    +    <uses-permission />
    +    <permission />
    +    <permission-tree />
    +    <permission-group />
    +    <instrumentation />
    +    <uses-sdk />
    +    <uses-configuration />  
    +    <uses-feature />  
    +    <supports-screens />  
    +    <compatible-screens />  
    +    <supports-gl-texture />  
    +
    +    <application>
    +
    +        <activity>
    +            <intent-filter>
    +                <action />
    +                <category />
    +                <data />
    +            </intent-filter>
    +            <meta-data />
    +        </activity>
    +
    +        <activity-alias>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data />
    +        </activity-alias>
    +
    +        <service>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data/>
    +        </service>
    +
    +        <receiver>
    +            <intent-filter> . . . </intent-filter>
    +            <meta-data />
    +        </receiver>
    +
    +        <provider>
    +            <grant-uri-permission />
    +            <meta-data />
    +            <path-permission />
    +        </provider>
    +
    +        <uses-library />
    +
    +    </application>
    +
    +</manifest>
    +
    + +

    +可出现在清单文件中的所有元素按字母顺序罗列如下。 +这些是仅有的合法元素;您无法添加自己的元素或属性。 + +

    + +

    +<action> +
    <activity> +
    <activity-alias> +
    <application> +
    <category> +
    <data> +
    <grant-uri-permission> +
    <instrumentation> +
    <intent-filter> +
    <manifest> +
    <meta-data> +
    <permission> +
    <permission-group> +
    <permission-tree> +
    <provider> +
    <receiver> +
    <service> +
    <supports-screens> +
    <uses-configuration> +
    <uses-feature> +
    <uses-library> +
    <uses-permission> +
    <uses-sdk> +

    + + + + +

    文件约定

    + +

    +有些约定和规则普遍适用于清单文件中的所有元素和属性: + +

    + +
    +
    元素
    +
    只有 +<manifest> 和 +<application> +元素是必需的,它们都必须存在并且只能出现一次。其他大部分元素可以出现多次或者根本不出现,即便清单文件中必须至少存在其中某些元素才能完成任何有意义的操作也是如此。 + + + + +

    +如果一个元素包含某些内容,也就包含其他元素。所有值均通过属性进行设置,而不是通过元素内的字符数据设置。 + +

    + +

    +同一级别的元素通常不分先后顺序。例如,<activity><provider> +和 +<service> +元素可以按任何顺序混合在一起。 +(<activity-alias> +元素不适用此规则: +它必须跟在别名所指的 +<activity> +之后。) +

    + +
    属性
    +
    从某种意义上说,所有属性都是可选的。但是,有些属性必须指定给元素以实现其目的。 +请使用本文档作为参考。 +对于真正可选的属性,它将指定默认值或声明缺乏规范时将执行何种操作。 + + +

    除了根 +<manifest> +元素的一些属性外,所有属性名称均以 {@code android:} +前缀开头,例如,{@code android:alwaysRetainTaskState}。由于该前缀是通用的,因此在按名称引用属性时,本文档通常会将其忽略。 + +

    + +
    声明类名
    +
    许多元素对应于 +Java +对象,其中包括应用本身的元素(<application> +元素)及其主要组件 +— +Activity +(<activity>)、服务 +(<service>)、广播接收器 +(<receiver>) +以及内容提供程序 +(<provider>)。 + +

    +如果按照您针对组件类({@link android.app.Activity}、{@link android.app.Service}、{@link android.content.BroadcastReceiver} +和 +{@link android.content.ContentProvider})几乎一直采用的方式来定义子类,则该子类需通过 {@code name} 属性来声明。 +该名称必须包含完整的软件包名称。 +例如,{@link android.app.Service} +子类可能会声明如下: +

    + +
    <manifest . . . >
    +    <application . . . >
    +        <service android:name="com.example.project.SecretService" . . . >
    +            . . .
    +        </service>
    +        . . .
    +    </application>
    +</manifest>
    + +

    +但是,为了简便起见,如果字符串的第一个字符是句点,则字符串将追加到应用的软件包名称(正如 +<manifest> +元素的 +package +属性中所指定)。 +以下赋值与上述方法相同: +

    + +
    <manifest package="com.example.project" . . . >
    +    <application . . . >
    +        <service android:name=".SecretService" . . . >
    +            . . .
    +        </service>
    +        . . .
    +    </application>
    +</manifest>
    + +

    +当启动组件时,Android 会创建已命名子类的实例。如果未指定子类,则会创建基类的实例。 + +

    + +
    多个值
    +
    如果可以指定多个值,则几乎总是在重复此元素,而不是列出单个元素内的多个值。 +例如,Intent 过滤器可以列出多个操作: + + +
    <intent-filter . . . >
    +    <action android:name="android.intent.action.EDIT" />
    +    <action android:name="android.intent.action.INSERT" />
    +    <action android:name="android.intent.action.DELETE" />
    +    . . .
    +</intent-filter>
    + +
    资源值
    +
    某些属性的值可以显示给用户,例如,Activity 的标签和图标。 +这些属性的值应该本地化,因此需要通过资源或主题进行设置。 +资源值用以下格式表示: +

    + +

    {@code @[package:]type:name}

    + +

    +其中,如果资源与应用位于同一软件包中,则可忽略 package 名称; + type 是资源类型,如“字串符”或“图片”; + name 是标识特定资源的名称。例如: + +

    + +
    <activity android:icon="@drawable/smallPic" . . . >
    + +

    +主题中的值用类似的方法表示,但是以“{@code ?}”开头而不是“{@code @}”: + +

    + +

    {@code ?[package:]type:name} +

    + +
    字串符值
    +
    如果属性值为字串符,则必须使用双反斜杠(“{@code \\}”) +转义字符。例如,使用“{@code \\n}”表示换行符或使用“{@code \\uxxxx}”表示 +Unicode 字符)。
    +
    + + +

    文件功能

    + +

    +下文介绍如何在清单文件中利用某些 +Android 特性。 +

    + + +

    Intent 过滤器

    + +

    +应用的核心组件(其 Activity、服务和广播接收器)由 + Intent 激活。Intent 是一系列用于描述所需操作的信息({@link android.content.Intent} +对象),其中包括要执行操作的数据、应执行操作的组件类别以及其他相关说明。 + +Android +会找到合适的组件来响应 + Intent,根据需要启动组件的新实例,并将其传递到 + Intent 对象。 +

    + +

    +组件将通过“Intent 过滤器”公布其功能,即它们可响应的 Intent 类型。 + 由于 +Android 系统在启动某组件之前必须了解该组件可以处理哪些 Intent,因此 Intent 过滤器在清单文件中被指定为 +<intent-filter> +元素。 +一个组件可能有任意数量的过滤器,其中每个过滤器描述一种不同的功能。 + +

    + +

    +显式命名目标组件的 + Intent 将激活该组件;过滤器不起作用。但是,不按名称指定目标的 + Intent +只有在能够通过组件的一个过滤器时才可激活该组件。 +

    + +

    +有关如何根据 + Intent 过滤器测试 + Intent 对象的信息,请参阅单独的文档 + Intent 和 Intent 过滤器。 +

    + + +

    图标和标签

    + +

    +对于可以显示给用户的小图标和文本标签,大量元素具有 {@code icon} 和 {@code label} +属性。此外,对于同样可以显示在屏幕上的较长说明文本,某些元素还具有 +{@code description} +属性。例如,<permission> +元素具有所有这三个属性。因此,当系统询问用户是否授权给请求获得权限的应用时,权限图标、权限名称以及所需信息的说明均可呈现给用户。 + + + + +

    + +

    +无论何种情况下,在包含元素中设置的图标和标签都将成为所有容器子元素的默认 +{@code icon} 和 {@code label} 设置。因此,在 +<application> +元素中设置的图标和标签是每个应用组件的默认图标和标签。 +同样,为组件(例如,<activity> +元素)设置的图标和标签是组件每个 +<intent-filter> +元素的默认设置。 + +如果 <application> 元素设置标签,但是 Activity 及其 + Intent 过滤器不执行此操作,则应用标签将被视为 Activity 和 + Intent 过滤器的标签。 + + +

    + +

    +在实现 Intent 过滤器公布的功能时,只要向用户呈现组件,系统便会使用为过滤器设置的图标和标签表示该组件。 + +例如,具有“{@code android.intent.action.MAIN}”和“{@code android.intent.category.LAUNCHER}”设置的过滤器将 Activity 公布为可启动应用的功能,即,公布为应显示在应用启动器中的功能。 + + + +因此,在过滤器中设置的图标和标签就是显示在启动器中的图标和标签。 + +

    + + +

    权限

    + +

    + 权限 是一种限制,用于限制对部分代码或设备上数据的访问。 + 施加限制是为了保护可能被误用以致破坏或损害用户体验的关键数据和代码。 + +

    + +

    +每种权限均由一个唯一的标签标识。标签通常指示受限制的操作。 +例如,以下是由 +Android 定义的一些权限: +

    + +

    {@code android.permission.CALL_EMERGENCY_NUMBERS} +
    {@code android.permission.READ_OWNER_DATA} +
    {@code android.permission.SET_WALLPAPER} +
    {@code android.permission.DEVICE_POWER}

    + +

    +一个功能最多只能由一种权限保护。 +

    + +

    +如果应用需要访问受权限保护的功能,则必须在清单文件中使用 +<uses-permission> +元素声明应用需要该权限。 +但是,将应用安装到设备上之后,安装程序会通过检查签署应用证书的颁发机构并(在某些情况下)询问用户,确定是否授予请求的权限。 + + +如果授予权限,则应用能够使用受保护的功能。 + +否则,其访问这些功能的尝试将会失败,并且不会向用户发送任何通知。 + +

    + +

    +此外,应用也可以使用权限保护自己的组件(Activity、服务、广播接收器和内容提供程序)。 +它可以采用由 +Android +定义(如 +{@link android.Manifest.permission android.Manifest.permission} 中所列)或由其他应用声明的任何权限。或者,它也可以定义自己的权限。新权限用 +<permission> +元素来声明。 +例如,Activity 可受到如下保护: +

    + +
    +<manifest . . . >
    +    <permission android:name="com.example.project.DEBIT_ACCT" . . . />
    +    <uses-permission android:name="com.example.project.DEBIT_ACCT" />
    +    . . .
    +    <application . . .>
    +        <activity android:name="com.example.project.FreneticActivity"
    +                  android:permission="com.example.project.DEBIT_ACCT"
    +                  . . . >
    +            . . .
    +        </activity>
    +    </application>
    +</manifest>
    +
    + +

    +请注意,在此示例中,{@code DEBIT_ACCT} +权限不仅是通过 +<permission> +元素来声明,而且其使用也是通过 +<uses-permission> +元素来请求。要让应用的其他组件也能够启动受保护的 Activity,就必须请求其使用权限,即便保护是由应用本身施加的亦如此。 + + +

    + +

    +同样还是在此示例中,如果将 +{@code permission} +属性设置为在其他位置(例如,{@code android.permission.CALL_EMERGENCY_NUMBERS})声明的权限,则无需使用 +<permission> +元素再次声明。 +但是,仍有必要通过 +<uses-permission> 请求使用它。 +

    + +

    +<permission-tree> +元素为一组将在代码中定义的权限声明命名空间。 + +同时, +<permission-group> +为一组权限(包括在清单文件中使用 +<permission> +元素声明的权限以及在其他位置声明的权限)定义标签。它只影响如何对提供给用户的权限进行分组。 +<permission-group> +元素并不指定哪些权限属于该组,而只是为组提供名称。 + +通过向 +<permission> +元素的 +permissionGroup +属性分配组名,将权限放入组中。 + +

    + + +

    + +

    +每个应用均链接到默认的 Android 库,该库中包括构建应用(以及通用类,如 Activity、服务、 Intent 、视图、按钮、应用、ContentProvider 等)的基本软件包。 + + + +

    + +

    +但是,某些软件包驻留在自己的库中。如果应用使用来自其中任一软件包的代码,则必须明确要求其链接到这些软件包。 + +清单文件必须包含单独的 +<uses-library> +元素来命名其中每个库。(库名称可在软件包的文档中找到。) + +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/calendar-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..59682843897da6c49f4ffb8cc3c6d5c1ff5904cf --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=日历提供程序 +@jd:body + +
    +
    +

    本文内容

    +
      +
    1. 基础知识
    2. +
    3. 用户权限
    4. +
    5. 日历表 +
        +
      1. 查询日历
      2. +
      3. 修改日历
      4. +
      5. 插入日历
      6. +
      +
    6. +
    7. 事件表 +
        +
      1. 添加事件
      2. +
      3. 更新事件
      4. +
      5. 删除事件
      6. +
      +
    8. +
    9. 参加者表 +
        +
      1. 添加参加者
      2. +
      +
    10. +
    11. 提醒表 +
        +
      1. 添加提醒
      2. +
      +
    12. +
    13. 实例表 +
        +
      1. 查询实例表
      2. +
    14. +
    15. 日历 Intent 对象 +
        +
      1. 使用 Intent 对象插入事件
      2. +
      3. 使用 Intent 对象编辑事件
      4. +
      5. 使用 Intent 对象查看日历数据
      6. +
      +
    16. + +
    17. 同步适配器
    18. +
    + +

    关键类

    +
      +
    1. {@link android.provider.CalendarContract.Calendars}
    2. +
    3. {@link android.provider.CalendarContract.Events}
    4. +
    5. {@link android.provider.CalendarContract.Attendees}
    6. +
    7. {@link android.provider.CalendarContract.Reminders}
    8. +
    +
    +
    + +

    日历提供程序是用户日历事件的存储库。您可以利用 +Calendar Provider API +对日历、事件、参加者、提醒等执行查询、插入、更新和删除操作。

    + + +

    Calender Provider API 可供应用和同步适配器使用。规则因进行调用的程序类型而异。 +本文主要侧重于介绍使用 +Calendar Provider API 作为应用的情况。如需了解对各类同步适配器差异的阐述,请参阅同步适配器。 + +

    + + +

    正常情况下,要想读取或写入日历数据,应用的清单文件必须包括用户权限中所述的适当权限。 + +为简化常见操作的执行,日历提供程序提供了一组 Intent 对象,日历 Intent 对象中对这些 Intent 做了说明。 + +这些 Intent 对象会将用户转到日历应用,执行插入事件、查看事件和编辑事件操作。 +用户与日历应用交互,然后返回原来的应用。 +因此,您的应用不需要请求权限,也不需要提供用于查看事件或创建事件的用户界面。 +

    + +

    基础知识

    + +

    内容提供程序存储数据并使其可供应用访问。 +Android +平台提供的内容提供程序(包括日历提供程序)通常以一组基于关系数据库模型的表格形式公开数据,在这个表格中,每一行都是一条记录,每一列都是特定类型和含义的数据。 +应用和同步适配器可以通过 +Calendar Provider API +获得对储存用户日历数据的数据库表的读取/写入权限。

    + +

    每一个内容提供程序都会公开一个对其数据集进行唯一标识的公共 +URI(包装成一个 {@link android.net.Uri} 对象)。 +控制多个数据集(多个表)的内容提供程序会为每个数据集公开单独的 URI。 +所有提供程序 URI 都以字符串“content://”开头。 +这表示数据受内容提供程序的控制。 +日历提供程序会为其每个类(表)定义 +URI 常量。这些 +URI 的格式为 <class>.CONTENT_URI。例如,{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}。 +

    + +

    图 1 是对日历提供程序数据模型的图形化表示。它显示了将彼此链接在一起的主要表和字段。 +

    + +Calendar Provider Data Model +

    图 1. 日历提供程序数据模型。

    + +

    用户可以有多个日历,可将不同类型的日历与不同类型的帐户(Google 日历、Exchange 等)关联。

    + +

    {@link android.provider.CalendarContract} 定义了日历和事件相关信息的数据模型。这些数据存储在以下所列的若干表中。

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    表(类)描述

    {@link android.provider.CalendarContract.Calendars}

    此表储存日历特定信息。 +此表中的每一行都包含一个日历的详细信息,例如名称、颜色、同步信息等。 +
    {@link android.provider.CalendarContract.Events}此表储存事件特定信息。 +此表中的每一行都包含一个事件的信息—例如事件名称、地点、开始时间、结束时间等。 + +事件可一次性发生,也可多次重复发生。参加者、提醒和扩展属性存储在单独的表内。 +它们各自具有一个 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID},用于引用 Events 表中的 {@link android.provider.BaseColumns#_ID}。 + +
    {@link android.provider.CalendarContract.Instances}此表储存每个事件实例的开始时间和结束时间。 +此表中的每一行都表示一个事件实例。 +对于一次性事件,实例与事件为 1:1 +映射。对于重复事件,会自动生成多个行,分别对应多个事件实例。 +
    {@link android.provider.CalendarContract.Attendees}此表储存事件参加者(来宾)信息。 +每一行都表示事件的一位来宾。 +它指定来宾的类型以及事件的来宾出席响应。 +
    {@link android.provider.CalendarContract.Reminders}此表储存提醒/通知数据。 +每一行都表示事件的一个提醒。一个事件可以有多个提醒。 +每个事件的最大提醒数量在 + + +{@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS} +中指定,后者由拥有给定日历的同步适配器设置。提醒以事件发生前的分钟数形式指定,其具有一个可决定用户提醒方式的方法。 +
    + +

    Calendar Provider API 以灵活、强大为设计宗旨。提供良好的最终用户体验以及保护日历及其数据的完整性也同样重要。 + +因此,请在使用该 API +时牢记以下要点:

    + +
      + +
    • 插入、更新和查看日历事件。要想直接从日历提供程序插入事件、修改事件以及读取事件,您需要具备相应权限。不过,如果您开发的并不是完备的日历应用或同步适配器,则无需请求这些权限。您可以改用 Android 的日历应用支持的 Intent 对象将读取操作和写入操作转到该应用执行。当您使用 Intent 对象时,您的应用会将用户转到日历应用,在一个预填充表单中执行所需操作。 +完成操作后,用户将返回您的应用。通过将您的应用设计为通过日历执行常见操作,可以为用户提供一致、可靠的用户界面。 + +这是推荐您采用的方法。 +如需了解详细信息,请参阅日历 Intent 对象。 +

      + + +
    • 同步适配器。同步适配器用于将用户设备上的日历数据与其他服务器或数据源同步。 +在 +{@link android.provider.CalendarContract.Calendars} 和 + +{@link android.provider.CalendarContract.Events} +表中,预留了一些供同步适配器使用的列。提供程序和应用不应修改它们。实际上,除非以同步适配器形式进行访问,否则它们处于不可见状态。 +如需了解有关同步适配器的详细信息,请参阅同步适配器。 +
    • + +
    + + +

    用户权限

    + +

    如需读取日历数据,应用必须在其清单文件中加入 {@link +android.Manifest.permission#READ_CALENDAR} 权限。文件中必须包括用于删除、插入或更新日历数据的 +{@link android.Manifest.permission#WRITE_CALENDAR} +权限:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    +    <uses-sdk android:minSdkVersion="14" />
    +    <uses-permission android:name="android.permission.READ_CALENDAR" />
    +    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    +    ...
    +</manifest>
    +
    + + +

    日历表

    + +

    {@link android.provider.CalendarContract.Calendars} 表包含各日历的详细信息。 +应用和同步适配器均可写入下列日历列。 +如需查看所支持字段的完整列表,请参阅 + +{@link android.provider.CalendarContract.Calendars} 参考资料。

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    常量描述
    {@link android.provider.CalendarContract.Calendars#NAME}日历的名称。
    {@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}该日历显示给用户时使用的名称。
    {@link android.provider.CalendarContract.Calendars#VISIBLE}表示是否选择显示该日历的布尔值。值为 0 表示不应显示与该日历关联的事件。 + +值为 1 +表示应该显示与该日历关联的事件。此值影响 {@link +android.provider.CalendarContract.Instances} 表中行的生成。
    {@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}一个布尔值,表示是否应同步日历并将其事件存储在设备上。 +值为 0 +表示不同步该日历,也不将其事件存储在设备上。值为 1 +表示同步该日历的事件,并将其事件存储在设备上。
    + +

    查询日历

    + +

    以下示例说明了如何获取特定用户拥有的日历。 +为了简便起见,在此示例中,查询操作显示在用户界面线程(“主线程”)中。 +实际上,此操作应该在一个异步线程而非主线程中完成。 +如需查看更详细的介绍,请参阅加载器。 +如果您的目的不只是读取数据,还要修改数据,请参阅 {@link android.content.AsyncQueryHandler}。 + +

    + + +
    +// Projection array. Creating indices for this array instead of doing
    +// dynamic lookups improves performance.
    +public static final String[] EVENT_PROJECTION = new String[] {
    +    Calendars._ID,                           // 0
    +    Calendars.ACCOUNT_NAME,                  // 1
    +    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    +    Calendars.OWNER_ACCOUNT                  // 3
    +};
    +  
    +// The indices for the projection array above.
    +private static final int PROJECTION_ID_INDEX = 0;
    +private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
    +private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
    +private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
    + + + + + +

    在示例的下一部分,您需要构建查询。选定范围指定查询的条件。 +在此示例中,查询寻找的是 +ACCOUNT_NAME +为“sampleuser@google.com”、ACCOUNT_TYPE +为“com.google”、OWNER_ACCOUNT +为“sampleuser@google.com”的日历。如果您想查看用户查看过的所有日历,而不只是用户拥有的日历,请省略 +OWNER_ACCOUNT。您可以利用查询返回的 +{@link android.database.Cursor} +对象遍历数据库查询返回的结果集。 +如需查看有关在内容提供程序中使用查询的详细介绍,请参阅内容提供程序。 +

    + + +
    // Run query
    +Cursor cur = null;
    +ContentResolver cr = getContentResolver();
    +Uri uri = Calendars.CONTENT_URI;   
    +String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" 
    +                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
    +                        + Calendars.OWNER_ACCOUNT + " = ?))";
    +String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
    +        "sampleuser@gmail.com"}; 
    +// Submit the query and get a Cursor object back. 
    +cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
    + +

    以下后续部分使用游标单步调试结果集。它使用在示例开头设置的常量来返回每个字段的值。 + +

    + +
    // Use the cursor to step through the returned records
    +while (cur.moveToNext()) {
    +    long calID = 0;
    +    String displayName = null;
    +    String accountName = null;
    +    String ownerName = null;
    +      
    +    // Get the field values
    +    calID = cur.getLong(PROJECTION_ID_INDEX);
    +    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    +    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    +    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
    +              
    +    // Do something with the values...
    +
    +   ...
    +}
    +
    + +

    修改日历

    + +

    如需执行日历更新,您可以通过 +URI 追加 +ID ({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 或第一个选定项形式提供日历的 + +{@link +android.provider.BaseColumns#_ID}。选定范围应以 +"_id=?" 开头,并且第一个 +selectionArg 应为日历的 {@link +android.provider.BaseColumns#_ID}。 +您还可以通过在 URI +中编码 ID 来执行更新。下例使用 ({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 方法更改日历的显示名称: + +

    + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long calID = 2;
    +ContentValues values = new ContentValues();
    +// The new display name for the calendar
    +values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
    +Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
    +int rows = getContentResolver().update(updateUri, values, null, null);
    +Log.i(DEBUG_TAG, "Rows updated: " + rows);
    + +

    插入日历

    + +

    日历设计为主要由同步适配器进行管理,因此您只应以同步适配器形式插入新日历。 +在大多数情况下,应用只能对日历进行一些表面更改,如更改显示名称。 +如果应用需要创建本地日历,可以利用 +{@link +android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} +的 +{@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE},通过以同步适配器形式执行日历插入来实现目的。{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} +是一种特殊的帐户类型,用于未关联设备帐户的日历。 +这种类型的日历不与服务器同步。如需了解对同步适配器的阐述,请参阅同步适配器。 +

    + +

    事件表

    + +

    {@link android.provider.CalendarContract.Events} +表包含各事件的详细信息。要想添加、更新或删除事件,应用必须在其清单文件中加入 +{@link android.Manifest.permission#WRITE_CALENDAR} +权限。

    + +

    应用和同步适配器均可写入下列事件列。 +如需查看所支持字段的完整列表,请参阅 {@link +android.provider.CalendarContract.Events} 参考资料。

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    常量描述
    {@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}事件所属日历的 {@link android.provider.BaseColumns#_ID}。
    {@link android.provider.CalendarContract.EventsColumns#ORGANIZER}事件组织者(所有者)的电子邮件。
    {@link android.provider.CalendarContract.EventsColumns#TITLE}事件的名称。
    {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}事件的发生地点。
    {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}事件的描述。
    {@link android.provider.CalendarContract.EventsColumns#DTSTART}事件开始时间,以从公元纪年开始计算的协调世界时毫秒数表示。
    {@link android.provider.CalendarContract.EventsColumns#DTEND}事件结束时间,以从公元纪年开始计算的协调世界时毫秒数表示。
    {@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}事件的时区。
    {@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}事件结束时间的时区。
    {@link android.provider.CalendarContract.EventsColumns#DURATION}RFC5545 +格式的事件持续时间。例如,值为 +"PT1H" 表示事件应持续一小时,值为 +"P2W" 表示持续 2 周。
    {@link android.provider.CalendarContract.EventsColumns#ALL_DAY}值为 1 +表示此事件占用一整天(按照本地时区的定义)。值为 0 +表示它是常规事件,可在一天内的任何时间开始和结束。
    {@link android.provider.CalendarContract.EventsColumns#RRULE}事件的重复发生规则格式。例如,"FREQ=WEEKLY;COUNT=10;WKST=SU"。 +您可以在此处找到更多示例。 +
    {@link android.provider.CalendarContract.EventsColumns#RDATE}事件的重复发生日期。 +{@link android.provider.CalendarContract.EventsColumns#RDATE} +与 {@link android.provider.CalendarContract.EventsColumns#RRULE} +通常联合用于定义一组聚合重复实例。 +如需查看更详细的介绍,请参阅 RFC5545 规范
    {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}将此事件视为忙碌时间还是可调度的空闲时间。 +
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}来宾是否可修改事件。
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}来宾是否可邀请其他来宾。
    {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}来宾是否可查看参加者列表。
    + +

    添加事件

    + +

    当您的应用插入新事件时,我们建议您按照使用 Intent 对象插入事件中所述使用 +{@link android.content.Intent#ACTION_INSERT INSERT} Intent 对象。不过,如需,也可直接插入事件。 +本节描述如何执行此操作。 +

    + + +

    以下是插入新事件的规则:

    +
      + +
    • 您必须加入 {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} 和 {@link +android.provider.CalendarContract.EventsColumns#DTSTART}。
    • + +
    • 您必须加入 {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}。如需获取系统中已安装时区 ID +的列表,请使用 {@link +java.util.TimeZone#getAvailableIDs()}。请注意,如果您按使用 Intent 对象插入事件中所述通过 +{@link +android.content.Intent#ACTION_INSERT INSERT} Intent 对象插入事件,则此规则不适用—在该情形下,系统会提供默认时区。 +
    • + +
    • 对于非重复事件,您必须加入 {@link +android.provider.CalendarContract.EventsColumns#DTEND}。
    • + + +
    • 对于重复事件,您必须加入 {@link +android.provider.CalendarContract.EventsColumns#DURATION} 以及 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 或 {@link +android.provider.CalendarContract.EventsColumns#RDATE}。请注意,如果您按使用 Intent 对象插入事件中所述通过 +{@link +android.content.Intent#ACTION_INSERT INSERT} Intent 对象插入事件,则此规则不适用—在该情形下,您可以将 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 与 {@link android.provider.CalendarContract.EventsColumns#DTSTART} 和 {@link android.provider.CalendarContract.EventsColumns#DTEND} +联用,日历应用会自动将其转换为持续时间。 +
    • + +
    + +

    以下是一个插入事件的示例。为了简便起见,此操作是在 UI +线程内执行的。实际上,应该在异步线程中完成插入和更新,以便将操作移入后台线程。 +如需了解详细信息,请参阅 +{@link android.content.AsyncQueryHandler}。

    + + +
    +long calID = 3;
    +long startMillis = 0; 
    +long endMillis = 0;     
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2012, 9, 14, 7, 30);
    +startMillis = beginTime.getTimeInMillis();
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2012, 9, 14, 8, 45);
    +endMillis = endTime.getTimeInMillis();
    +...
    +
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Events.DTSTART, startMillis);
    +values.put(Events.DTEND, endMillis);
    +values.put(Events.TITLE, "Jazzercise");
    +values.put(Events.DESCRIPTION, "Group workout");
    +values.put(Events.CALENDAR_ID, calID);
    +values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
    +Uri uri = cr.insert(Events.CONTENT_URI, values);
    +
    +// get the event ID that is the last element in the Uri
    +long eventID = Long.parseLong(uri.getLastPathSegment());
    +// 
    +// ... do something with event ID
    +//
    +//
    + +

    注:请注意以上示例如何在事件创建后捕获事件 +ID。这是获取事件 ID +的最简单方法。您经常需要使用事件 ID +来执行其他日历操作—例如,向事件添加参加者或提醒。

    + + +

    更新事件

    + +

    当您的应用想允许用户编辑事件时,我们建议您按照使用 Intent 对象编辑事件中所述使用 +{@link android.content.Intent#ACTION_EDIT EDIT} + Intent 对象。不过,如需,也可以直接编辑事件。 +如需执行事件更新,您可以通过 URI 追加 ID ({@link + +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) + +或第一个选定项形式提供事件的 _ID。 +选定范围应以 "_id=?" 开头,并且第一个selectionArg 应为事件的 +_ID。您还可以使用不含 +ID +的选定范围执行更新。以下是一个更新事件的示例。它使用 + {@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()} +方法更改事件的名称:

    + + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long eventID = 188;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +Uri updateUri = null;
    +// The new title for the event
    +values.put(Events.TITLE, "Kickboxing"); 
    +updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +int rows = getContentResolver().update(updateUri, values, null, null);
    +Log.i(DEBUG_TAG, "Rows updated: " + rows);  
    + +

    删除事件

    + +

    您可以通过将事件 {@link +android.provider.BaseColumns#_ID} 作为 URI 追加 ID +或通过使用标准选定范围来删除事件。如果您使用追加 +ID,则将无法同时使用选定范围。共有两个版本的删除:应用删除和同步适配器删除。应用删除将 +deleted 列设置为 1。此标志告知同步适配器该行已删除,并且应将此删除操作传播至服务器。 + +同步适配器删除会将事件连同其所有关联数据从数据库中删除。 +以下是一个应用通过事件 {@link android.provider.BaseColumns#_ID} +删除事件的示例:

    + + +
    private static final String DEBUG_TAG = "MyActivity";
    +...
    +long eventID = 201;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +Uri deleteUri = null;
    +deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +int rows = getContentResolver().delete(deleteUri, null, null);
    +Log.i(DEBUG_TAG, "Rows deleted: " + rows);  
    +
    + +

    参加者表

    + +

    {@link android.provider.CalendarContract.Attendees} +表的每一行都表示事件的一位参加者或来宾。调用 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} +会返回一个参加者列表,其中包含具有给定 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +的事件的参加者。 +此 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +必须匹配特定事件的 {@link +android.provider.BaseColumns#_ID}。

    + +

    下表列出了可写入的字段。 +插入新参加者时,您必须加入除 ATTENDEE_NAME 之外的所有字段。 + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    常量描述
    {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}事件的 ID。
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}参加者的姓名。
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}参加者的电子邮件地址。
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}

    参加者与事件的关系。下列值之一:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}
    • +
    +
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}

    参加者的类型。下列值之一:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}
    • +
    {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}

    参加者的出席状态。下列值之一:

    +
      +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}
    • +
    • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}
    • +
    + +

    添加参加者

    + +

    以下是一个为事件添加一位参加者的示例。请注意,{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} +是必填项: +

    + +
    +long eventID = 202;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Attendees.ATTENDEE_NAME, "Trevor");
    +values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
    +values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
    +values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
    +values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
    +values.put(Attendees.EVENT_ID, eventID);
    +Uri uri = cr.insert(Attendees.CONTENT_URI, values);
    +
    + +

    提醒表

    + +

    {@link android.provider.CalendarContract.Reminders} +表的每一行都表示事件的一个提醒。调用 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} +会返回一个提醒列表,其中包含具有给定 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 的事件的提醒。 +

    + + +

    下表列出了提醒的可写入字段。插入新提醒时,必须加入所有字段。 +请注意,同步适配器指定它们在 +{@link +android.provider.CalendarContract.Calendars} 表中支持的提醒类型。详情请参阅 +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS} +。

    + + + + + + + + + + + + + + + + + + + +
    常量描述
    {@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}事件的 ID。
    {@link android.provider.CalendarContract.RemindersColumns#MINUTES}事件发生前的分钟数,应在达到该时间时发出提醒。
    {@link android.provider.CalendarContract.RemindersColumns#METHOD}

    服务器上设置的提醒方法。下列值之一:

    +
      +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}
    • +
    • {@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}
    • +
    + +

    添加提醒

    + +

    下例显示如何为事件添加提醒。提醒在事件发生前 15 +分钟发出。

    +
    +long eventID = 221;
    +...
    +ContentResolver cr = getContentResolver();
    +ContentValues values = new ContentValues();
    +values.put(Reminders.MINUTES, 15);
    +values.put(Reminders.EVENT_ID, eventID);
    +values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
    +Uri uri = cr.insert(Reminders.CONTENT_URI, values);
    + +

    实例表

    + +

    +{@link android.provider.CalendarContract.Instances} +表储存事件实例的开始时间和结束时间。此表中的每一行都表示一个事件实例。 +实例表无法写入,只提供查询事件实例的途径。 +

    + +

    下表列出了一些您可以执行实例查询的字段。请注意, +时区由 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} +和 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES} 定义。

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    常量描述
    {@link android.provider.CalendarContract.Instances#BEGIN}实例的开始时间,以协调世界时毫秒数表示。
    {@link android.provider.CalendarContract.Instances#END}实例的结束时间,以协调世界时毫秒数表示。
    {@link android.provider.CalendarContract.Instances#END_DAY}与日历时区相应的实例儒略历结束日。 + + +
    {@link android.provider.CalendarContract.Instances#END_MINUTE}从日历时区午夜开始计算的实例结束时间(分钟)。 +
    {@link android.provider.CalendarContract.Instances#EVENT_ID}该实例对应事件的 _ID
    {@link android.provider.CalendarContract.Instances#START_DAY}与日历时区相应的实例儒略历开始日。 +
    {@link android.provider.CalendarContract.Instances#START_MINUTE}从日历时区午夜开始计算的实例开始时间(分钟)。 + +
    + +

    查询实例表

    + +

    如需查询实例表,您需要在 URI +中指定查询的时间范围。在以下示例中,{@link android.provider.CalendarContract.Instances} +通过其 {@link android.provider.CalendarContract.EventsColumns} 接口实现获得对 {@link +android.provider.CalendarContract.EventsColumns#TITLE} +字段的访问权限。 +换言之,{@link +android.provider.CalendarContract.EventsColumns#TITLE} 是通过数据库视图,而不是通过查询原始 +{@link +android.provider.CalendarContract.Instances} 表返回的。

    + +
    +private static final String DEBUG_TAG = "MyActivity";
    +public static final String[] INSTANCE_PROJECTION = new String[] {
    +    Instances.EVENT_ID,      // 0
    +    Instances.BEGIN,         // 1
    +    Instances.TITLE          // 2
    +  };
    +  
    +// The indices for the projection array above.
    +private static final int PROJECTION_ID_INDEX = 0;
    +private static final int PROJECTION_BEGIN_INDEX = 1;
    +private static final int PROJECTION_TITLE_INDEX = 2;
    +...
    +
    +// Specify the date range you want to search for recurring
    +// event instances
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2011, 9, 23, 8, 0);
    +long startMillis = beginTime.getTimeInMillis();
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2011, 10, 24, 8, 0);
    +long endMillis = endTime.getTimeInMillis();
    +  
    +Cursor cur = null;
    +ContentResolver cr = getContentResolver();
    +
    +// The ID of the recurring event whose instances you are searching
    +// for in the Instances table
    +String selection = Instances.EVENT_ID + " = ?";
    +String[] selectionArgs = new String[] {"207"};
    +
    +// Construct the query with the desired date range.
    +Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
    +ContentUris.appendId(builder, startMillis);
    +ContentUris.appendId(builder, endMillis);
    +
    +// Submit the query
    +cur =  cr.query(builder.build(), 
    +    INSTANCE_PROJECTION, 
    +    selection, 
    +    selectionArgs, 
    +    null);
    +   
    +while (cur.moveToNext()) {
    +    String title = null;
    +    long eventID = 0;
    +    long beginVal = 0;    
    +    
    +    // Get the field values
    +    eventID = cur.getLong(PROJECTION_ID_INDEX);
    +    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    +    title = cur.getString(PROJECTION_TITLE_INDEX);
    +              
    +    // Do something with the values. 
    +    Log.i(DEBUG_TAG, "Event:  " + title); 
    +    Calendar calendar = Calendar.getInstance();
    +    calendar.setTimeInMillis(beginVal);  
    +    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    +    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));    
    +    }
    + }
    + +

    日历 Intent 对象

    +

    您的应用不需要读取和写入日历数据的权限。它可以改用 Android 的日历应用支持的 Intent 对象将读取和写入操作转到该应用执行。下表列出了日历提供程序支持的 Intent 对象:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    操作URI描述Extra

    + {@link android.content.Intent#ACTION_VIEW VIEW}

    content://com.android.calendar/time/<ms_since_epoch>

    + 您还可以通过 +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI} 引用 URI。 +如需查看使用该 Intent 对象的示例,请参阅使用 Intent 对象查看日历数据。 + +
    打开日历后定位到 <ms_since_epoch> 指定的时间。无。

    {@link android.content.Intent#ACTION_VIEW VIEW}

    + +

    content://com.android.calendar/events/<event_id>

    + + 您还可以通过 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 引用 URI。 +如需查看使用该 Intent 对象的示例,请参阅使用 Intent 对象查看日历数据。 + +
    查看 <event_id> 指定的事件。{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
    +
    +
    + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
    {@link android.content.Intent#ACTION_EDIT EDIT}

    content://com.android.calendar/events/<event_id>

    + + 您还可以通过 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 引用 URI。 +如需查看使用该 Intent 对象的示例,请参阅使用 Intent 对象编辑事件。 + + +
    编辑 <event_id> 指定的事件。{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
    +
    +
    + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
    {@link android.content.Intent#ACTION_EDIT EDIT}
    +
    + {@link android.content.Intent#ACTION_INSERT INSERT}

    content://com.android.calendar/events

    + + 您还可以通过 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 引用 URI。 +如需查看使用该 Intent 对象的示例,请参阅使用 Intent 对象插入事件。 + +
    创建事件。下表列出的任一 Extra。
    + +

    下表列出了日历提供程序支持的 Intent Extra: +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Intent Extra描述
    {@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}事件的名称。
    {@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}事件开始时间,以从公元纪年开始计算的毫秒数表示。
    {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}事件结束时间,以从公元纪年开始计算的毫秒数表示。
    {@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}一个布尔值,表示事件属于全天事件。值可以是 +truefalse
    {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}事件的地点。
    {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}事件描述。
    + {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}逗号分隔值形式的受邀者电子邮件地址列表。
    + {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}事件的重复发生规则。
    + {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}事件是私人性质还是公共性质。
    {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}将此事件视为忙碌时间还是可调度的空闲时间。
    +

    下文描述如何使用这些 Intent 对象。

    + + +

    使用 Intent 对象插入事件

    + +

    您的应用可以利用 {@link android.content.Intent#ACTION_INSERT INSERT} + Intent 对象将事件插入任务转到日历应用执行。使用此方法时,您的应用甚至不需要在其清单文件中加入 +{@link +android.Manifest.permission#WRITE_CALENDAR} 权限。

    + + +

    当用户运行使用此方法的应用时,应用会将其转到日历来完成事件添加操作。 +{@link +android.content.Intent#ACTION_INSERT INSERT} Intent 利用 extra +字段为表单预填充日历中事件的详细信息。用户随后可取消事件、根据需要编辑表单或将事件保存到日历中。 + +

    + + + +

    以下是一个代码段,用于安排一个在 2012 年 1 月 19 日上午 +7:30 开始、8:30 结束的事件。请注意该代码段中的以下内容:

    + +
      +
    • 它将 {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} +指定为 URI。
    • + +
    • 它使用 {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} 和 {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} extra +字段为表单预填充事件的时间。这些时间的值必须以从公元纪年开始计算的协调世界时毫秒数表示。 +
    • + +
    • 它使用 {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} +extra 字段提供以逗号分隔的受邀者电子邮件地址列表。
    • + +
    +
    +Calendar beginTime = Calendar.getInstance();
    +beginTime.set(2012, 0, 19, 7, 30);
    +Calendar endTime = Calendar.getInstance();
    +endTime.set(2012, 0, 19, 8, 30);
    +Intent intent = new Intent(Intent.ACTION_INSERT)
    +        .setData(Events.CONTENT_URI)
    +        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
    +        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
    +        .putExtra(Events.TITLE, "Yoga")
    +        .putExtra(Events.DESCRIPTION, "Group class")
    +        .putExtra(Events.EVENT_LOCATION, "The gym")
    +        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
    +        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
    +startActivity(intent);
    +
    + +

    使用 Intent 对象编辑事件

    + +

    您可以按更新事件中所述直接更新事件。但使用 {@link +android.content.Intent#ACTION_EDIT EDIT} Intent 可以让不具有事件编辑权限的应用将事件编辑操作转到日历应用执行。当用户在日历中完成事件编辑后,将会返回原来的应用。 + + +

    以下是一个 Intent 对象的示例,它为指定事件设置新名称,并允许用户在日历中编辑事件。 +

    + + +
    long eventID = 208;
    +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +Intent intent = new Intent(Intent.ACTION_EDIT)
    +    .setData(uri)
    +    .putExtra(Events.TITLE, "My New Title");
    +startActivity(intent);
    + +

    使用 Intent 对象查看日历数据

    +

    日历提供程序提供了两种不同的 {@link android.content.Intent#ACTION_VIEW VIEW} Intent 对象使用方法:

    +
      +
    • 打开日历并定位到特定日期。
    • +
    • 查看事件。
    • + +
    +

    下例显示如何打开日历并定位到特定日期:

    +
    // A date-time specified in milliseconds since the epoch.
    +long startMillis;
    +...
    +Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
    +builder.appendPath("time");
    +ContentUris.appendId(builder, startMillis);
    +Intent intent = new Intent(Intent.ACTION_VIEW)
    +    .setData(builder.build());
    +startActivity(intent);
    + +

    下例显示如何打开事件进行查看:

    +
    long eventID = 208;
    +...
    +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
    +Intent intent = new Intent(Intent.ACTION_VIEW)
    +   .setData(uri);
    +startActivity(intent);
    +
    + + +

    同步适配器

    + + +

    应用和同步适配器在访问日历提供程序的方式上只存在微小差异: +

    + +
      +
    • 同步适配器需要通过将 {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} 设置为 true 来表明它是同步适配器。
    • + + +
    • 同步适配器需要提供 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} 和 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} 作为 URI 中的查询参数。
    • + +
    • 与应用或小工具相比,同步适配器拥有写入权限的列更多。 + 例如,应用只能修改日历的少数几种特性, +例如其名称、显示名称、能见度设置以及是否同步日历。 +相比之下,同步适配器不仅可以访问这些列,还能访问许多其他列, +例如日历颜色、时区、访问级别、地点等等。不过,同步适配器受限于它指定的 +ACCOUNT_NAME 和 +ACCOUNT_TYPE
    + +

    您可以利用以下 helper 方法返回供与同步适配器一起使用的 URI:

    +
     static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    +    return uri.buildUpon()
    +        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
    +        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
    +        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
    + }
    +
    +

    如需查看同步适配器的实现示例(并非仅限与日历有关的实现),请参阅 +SampleSyncAdapter。 diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/contacts-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..7125fb983d54d5763922600486778a5fc3e0e807 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=联系人提供程序 +@jd:body +

    +
    +

    内容快览

    +
      +
    • Android 有关联系人的信息存储库。
    • +
    • + 与 Web 同步。 +
    • +
    • + 集成社交流数据。 +
    • +
    +

    本文内容

    +
      +
    1. + 联系人提供程序组织 +
    2. +
    3. + 原始联系人 +
    4. +
    5. + 数据 +
    6. +
    7. + 联系人 +
    8. +
    9. + 来自同步适配器的数据 +
    10. +
    11. + 所需权限 +
    12. +
    13. + 用户个人资料 +
    14. +
    15. + 联系人提供程序元数据 +
    16. +
    17. + 联系人提供程序访问 +
    18. +
    19. +
    20. + 联系人提供程序同步适配器 +
    21. +
    22. + 社交流数据 +
    23. +
    24. + 其他联系人提供程序功能 +
    25. +
    +

    关键类

    +
      +
    1. {@link android.provider.ContactsContract.Contacts}
    2. +
    3. {@link android.provider.ContactsContract.RawContacts}
    4. +
    5. {@link android.provider.ContactsContract.Data}
    6. +
    7. {@code android.provider.ContactsContract.StreamItems}
    8. +
    +

    相关示例

    +
      +
    1. + +联系人管理器 + +
    2. +
    3. + +示例同步适配器 +
    4. +
    +

    另请参阅

    +
      +
    1. + +内容提供程序基础知识 + +
    2. +
    +
    +
    +

    + 联系人提供程序是一个强大而又灵活的 Android 组件,用于管理设备上有关联系人数据的中央存储库。 +联系人提供程序是您在设备的联系人应用中看到的数据源,您也可以在自己的应用中访问其数据,并可在设备与在线服务之间传送数据。 + +提供程序储存有多种数据源,由于它会试图为每个联系人管理尽可能多的数据,因此造成其组织结构非常复杂。 + +为此,该提供程序的 API 包含丰富的协定类和接口,为数据检索和修改提供便利。 + + +

    +

    + 本指南介绍下列内容: +

    +
      +
    • + 提供程序基本结构 +
    • +
    • + 如何从提供程序检索数据 +
    • +
    • + 如何修改提供程序中的数据 +
    • +
    • + 如何编写用于同步服务器数据与联系人提供程序数据的同步适配器。 + +
    • +
    +

    + 本指南假定您了解 Android 内容提供程序的基础知识。如需了解有关 Android 内容提供程序的更多信息,请阅读 +内容提供程序基础知识指南。 + + +示例同步适配器示例应用是一个示例,展示如何使用同步适配器在联系人提供程序与 Google 网络服务托管的一个示例应用之间传送数据。 + + +

    +

    联系人提供程序组织

    +

    + 联系人提供程序是 Android 内容提供程序的一个组件。它保留了三种类型的联系人数据,每一种数据都对应提供程序提供的一个表,如图 1 所示: + + +

    + +

    + 图 1. 联系人提供程序表结构。 +

    +

    + 这三个表通常以其协定类的名称命名。这些类定义表所使用的内容 URI、列名称及列值相应的常量: + +

    +
    +
    + {@link android.provider.ContactsContract.Contacts} 表 +
    +
    + 表示不同联系人的行,基于聚合的原始联系人行。 +
    +
    + {@link android.provider.ContactsContract.RawContacts} 表 +
    +
    + 包含联系人数据摘要的行,针对特定用户帐户和类型。 +
    +
    + {@link android.provider.ContactsContract.Data} 表 +
    +
    + 包含原始联系人详细信息(例如电子邮件地址或电话号码)的行。 +
    +
    +

    + 由 {@link android.provider.ContactsContract} +中的协定类表示的其他表是辅助表,联系人提供程序利用它们来管理其操作,或为设备的联系人或电话应用中的特定功能提供支持。 + +

    +

    原始联系人

    +

    + 一个原始联系人表示来自某一帐户类型和帐户名称、有关某个联系人的数据。 +由于联系人提供程序允许将多个在线服务作为某一联系人的数据源,因此它允许同一联系人对应多个原始联系人。 + + 借助支持多个原始联系人的特性,用户还可以将某一联系人在帐户类型相同的多个帐户中的数据进行合并。 + +

    +

    + 原始联系人的大部分数据并不存储在 +{@link android.provider.ContactsContract.RawContacts} 表内,而是存储在 +{@link android.provider.ContactsContract.Data} 表中的一行或多行内。每个数据行都有一个 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID} 列,其中包含其父级 {@link android.provider.ContactsContract.RawContacts} 行的 {@code android.provider.BaseColumns#_ID RawContacts._ID} 值。 + + +

    +

    重要的原始联系人列

    +

    + 表 1 列出了 {@link android.provider.ContactsContract.RawContacts} 表中的重要列。 +请阅读表后的说明: +

    +

    + 表 1. 重要的原始联系人列。 +

    + + + + + + + + + + + + + + + + + + + + +
    列名称用途备注
    + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + + 作为该原始联系人来源的帐户类型的帐户名称。 + 例如,Google 帐户的帐户名称是设备所有者的某个 Gmail +地址。如需了解详细信息,请参阅有关 + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} 的下一条目。 + + + 此名称的格式专用于其帐户类型。它不一定是电子邮件地址。 + +
    + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + + 作为该原始联系人来源的帐户类型。例如,Google 帐户的帐户类型是 com.google。 +请务必使用您拥有或控制的域的域标识符限定您的帐户类型。 +这可以确保您的帐户类型具有唯一性。 + + + 提供联系人数据的帐户类型通常关联有同步适配器,用于与联系人提供程序进行同步。 + +
    + {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + + 原始联系人的“已删除”标志。 + + 此标志让联系人提供程序能够在内部保留该行,直至同步适配器能够从服务器删除该行,然后再从存储库中最终删除该行。 + + +
    +

    说明

    +

    + 以下是关于 + {@link android.provider.ContactsContract.RawContacts} 表的重要说明: +

    +
      +
    • + 原始联系人的姓名并不存储其在 +{@link android.provider.ContactsContract.RawContacts} 中的行内,而是存储在 +{@link android.provider.ContactsContract.Data} 表的 +{@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 行内。一个原始联系人在 {@link android.provider.ContactsContract.Data} 表中只有一个该类型的行。 + +
    • +
    • + 注意:要想在原始联系人行中使用您自己的帐户数据,必须先在 {@link android.accounts.AccountManager} 中注册帐户。 +为此,请提示用户将帐户类型及其帐户名称添加到帐户列表。 +如果您不这样做,联系人提供程序将自动删除您的原始联系人行。 + +

      + 例如,如果您想让您的应用为您域名为 {@code com.example.dataservice}、基于 Web 的服务保留联系人数据,并且您的服务的用户帐户是 {@code becky.sharp@dataservice.example.com},则用户必须先添加帐户“类型”({@code com.example.dataservice}) 和帐户“名称”({@code becky.smart@dataservice.example.com}),然后您的应用才能添加原始联系人行。 + + + + + 您可以在文档中向用户解释这项要求,也可以提示用户添加类型和名称,或者同时采用这两种措施。 +下文对帐户类型和帐户名称做了更详尽的描述。 + +

    • +
    +

    原始联系人数据来源

    +

    + 为理解原始联系人的工作方式,假设有一位用户“Emily Dickinson”,她的设备上定义了以下三个用户帐户: + +

    +
      +
    • emily.dickinson@gmail.com
    • +
    • emilyd@gmail.com
    • +
    • Twitter 帐户“belle_of_amherst”
    • +
    +

    + 该用户已在 Accounts 设置中为全部三个帐户启用了 +Sync Contacts。 +

    +

    + 假定 Emily Dickinson 打开一个浏览器窗口,以 +emily.dickinson@gmail.com 身份登录 Gmail,然后打开 +“联系人”,并添加“Thomas Higginson”。后来,她以 +emilyd@gmail.com 身份登录 Gmail,并向“Thomas Higginson”发送一封电子邮件,此操作会自动将他添加为联系人。 +她还在 Twitter 上关注了“colonel_tom”(Thomas Higginson 的 Twitter ID)。 + +

    +

    + 以上操作的结果是,联系人提供程序会创建以下这三个原始联系人: +

    +
      +
    1. + 第一个原始联系人对应“Thomas Higginson”,关联帐户 emily.dickinson@gmail.com。 + 用户帐户类型是 Google。 +
    2. +
    3. + 第二个原始联系人对应“Thomas Higginson”,关联帐户 emilyd@gmail.com。 + 用户帐户类型也是 Google。由于添加的联系人对应的用户帐户不同,因此尽管名称与前一名称完全相同,也只能作为第二个原始联系人。 + + +
    4. +
    5. + 第三个原始联系人对应“Thomas Higginson”,关联帐户“belle_of_amherst”。用户帐户类型是 Twitter。 + +
    6. +
    +

    数据

    +

    + 如前文所做的说明,原始联系人的数据存储在一个 +{@link android.provider.ContactsContract.Data} 行中,该行链接到原始联系人的 +_ID 值。这使一位原始联系人可以拥有多个具有相同数据类型的实例,例如电子邮件地址或电话号码。 +例如,如果对应 +{@code emilyd@gmail.com} 的“Thomas Higginson”(关联 Google 帐户 emilyd@gmail.com 的 Thomas Higginson +的原始联系人行)的住宅电子邮件地址为 +thigg@gmail.com,办公电子邮件地址为 +thomas.higginson@gmail.com,则联系人提供程序会存储这两个电子邮件地址行,并将它们都链接到原始联系人。 + +

    +

    + 请注意,这个表中存储了不同类型的数据。显示姓名、电话号码、电子邮件、邮政地址、照片以及网站明细行都可以在 {@link android.provider.ContactsContract.Data} 表中找到。 + +为便于管理这些数据, +{@link android.provider.ContactsContract.Data} 表为一些列使用了描述性名称,为其他列使用了通用名称。 +使用描述性名称的列的内容具有相同的含义,与行中数据的类型无关,而使用通用名称的列的内容则会随数据类型的不同而具有不同的含义。 + + +

    +

    描述性列名称

    +

    + 以下是一些描述性列名称的示例: +

    +
    +
    + {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} +
    +
    + 该数据对应的原始联系人 _ID 列的值。 +
    +
    + {@link android.provider.ContactsContract.Data#MIMETYPE} +
    +
    + 该行中存储的数据类型,以自定义 MIME(多用途互联网邮件扩展)类型表示。联系人提供程序使用了 +{@link android.provider.ContactsContract.CommonDataKinds} 子类中定义的 MIME 类型。 +这些 MIME 类型为开源类型,可供与联系人提供程序协作的任何应用或同步适配器使用。 + +
    +
    + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} +
    +
    + 如果一个原始联系人可能具有多个这种类型的数据行, +{@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 列会标记 +包含该类型主要数据的数据行。例如,如果用户长按某个联系人的电话号码,并选择 Set default,则包含该号码的 {@link android.provider.ContactsContract.Data} 行会将其 {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 列设置为一个非零值。 + + + + +
    +
    +

    通用列名称

    +

    + 有 15 个通用列命名为 DATA1 至 +DATA15,可普遍适用;还有四个通用列命名为 SYNC1SYNC4,只应由同步适配器使用。 + +通用列名称常量始终有效,与行包含的数据类型无关。 + +

    +

    + DATA1 列为索引列。联系人提供程序总是在此列中存储其预期会成为最频繁查询目标的数据。 +例如,在一个电子邮件行中,此列包含实际电子邮件地址。 + +

    +

    + 按照惯例,DATA15 为预留列,用于存储照片缩略图等二进制大型对象 +(BLOB) 数据。 +

    +

    类型专用列名称

    +

    + 为便于处理特定类型行的列,联系人提供程序还提供了 +{@link android.provider.ContactsContract.CommonDataKinds} 子类中定义的类型专用列名称常量。 +这些常量只是为同一列名称提供不同的常量名称,这有助于您访问特定类型行中的数据。 + + +

    +

    + 例如,{@link android.provider.ContactsContract.CommonDataKinds.Email} 类为 {@link android.provider.ContactsContract.Data} 行定义类型专用列名称常量,该行的 MIME 类型为 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE}。 + + +该类包含电子邮件地址列的 +{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} +常量。 +{@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} 的实际值为“data1”,这与列的通用名称相同。 + +

    +

    + 注意:请勿使用具有提供程序某个预定义 MIME 类型的行向 +{@link android.provider.ContactsContract.Data} 表中添加您自己的自定义数据。 +否则您可能会丢失数据,或导致提供程序发生故障。 +例如,如果某一行具有 MIME 类型 +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE + Email.CONTENT_ITEM_TYPE},并且 +DATA1 列包含的是用户名而不是电子邮件地址,您就不应添加该行。如果您为该行使用自定义的 MIME 类型,则可自由定义您的自定义类型专用的列名称,并随心所欲地使用这些列。 + +

    +

    + 图 2 显示的是描述性列和数据列在 +{@link android.provider.ContactsContract.Data} 行中的显示情况,以及类型专用列名称“覆盖”通用列名称的情况 + +

    +How type-specific column names map to generic column names +

    + 图 2. 类型专用列名称和通用列名称。 +

    +

    类型专用列名称类

    +

    + 表 2 列出了最常用的类型专用列名称类: +

    +

    + 表 2. 类型专用列名称类

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    映射类数据类型备注
    {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}与该数据行关联的原始联系人的姓名数据。一位原始联系人只有其中一行。
    {@link android.provider.ContactsContract.CommonDataKinds.Photo}与该数据行关联的原始联系人的主要照片。一位原始联系人只有其中一行。
    {@link android.provider.ContactsContract.CommonDataKinds.Email}与该数据行关联的原始联系人的电子邮件地址。一位原始联系人可有多个电子邮件地址。
    {@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}与该数据行关联的原始联系人的邮政地址。一位原始联系人可有多个邮政地址。
    {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}将原始联系人链接到联系人提供程序内其中一组的标识符。 + 组是帐户类型和帐户名称的一项可选功能。联系人组部分对其做了更详尽的描述。 + +
    +

    联系人

    +

    + 联系人提供程序通过将所有帐户类型和帐户名称的原始联系人行合并来形成联系人。 +这可以为显示和修改用户针对某一联系人收集的所有数据提供便利。 +联系人提供程序管理新联系人行的创建,以及原始联系人与现有联系人行的合并。 +系统不允许应用或同步适配器添加联系人,并且联系人行中的某些列是只读列。 + +

    +

    + 注:如果您试图通过 +{@link android.content.ContentResolver#insert(Uri,ContentValues) insert()} 向联系人提供程序添加联系人,会引发一个 {@link java.lang.UnsupportedOperationException} 异常。 +如果您试图更新一个列为“只读”的列,更新会被忽略。 + +

    +

    + 如果添加的新原始联系人不匹配任何现有联系人,联系人提供程序会相应地创建新联系人。 +如果某个现有原始联系人的数据发生了变化,不再匹配其之前关联的联系人,则提供程序也会执行此操作。 + +如果应用或同步适配器创建的新原始联系人“的确”匹配某位现有联系人,则新原始联系人将与现有联系人合并。 + + +

    +

    + 联系人提供程序通过 {@link android.provider.ContactsContract.Contacts Contacts} 表中联系人行的 +_ID 列将联系人行与其各原始联系人行链接起来。 +原始联系人表 {@link android.provider.ContactsContract.RawContacts} 的 CONTACT_ID 列包含对应于每个原始联系人行所关联联系人行的 _ID 值。 + + +

    +

    + {@link android.provider.ContactsContract.Contacts} 表还有一个 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 列,它是一个指向联系人行的“永久性”链接。 +由于联系人提供程序会自动维护联系人,因此可能会在合并或同步时相应地更改联系人行的 {@code android.provider.BaseColumns#_ID} 值。 + +即使发生这种情况,合并了联系人 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 的内容 URI +{@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} 仍将指向联系人行,这样,您就能使用 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} +保持指向“最喜爱”联系人的链接,以及执行其他操作。 +该列具有其自己的格式,与 {@code android.provider.BaseColumns#_ID} 列的格式无关。 + +

    +

    + 图 3 显示的是这三个主要表的相互关系。 +

    +Contacts provider main tables +

    + 图 3. 联系人表、原始联系人表与详细信息表之间的关系。 +

    +

    来自同步适配器的数据

    +

    + 虽然用户是直接将联系人数据输入到设备中,但这些数据也会通过同步适配器从 Web 服务流入联系人提供程序中,这些同步适配器可自动化设备与服务之间的数据传送。 + +同步适配器在系统控制下在后台运行,它们会调用 {@link android.content.ContentResolver} 方法来管理数据。 + + +

    +

    + 在 Android 中,与同步适配器协作的 Web 服务通过帐户类型加以标识。 + 每个同步适配器都与一个帐户类型协作,但它可以支持该类型的多个帐户名称。 +原始联系人数据来源部分对帐户类型和帐户名称做了简要描述。 +下列定义提供了更多详细信息,并描述了帐户类型及帐户名称与同步适配器及服务之间的关系。 + +

    +
    +
    + 帐户类型 +
    +
    + 表示用户在其中存储数据的服务。在大多数时候,用户需要向服务验证身份。 +例如,Google Contacts 是一个以代码 google.com 标识的帐户类型。 +该值对应于 +{@link android.accounts.AccountManager} 使用的帐户类型。 +
    +
    + 帐户名称 +
    +
    + 表示某个帐户类型的特定帐户或登录名。Google Contacts 帐户与 Google 帐户相同,都是以电子邮件地址作为帐户名称。 + + 其他服务可能使用一个单词的用户名或数字 ID。 +
    +
    +

    + 帐户类型不必具有唯一性。用户可以配置多个 Google Contacts 帐户并将它们的数据下载到联系人提供程序;如果用户为个人帐户名称和工作帐户名称分别设置了一组联系人,就可能发生这种情况。 + +帐户名称通常具有唯一性。 +它们共同标识联系人提供程序与外部服务之间的特定数据流。 + +

    +

    + 如果您想将服务的数据传送到联系人提供程序,则需编写您自己的同步适配器。 +联系人提供程序同步适配器部分对此做了更详尽的描述。 + +

    +

    + 图 4 显示的是联系人提供程序如何融入联系人数据的流动。 +在名为“同步适配器”的方框中,每个适配器都以其帐户类型命名。 +

    +Flow of data about people +

    + 图 4. 联系人提供程序数据流。 +

    +

    所需权限

    +

    + 想要访问联系人提供程序的应用必须请求以下权限: + +

    +
    +
    对一个或多个表的读取权限
    +
    + {@link android.Manifest.permission#READ_CONTACTS},在 +AndroidManifest.xml 中指定,使用 + + <uses-permission> 元素作为 +<uses-permission android:name="android.permission.READ_CONTACTS">。 +
    +
    对一个或多个表的写入权限
    +
    + {@link android.Manifest.permission#WRITE_CONTACTS},在 +AndroidManifest.xml 中指定,使用 + + <uses-permission> 元素作为 +<uses-permission android:name="android.permission.WRITE_CONTACTS">。 +
    +
    +

    + 这些权限不适用于用户个人资料数据。下面的用户个人资料部分对用户个人资料及其所需权限做了阐述。 + + +

    +

    + 请切记,用户的联系人数据属于个人敏感数据。用户关心其隐私权,因此不希望应用收集有关其自身的数据或其联系人的数据。 + + 如需权限来访问其联系人数据的理由并不充分,用户可能给您的应用作出差评或干脆拒绝安装。 + +

    +

    用户个人资料

    +

    + {@link android.provider.ContactsContract.Contacts} 表有一行包含设备用户的个人资料数据。 +这些数据描述设备的 user 而不是用户的其中一位联系人。 +对于每个使用个人资料的系统,该个人资料联系人行都链接到某个原始联系人行。 + + 每个个人资料原始联系人行可具有多个数据行。{@link android.provider.ContactsContract.Profile} 类中提供了用于访问用户个人资料的常量。 + +

    +

    + 访问用户个人资料需要特殊权限。除了进行读取和写入所需的 +{@link android.Manifest.permission#READ_CONTACTS} 和 +{@link android.Manifest.permission#WRITE_CONTACTS} 权限外,如果想访问用户个人资料,还分别需要 {@code android.Manifest.permission#READ_PROFILE} 和 +{@code android.Manifest.permission#WRITE_PROFILE} 权限进行读取和写入访问。 + + +

    +

    + 请切记,您应该将用户的个人资料视为敏感数据。{@code android.Manifest.permission#READ_PROFILE} 权限让您可以访问设备用户的个人身份识别数据。 + +请务必在您的应用的描述中告知用户您需要用户个人资料访问权限的原因。 + +

    +

    + 要检索包含用户个人资料的联系人行,请调用 {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) +ContentResolver.query()}。 +将内容 URI 设置为 +{@link android.provider.ContactsContract.Profile#CONTENT_URI} 并且不要提供任何选择条件。 +您还可以使用该内容 URI 作为检索原始联系人或个人资料数据的基本 URI。 +例如,以下代码段用于检索个人资料数据: +

    +
    +// Sets the columns to retrieve for the user profile
    +mProjection = new String[]
    +    {
    +        Profile._ID,
    +        Profile.DISPLAY_NAME_PRIMARY,
    +        Profile.LOOKUP_KEY,
    +        Profile.PHOTO_THUMBNAIL_URI
    +    };
    +
    +// Retrieves the profile from the Contacts Provider
    +mProfileCursor =
    +        getContentResolver().query(
    +                Profile.CONTENT_URI,
    +                mProjection ,
    +                null,
    +                null,
    +                null);
    +
    +

    + 注:如果您要检索多个联系人行并想要确定其中一个是否为用户个人资料,请测试该行的 +{@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} 列。 +如果该联系人是用户个人资料,则此列设置为“1”。 + +

    +

    联系人提供程序元数据

    +

    + 联系人提供程序管理用于追踪存储库中联系人数据状态的数据。 +这些有关存储库的元数据存储在各处,其中包括原始联系人表行、数据表行和联系人表行、 +{@link android.provider.ContactsContract.Settings} 表以及 +{@link android.provider.ContactsContract.SyncState} 表。 +下表显示的是每一部分元数据的作用: + +

    +

    + 表 3. 联系人提供程序中的元数据

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    含义
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#DIRTY}“0”:上次同步以来未发生变化。 + 标记设备上因发生变化而需要同步回服务器的原始联系人。 +当 Android 应用更新行时,联系人提供程序会自动设置该值。 + +

    + 修改原始联系人表或数据表的同步适配器应始终向他们使用的内容 URI 追加字符串 {@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER}。 + +这可以防止提供程序将行标记为已更新。 + 否则,即使服务器是修改的来源,同步适配器修改仍显示为本地修改,并会发送到服务器。 + +

    +
    “1”:上次同步以来发生了变化,需要同步回服务器。
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#VERSION}此行的版本号。 + 每当行或其相关数据发生变化时,联系人提供程序都会自动增加此值。 + +
    {@link android.provider.ContactsContract.Data}{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}此行的版本号。 + 每当数据行发生变化时,联系人提供程序都会自动增加此值。 + +
    {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} + 一个字符串值,用于在创建此原始联系人的帐户中对该联系人进行唯一标识。 + + + 当同步适配器创建新原始联系人时,此列应设置为该原始联系人在服务器中的唯一 ID。 +当 Android 应用创建新原始联系人时,应将此列留空。 +这是为了向同步适配器表明,它应该在服务器上创建新原始联系人,并获取 + {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} 的值。 + +

    + 具体地讲,对于每个帐户类型,该源 ID 都必须是唯一的,并且应在所有同步中保持稳定: + +

    +
      +
    • + 唯一:帐户的每个原始联系人都必须有自己的源 ID。如果您不强制执行此要求,会在联系人应用中引发问题。 + + 请注意,帐户类型相同的两个原始联系人可以具有相同的源 ID。 +例如,允许帐户 {@code emily.dickinson@gmail.com} 的原始联系人“Thomas Higginson”与帐户 +{@code emilyd@gmail.com} 的原始联系人“Thomas Higginson”具有相同的源 ID。 + + +
    • +
    • + 稳定:源 ID 是该原始联系人在在线服务中的数据的永久性组成部分。 +例如,如果用户从应用设置中清除存储的联系人数据并重新同步,则恢复的原始联系人的源 ID 应与以前相同。 + +如果您不强制执行此要求,快捷方式将停止工作。 + +
    • +
    +
    {@link android.provider.ContactsContract.Groups}{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}“0”:此组中的联系人在 Android 应用 UI 中不应处于可见状态。 + 此列用于兼容那些允许用户隐藏特定组中联系人的服务器。 + +
    “1”:系统允许此组中的联系人在应用 UI 中处于可见状态。
    {@link android.provider.ContactsContract.Settings} + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} + “0”:对于此帐户和帐户类型,未归入组的联系人在 Android 应用 UI 中处于不可见状态。 + + + 默认情况下,如果联系人的所有原始联系人都未归入组,则它们将处于不可见状态(原始联系人的组成员身份通过 {@link android.provider.ContactsContract.Data} 表中的一个或多个 +{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} 行指示)。 + + + 通过在 {@link android.provider.ContactsContract.Settings} 表行中为帐户类型和帐户设置此标志,您可以强制未归入组的联系人处于可见状态。 + + 此标志的一个用途是显示不使用组的服务器上的联系人。 +
    + “1”:对于此帐户和帐户类型,未归入组的联系人在应用 UI 中处于可见状态。 + +
    {@link android.provider.ContactsContract.SyncState}(所有列) + 此表用于存储同步适配器的元数据。 + + 利用此表,您可以将同步状态及其他同步相关数据持久地存储在设备中。 + +
    +

    联系人提供程序访问

    +

    + 本节描述访问联系人提供程序中数据的准则,侧重于阐述以下内容: + +

    +
      +
    • + 实体查询。 +
    • +
    • + 批量修改。 +
    • +
    • + 通过 Intent 执行检索和修改。 +
    • +
    • + 数据完整性。 +
    • +
    +

    + 联系人提供程序同步适配器部分也对通过同步适配器进行修改做了更详尽的阐述。 + +

    +

    查询实体

    +

    + 由于联系人提供程序表是以层级形式组织,因此对于检索某一行以及与其链接的所有“子”行,往往很有帮助。 +例如,要想显示某位联系人的所有信息,您可能需要检索某个 +{@link android.provider.ContactsContract.Contacts} 行的所有{@link android.provider.ContactsContract.RawContacts} 行,或者检索某个 +{@link android.provider.ContactsContract.RawContacts} 行的所有 +{@link android.provider.ContactsContract.CommonDataKinds.Email} 行。 + +为便于执行此操作,联系人提供程序提供了实体构造,其作用类似于表间的数据库连接。 + + +

    +

    + 实体类似于一个表,由父表及其子表中的选定列组成。 + 当您查询实体时,需要根据实体中的可用列提供投影和搜索条件。 +结果会得到一个 {@link android.database.Cursor},检索的每个子表行在其中都有一行与之对应。 +例如,如果您在 +{@link android.provider.ContactsContract.Contacts.Entity} 中查询某个联系人姓名以及该姓名所有原始联系人的所有 {@link android.provider.ContactsContract.CommonDataKinds.Email} 行,您会获得一个 {@link android.database.Cursor},每个 {@link android.provider.ContactsContract.CommonDataKinds.Email} 行在其中都有一行与之对应。 + + + +

    +

    + 实体简化了查询。使用实体时,您可以一次性检索联系人或原始联系人的所有联系人数据,而不必先通过查询父表获得ID,然后通过该 ID 查询子表。此外,联系人提供程序可通过单一事务处理实体查询,这确保了所检索数据的内部一致性。 + + + + +

    +

    + 注:实体通常不包含父表和子表的所有列。 +如果您试图使用的列名称并未出现在实体的列名称常量列表中,则会引发一个 {@link java.lang.Exception}。 + +

    +

    + 以下代码段说明如何检索某位联系人的所有原始联系人行。该代码段是一个大型应用的组成部分,包含“主”和“详”两个 Activity。 +主 Activity 显示一个联系人行列表;当用户选择一行时,该 Activity 会将其 ID 发送至详 Activity。 + +详 Activity 使用 {@link android.provider.ContactsContract.Contacts.Entity} 显示与所选联系人关联的所有原始联系人中的所有数据行。 + + +

    +

    + 以下代码段摘自“detail”Activity: +

    +
    +...
    +    /*
    +     * Appends the entity path to the URI. In the case of the Contacts Provider, the
    +     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
    +     */
    +    mContactUri = Uri.withAppendedPath(
    +            mContactUri,
    +            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
    +
    +    // Initializes the loader identified by LOADER_ID.
    +    getLoaderManager().initLoader(
    +            LOADER_ID,  // The identifier of the loader to initialize
    +            null,       // Arguments for the loader (in this case, none)
    +            this);      // The context of the activity
    +
    +    // Creates a new cursor adapter to attach to the list view
    +    mCursorAdapter = new SimpleCursorAdapter(
    +            this,                        // the context of the activity
    +            R.layout.detail_list_item,   // the view item containing the detail widgets
    +            mCursor,                     // the backing cursor
    +            mFromColumns,                // the columns in the cursor that provide the data
    +            mToViews,                    // the views in the view item that display the data
    +            0);                          // flags
    +
    +    // Sets the ListView's backing adapter.
    +    mRawContactList.setAdapter(mCursorAdapter);
    +...
    +@Override
    +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    +
    +    /*
    +     * Sets the columns to retrieve.
    +     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
    +     * DATA1 contains the first column in the data row (usually the most important one).
    +     * MIMETYPE indicates the type of data in the data row.
    +     */
    +    String[] projection =
    +        {
    +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
    +            ContactsContract.Contacts.Entity.DATA1,
    +            ContactsContract.Contacts.Entity.MIMETYPE
    +        };
    +
    +    /*
    +     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
    +     * contact collated together.
    +     */
    +    String sortOrder =
    +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
    +            " ASC";
    +
    +    /*
    +     * Returns a new CursorLoader. The arguments are similar to
    +     * ContentResolver.query(), except for the Context argument, which supplies the location of
    +     * the ContentResolver to use.
    +     */
    +    return new CursorLoader(
    +            getApplicationContext(),  // The activity's context
    +            mContactUri,              // The entity content URI for a single contact
    +            projection,               // The columns to retrieve
    +            null,                     // Retrieve all the raw contacts and their data rows.
    +            null,                     //
    +            sortOrder);               // Sort by the raw contact ID.
    +}
    +
    +

    + 加载完成时,{@link android.app.LoaderManager} 会调用一个 +{@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) +onLoadFinished()} 回调。此方法的传入参数之一是一个 +{@link android.database.Cursor},其中包含查询的结果。在您自己的应用中,您可以从该 {@link android.database.Cursor} 获取数据,以进行显示或做进一步处理。 + +

    +

    批量修改

    +

    + 您应尽可能地通过创建一个 {@link android.content.ContentProviderOperation} 对象 {@link java.util.ArrayList} +并调用 {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()},以“批处理模式”在联系人提供程序中插入、更新和删除数据。 + +由于联系人提供程序是在 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 中通过单一事务执行所有操作,因此您的修改绝不会使联系人存储库出现不一致问题。 + + +此外,批量修改还有便于同时插入原始联系人及其明细数据。 + +

    +

    + 注:要修改单个原始联系人,可以考虑向设备的联系人应用发送一个 Intent,而不是在您的应用中处理修改。通过 Intent 执行检索和修改部分对此操作做了更详尽的描述。 + + + +

    +

    屈服点

    +

    + 一个包含大量操作的批量修改可能会阻断其他进程,导致糟糕的总体用户体验。 +要将您想执行的所有修改组织到尽可能少的单独列表中,同时防止它们阻断系统,则应为一项或多项操作设置屈服点。 + + + 屈服点是一个 {@link android.content.ContentProviderOperation} 对象,其 +{@link android.content.ContentProviderOperation#isYieldAllowed()} 值设置为 +true。当联系人提供程序遇到屈服点时,它会暂停其工作,让其他进程运行,并关闭当前事务。 +当提供程序再次启动时,它会继续执行 {@link java.util.ArrayList} 中的下一项操作,并启动一个新的事务。 + + +

    +

    + 屈服点会导致每次调用 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 会产生多个事务。因此,您应该为针对一组相关行的最后一项操作设置屈服点。 + + 例如,您应该为一组操作中添加原始联系人行及其关联数据行的最后一项操作,或者针对一组与一位联系人相关的行的最后一项操作设置屈服点。 + + +

    +

    + 屈服点也是一个原子操作单元。两个屈服点之间所有访问的成功或失败都将以一个单元的形式出现。 +如果您不设置任何屈服点,则最小的原子操作是整个批量操作。 +如果您使用了屈服点,则可以防止操作降低系统性能,还可确保一部分操作是原子操作。 + + +

    +

    修改向后引用

    +

    + 当您将一个新原始联系人行及其关联的数据行作为一组 +{@link android.content.ContentProviderOperation} 对象插入时,需要通过将原始联系人的 +{@code android.provider.BaseColumns#_ID} 值作为 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 值插入,将数据行链接到原始联系人行。 +不过,当您为数据行创建 +{@link android.content.ContentProviderOperation} 时,该值不可用,因为您尚未对原始联系人行应用 +{@link android.content.ContentProviderOperation}。 +为解决此问题, +{@link android.content.ContentProviderOperation.Builder} 类使用了 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} 方法。 + 该方法让您可以插入或修改包含上一操作结果的列。 + +

    +

    + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +方法具有两个参数: +

    +
    +
    + key +
    +
    + 键-值对的键。此参数的值应为您要修改的表中某一列的名称。 + +
    +
    + previousResult +
    +
    + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 中 +{@link android.content.ContentProviderResult} 对象数组内某一值以 0 开始的索引。 +应用批处理操作时,每个操作的结果都存储在一个中间结果数组内。 + +previousResult 值是其中一个结果的索引,它通过 key +值进行检索和存储。 +这样,您就可以插入一条新的原始联系人记录,并取回其 +{@code android.provider.BaseColumns#_ID} 值,然后在添加 {@link android.provider.ContactsContract.Data} 行时“向后引用”该值。 + +

    + 系统会在您首次调用 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 时创建整个结果数组,其大小与您提供的 {@link android.content.ContentProviderOperation} 对象的 {@link java.util.ArrayList} 大小相等。 + +不过,结果数组中的所有元素都设置为 null,如果您试图向后引用某个尚未应用的操作的结果, +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +会引发一个 {@link java.lang.Exception}。 + + + +

    +
    +
    +

    + 以下代码段说明如何批量插入新原始联系人和数据。代码段中包括用于建立屈服点和使用向后引用的代码。 +这些代码段是扩展版本的 createContacEntry() 方法,该方法是 + Contact Manager 示例应用中 ContactAdder 类的组成部分。 + + + +

    +

    + 第一个代码段用于检索 UI 中的联系人数据。此时,用户已经选择了应添加新原始联系人的帐户。 + +

    +
    +// Creates a contact entry from the current UI values, using the currently-selected account.
    +protected void createContactEntry() {
    +    /*
    +     * Gets values from the UI
    +     */
    +    String name = mContactNameEditText.getText().toString();
    +    String phone = mContactPhoneEditText.getText().toString();
    +    String email = mContactEmailEditText.getText().toString();
    +
    +    int phoneType = mContactPhoneTypes.get(
    +            mContactPhoneTypeSpinner.getSelectedItemPosition());
    +
    +    int emailType = mContactEmailTypes.get(
    +            mContactEmailTypeSpinner.getSelectedItemPosition());
    +
    +

    + 下一个代码段用于创建将该原始联系人行插入 +{@link android.provider.ContactsContract.RawContacts} 表的操作: +

    +
    +    /*
    +     * Prepares the batch operation for inserting a new raw contact and its data. Even if
    +     * the Contacts Provider does not have any data for this person, you can't add a Contact,
    +     * only a raw contact. The Contacts Provider will then add a Contact automatically.
    +     */
    +
    +     // Creates a new array of ContentProviderOperation objects.
    +    ArrayList<ContentProviderOperation> ops =
    +            new ArrayList<ContentProviderOperation>();
    +
    +    /*
    +     * Creates a new raw contact with its account type (server type) and account name
    +     * (user's account). Remember that the display name is not stored in this row, but in a
    +     * StructuredName data row. No other data is required.
    +     */
    +    ContentProviderOperation.Builder op =
    +            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
    +            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
    +            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +

    + 接着,代码会创建显示姓名行、电话行和电子邮件行的数据行。 +

    +

    + 每个操作生成器对象都使用 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +来获取 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}。引用指回来自第一次操作的 {@link android.content.ContentProviderResult} 对象,第一次操作就是添加原始联系人行并返回其新 {@code android.provider.BaseColumns#_ID} +值。 + +结果是,每个数据行都通过其 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} +自动链接到其所属的 {@link android.provider.ContactsContract.RawContacts} 行。 +

    +

    + 添加电子邮件行的 {@link android.content.ContentProviderOperation.Builder} 对象带有 +{@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) +withYieldAllowed()} 标志,用于设置屈服点: +

    +
    +    // Creates the display name for the new raw contact, as a StructuredName data row.
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * withValueBackReference sets the value of the first argument to the value of
    +             * the ContentProviderResult indexed by the second argument. In this particular
    +             * call, the raw contact ID column of the StructuredName data row is set to the
    +             * value of the result returned by the first operation, which is the one that
    +             * actually adds the raw contact row.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to StructuredName
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
    +
    +            // Sets the data row's display name to the name in the UI.
    +            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +    // Inserts the specified phone number and type as a Phone data row
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * Sets the value of the raw contact id column to the new raw contact ID returned
    +             * by the first operation in the batch.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to Phone
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
    +
    +            // Sets the phone number and type
    +            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
    +            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +    // Inserts the specified email and type as a Phone data row
    +    op =
    +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
    +            /*
    +             * Sets the value of the raw contact id column to the new raw contact ID returned
    +             * by the first operation in the batch.
    +             */
    +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
    +
    +            // Sets the data row's MIME type to Email
    +            .withValue(ContactsContract.Data.MIMETYPE,
    +                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
    +
    +            // Sets the email address and type
    +            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
    +            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
    +
    +    /*
    +     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
    +     * will yield priority to other threads. Use after every set of operations that affect a
    +     * single contact, to avoid degrading performance.
    +     */
    +    op.withYieldAllowed(true);
    +
    +    // Builds the operation and adds it to the array of operations
    +    ops.add(op.build());
    +
    +

    + 最后一个代码段显示的是 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 调用,用于插入新原始联系人行和数据行。 + +

    +
    +    // Ask the Contacts Provider to create a new contact
    +    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
    +            mSelectedAccount.getType() + ")");
    +    Log.d(TAG,"Creating contact: " + name);
    +
    +    /*
    +     * Applies the array of ContentProviderOperation objects in batch. The results are
    +     * discarded.
    +     */
    +    try {
    +
    +            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    +    } catch (Exception e) {
    +
    +            // Display a warning
    +            Context ctx = getApplicationContext();
    +
    +            CharSequence txt = getString(R.string.contactCreationFailure);
    +            int duration = Toast.LENGTH_SHORT;
    +            Toast toast = Toast.makeText(ctx, txt, duration);
    +            toast.show();
    +
    +            // Log exception
    +            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    +    }
    +}
    +
    +

    + 此外,您还可以利用批处理操作实现乐观并发控制,这是一种无需锁定底层存储库便可应用修改事务的控制方法。 + + 要使用此方法,您需要应用事务,然后检查是否存在可能已同时做出的其他修改。 +如果您发现了不一致的修改,请回滚事务并重试。 + +

    +

    + 乐观并发控制对于移动设备很有用,因为在移动设备上,同一时间只有一位用户,并且同时访问数据存储库的情况很少见。 +由于未使用锁定功能,因此不用浪费时间设置锁定或等待其他事务解除锁定。 + +

    +

    + 要在更新某个 +{@link android.provider.ContactsContract.RawContacts} 行时使用乐观并发控制,请按以下步骤操作: +

    +
      +
    1. + 检索原始联系人的 {@link android.provider.ContactsContract.SyncColumns#VERSION} +列以及要检索的其他数据。 +
    2. +
    3. + 创建一个适合使用 +{@link android.content.ContentProviderOperation#newAssertQuery(Uri)} 方法强制执行约束 +的 {@link android.content.ContentProviderOperation.Builder} 对象。对于内容 URI,请使用追加有原始联系人 {@code android.provider.BaseColumns#_ID} 的 {@link android.provider.ContactsContract.RawContacts#CONTENT_URI +RawContacts.CONTENT_URI} +。 + +
    4. +
    5. + 对于 {@link android.content.ContentProviderOperation.Builder} 对象,请调用 +{@link android.content.ContentProviderOperation.Builder#withValue(String, Object) +withValue()},对 {@link android.provider.ContactsContract.SyncColumns#VERSION} +列与您刚检索的版本号进行比较。 +
    6. +
    7. + 对于同一 {@link android.content.ContentProviderOperation.Builder},请调用 +{@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) +withExpectedCount()},确保此断言只对一行进行测试。 +
    8. +
    9. + 调用 {@link android.content.ContentProviderOperation.Builder#build()} 创建 +{@link android.content.ContentProviderOperation} 对象,然后将此对象添加为要传递至 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的 {@link java.util.ArrayList} 中的第一个对象。 + +
    10. +
    11. + 应用批处理事务。 +
    12. +
    +

    + 如果在您读取原始联系人行到您试图对其进行修改这段时间有另一项操作更新了该行,“断言”{@link android.content.ContentProviderOperation} +将会失败,系统将终止整个批处理操作。 +此情况下,您可以选择重新执行批处理操作,或执行其他某操作。 + +

    +

    + 以下代码段演示如何在使用 {@link android.content.CursorLoader} 查询一位原始联系人后创建一个“断言” +{@link android.content.ContentProviderOperation}: + +

    +
    +/*
    + * The application uses CursorLoader to query the raw contacts table. The system calls this method
    + * when the load is finished.
    + */
    +public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    +
    +    // Gets the raw contact's _ID and VERSION values
    +    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    +    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
    +}
    +
    +...
    +
    +// Sets up a Uri for the assert operation
    +Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
    +
    +// Creates a builder for the assert operation
    +ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
    +
    +// Adds the assertions to the assert operation: checks the version and count of rows tested
    +assertOp.withValue(SyncColumns.VERSION, mVersion);
    +assertOp.withExpectedCount(1);
    +
    +// Creates an ArrayList to hold the ContentProviderOperation objects
    +ArrayList ops = new ArrayList<ContentProviderOperationg>;
    +
    +ops.add(assertOp.build());
    +
    +// You would add the rest of your batch operations to "ops" here
    +
    +...
    +
    +// Applies the batch. If the assert fails, an Exception is thrown
    +try
    +    {
    +        ContentProviderResult[] results =
    +                getContentResolver().applyBatch(AUTHORITY, ops);
    +
    +    } catch (OperationApplicationException e) {
    +
    +        // Actions you want to take if the assert operation fails go here
    +    }
    +
    +

    通过 Intent 执行检索和修改

    +

    + 通过向设备的联系人应用发送 Intent,您可以间接访问联系人提供程序。 + Intent 会启动设备的联系人应用 UI,用户可以在其中执行与联系人有关的操作。 +通过这种访问方式,用户可以: +

      +
    • 从列表中选取一位联系人并将其返回给您的应用以执行进一步操作。
    • +
    • 编辑现有联系人的数据。
    • +
    • 为其任一帐户插入新原始联系人。
    • +
    • 删除联系人或联系人数据。
    • +
    +

    + 如果用户要插入或更新数据,您可以先收集数据,然后将其作为 Intent 的一部分发送。 + +

    +

    + 当您使用 Intent 通过设备的联系人应用访问联系人提供程序时,您无需自行编写用于访问该提供程序的 UI 或代码。 +您也无需请求对提供程序的读取或写入权限。 +设备的联系人应用可以将联系人读取权限授予给您,而且您是通过另一个应用对该提供程序进行修改,不需要拥有写入权限。 + + +

    +

    + 内容提供程序基础知识指南“通过 Intent 访问数据”部分详细描述了通过发送 Intent 来访问某提供程序的一般过程。 + +表 4 汇总了您为可用任务使用的操作、MIME 类型以及数据值,{@link android.provider.ContactsContract.Intents.Insert} 参考文档列出了您可用于{@link android.content.Intent#putExtra(String, String) putExtra()} 的 Extra 值: + + + + +

    +

    + 表 4. 联系人提供程序 Intent。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    任务操作数据MIME 类型备注
    从列表中选取一位联系人{@link android.content.Intent#ACTION_PICK} + 下列值之一: +
      +
    • +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI},显示联系人列表。 + +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI},显示原始联系人的电话号码列表。 + +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI},显示原始联系人的邮政地址列表。 + +
    • +
    • +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI},显示原始联系人的电子邮件地址列表。 + +
    • +
    +
    + 未使用 + + 显示原始联系人列表或一位原始联系人的数据列表,具体取决于您提供的内容 URI 类型。 + +

    + 调用 + {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} 方法,该方法返回所选行的内容 URI。 +该 URI 的形式为:追加有该行 LOOKUP_ID 的表的内容 URI。 + + 设备的联系人应用会在 Activity 的生命周期内将读取和写入权限授予给此内容 URI。 +如需了解更多详细信息,请参阅内容提供程序基础知识指南。 + + +

    +
    插入新原始联系人{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}不适用 + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE +RawContacts.CONTENT_TYPE},用于一组原始联系人的 MIME 类型。 + + 显示设备联系人应用的添加联系人屏幕。系统会显示您添加到 Intent 中的 Extra 值。 +如果是随 +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} + 发送,系统会将新添加的原始联系人的内容 URI 传回给 +{@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()} +回调方法并作为后者 {@link android.content.Intent} 参数的“data”字段。 +要获取该值,请调用 {@link android.content.Intent#getData()}。 +
    编辑联系人{@link android.content.Intent#ACTION_EDIT} + 该联系人的 {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}。 +该编辑器 Activity 让用户能够对任何与该联系人关联的数据进行编辑。 + + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE +Contacts.CONTENT_ITEM_TYPE},一位联系人。 + 显示联系人应用中的“编辑联系人”屏幕。系统会显示您添加到 Intent 中的 Extra 值。 +当用户点击完成保存编辑时,您的 Activity 会返回前台。 + +
    显示一个同样可以添加数据的选取器。{@link android.content.Intent#ACTION_INSERT_OR_EDIT} + 不适用 + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + + 此 Intent 始终显示联系人应用的选取器屏幕。用户可以选取要编辑的联系人,或添加新联系人。 +根据用户的选择,系统会显示编辑屏幕或添加屏幕,还会显示您使用 Intent 传递的 Extra 数据。 + +如果您的应用显示电子邮件或电话号码等联系人数据,请使用此 Intent 来允许用户向现有联系人添加数据。 + + +

    + 注:不需要通过此 Intent 的 Extra 发送姓名值,因为用户总是会选取现有姓名或添加新姓名。 +此外,如果您发送姓名,并且用户选择执行编辑操作,则联系人应用将显示您发送的姓名,该姓名将覆盖以前的值。 + +如果用户未注意这一情况便保存了编辑,原有值将会丢失。 + +

    +
    +

    + 设备的联系人应用不允许您使用 Intent 删除原始联系人或其任何数据。 +因此,要删除原始联系人,请使用 +{@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} +或 {@link android.content.ContentProviderOperation#newDelete(Uri) +ContentProviderOperation.newDelete()}。 +

    +

    + 以下代码段说明如何构建和发送一个插入新原始联系人和数据的 Intent: + +

    +
    +// Gets values from the UI
    +String name = mContactNameEditText.getText().toString();
    +String phone = mContactPhoneEditText.getText().toString();
    +String email = mContactEmailEditText.getText().toString();
    +
    +String company = mCompanyName.getText().toString();
    +String jobtitle = mJobTitle.getText().toString();
    +
    +// Creates a new intent for sending to the device's contacts application
    +Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
    +
    +// Sets the MIME type to the one expected by the insertion activity
    +insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
    +
    +// Sets the new contact name
    +insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
    +
    +// Sets the new company and job title
    +insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
    +insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
    +
    +/*
    + * Demonstrates adding data rows as an array list associated with the DATA key
    + */
    +
    +// Defines an array list to contain the ContentValues objects for each row
    +ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
    +
    +
    +/*
    + * Defines the raw contact row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues rawContactRow = new ContentValues();
    +
    +// Adds the account type and name to the row
    +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
    +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
    +
    +// Adds the row to the array
    +contactData.add(rawContactRow);
    +
    +/*
    + * Sets up the phone number data row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues phoneRow = new ContentValues();
    +
    +// Specifies the MIME type for this data row (all data rows must be marked by their type)
    +phoneRow.put(
    +        ContactsContract.Data.MIMETYPE,
    +        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
    +);
    +
    +// Adds the phone number and its type to the row
    +phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
    +
    +// Adds the row to the array
    +contactData.add(phoneRow);
    +
    +/*
    + * Sets up the email data row
    + */
    +
    +// Sets up the row as a ContentValues object
    +ContentValues emailRow = new ContentValues();
    +
    +// Specifies the MIME type for this data row (all data rows must be marked by their type)
    +emailRow.put(
    +        ContactsContract.Data.MIMETYPE,
    +        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
    +);
    +
    +// Adds the email address and its type to the row
    +emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
    +
    +// Adds the row to the array
    +contactData.add(emailRow);
    +
    +/*
    + * Adds the array to the intent's extras. It must be a parcelable object in order to
    + * travel between processes. The device's contacts app expects its key to be
    + * Intents.Insert.DATA
    + */
    +insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
    +
    +// Send out the intent to start the device's contacts app in its add contact activity.
    +startActivity(insertIntent);
    +
    +

    数据完整性

    +

    + 联系人存储库包含用户认为是正确且是最新的重要敏感数据,因此联系人提供程序具有规定清晰的数据完整性规则。 +您有责任在修改联系人数据时遵守这些规则。 +以下列出了其中的重要规则: + +

    +
    +
    + 务必为您添加的每个 {@link android.provider.ContactsContract.RawContacts} 行添加一个 {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 行。 + +
    +
    + 如果 {@link android.provider.ContactsContract.Data} 表中的 +{@link android.provider.ContactsContract.RawContacts} 行没有 +{@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 行,可能会在聚合时引发问题。 + +
    +
    + 务必将新 {@link android.provider.ContactsContract.Data} 行链接到其父 +{@link android.provider.ContactsContract.RawContacts} 行。 +
    +
    + 如果 {@link android.provider.ContactsContract.Data} 行未链接到 +{@link android.provider.ContactsContract.RawContacts},则其在设备的联系人应用中将处于不可见状态,而且这可能会导致同步适配器出现问题。 + +
    +
    + 请仅更改您拥有的那些原始联系人的数据。 +
    +
    + 请切记,联系人提供程序所管理的数据通常来自多个不同帐户类型/在线服务。 +您需要确保您的应用仅修改或删除归您所有的行的数据,并且仅通过您控制的帐户类型和帐户名称插入数据。 + + +
    +
    + 务必使用在 {@link android.provider.ContactsContract} 及其子类中为权限、内容 URI、URI 路径、列名称、MIME 类型以及 +{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} 值定义的常量。 + +
    +
    + 使用这些常量有助于您避免错误。如有任何常量被弃用,您还会从编译器警告收到通知。 + +
    +
    +

    自定义数据行

    +

    + 通过创建和使用自己的自定义 MIME 类型,您可以在 {@link android.provider.ContactsContract.Data} 表中插入、编辑、删除和检索您的自有数据行。 +这些行仅限使用 {@link android.provider.ContactsContract.DataColumns} 中定义的列,但您可以将您自己的类型专用列名称映射到默认列名称。 + + +在设备的联系人应用中,会显示这些行的数据,但无法对其进行编辑或删除,用户也无法添加其他数据。 + +要允许用户修改您的自定义数据行,您必须在自己的应用中提供编辑器 Activity。 + +

    +

    + 要显示您的自定义数据,请提供一个 contacts.xml 文件,其中须包含一个 +<ContactsAccountType> 元素,及其一个或多个 +<ContactsDataKind> 子元素。<ContactsDataKind> element 部分对此做了更详尽的描述。 + +

    +

    + 如需了解有关自定义 MIME 类型的更多信息,请阅读创建内容提供程序指南。 + + +

    +

    联系人提供程序同步适配器

    +

    + 联系人提供程序专门设计用于处理设备与在线服务之间的联系人数据同步。 +借助同步功能,用户可以将现有数据下载到新设备,以及将现有数据上传到新帐户。 + + 此外,同步还能确保用户掌握最新数据,无需考虑数据增加和更改的来源。 +同步的另一个优点是,即使设备未连接网络,联系人数据同样可用。 + +

    +

    + 虽然您可以通过各种方式实现同步,不过 Android 系统提供了一个插件同步框架,可自动化完成下列任务: + +

      + +
    • + 检查网络可用性。 +
    • +
    • + 根据用户偏好安排和执行同步。 +
    • +
    • + 重启已停止的同步。 +
    • +
    +

    + 要使用此框架,您需要提供一个同步适配器插件。每个同步适配器都专用于某个服务和内容提供程序,但可以处理同一服务的多个帐户名称。 +该框架还允许同一服务和提供程序具有多个同步适配器。 + +

    +

    同步适配器类和文件

    +

    + 您需要将同步适配器作为 +{@link android.content.AbstractThreadedSyncAdapter} 的子类进行实现,并作为 Android +应用的一部分进行安装。系统通过您的应用清单文件中的元素以及由清单文件指向的一个特殊 XML 文件了解有关同步适配器的信息。 +该 XML 文件定义在线服务的帐户类型和内容提供程序的权限,它们共同对适配器进行唯一标识。 + +用户为同步适配器的帐户类型添加一个帐户,并为与同步适配器同步的内容提供程序启用同步后,同步适配器才会激活。 + +激活后,系统将开始管理适配器,并在必要时调用它,以在内容提供程序与服务器之间同步数据。 + +

    +

    + 注:将帐户类型用作同步适配器标识的一部分让系统可以发现从同一组织访问不同服务的同步适配器,并将它们组合在一起。 + +例如,Google 在线服务的同步适配器都具有相同的帐户类型 com.google。 +当用户向其设备添加 Google 帐户时,已安装的所有 Google 服务同步适配器将一起列出;列出的每个同步适配器都与设备上不同的内容提供程序同步。 + + +

    +

    + 大多数服务都要求用户验证身份后才能访问数据,为此,Android 系统提供了一个身份验证框架,该框架与同步适配器框架类似,并且经常与其联用。 + +该身份验证框架使用的插件身份验证器是 +{@link android.accounts.AbstractAccountAuthenticator} 的子类。 +身份验证器通过下列步骤验证用户的身份: + +

      +
    1. + 收集用户名、用户密码或类似信息(用户的凭据)。 + +
    2. +
    3. + 将凭据发送给服务 +
    4. +
    5. + 检查服务的回复。 +
    6. +
    +

    + 如果服务接受了凭据,身份验证器便可存储凭据以供日后使用。 +由于插件身份验证器框架的存在,{@link android.accounts.AccountManager} 可以提供对身份验证器支持并选择公开的任何身份验证令牌(例如 OAuth2 身份验证令牌)的访问。 + + +

    +

    + 尽管身份验证并非必需,但大多数联系人服务都会使用它。 + 不过,您不一定要使用 Android 身份验证框架进行身份验证。 +

    +

    同步适配器实现

    +

    + 要为联系人提供程序实现同步适配器,您首先要创建一个包含以下内容的 +Android 应用: +

    +
    +
    + 一个 {@link android.app.Service} 组件,用于响应系统发出的绑定到同步适配器的请求。 + +
    +
    + 当系统想要运行同步时,它会调用服务的 +{@link android.app.Service#onBind(Intent) onBind()} 方法,为同步适配器获取一个 +{@link android.os.IBinder}。这样,系统便可跨进程调用适配器的方法。 + +

    + 在示例同步适配器示例应用中,该服务的类名是 com.example.android.samplesync.syncadapter.SyncService。 + + +

    +
    +
    + 作为 +{@link android.content.AbstractThreadedSyncAdapter} 具体子类实现的实际同步适配器。 +
    +
    + 此类的作用是从服务器下载数据、从设备上传数据以及解决冲突。 +适配器的主要工作是在方法 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( +Account, Bundle, String, ContentProviderClient, SyncResult) +onPerformSync()} 中完成的。 +必须将此类实例化为单一实例。 +

    + 在示例同步适配器示例应用中,同步适配器是在 com.example.android.samplesync.syncadapter.SyncAdapter 类中定义的。 + + +

    +
    +
    + {@link android.app.Application} 的子类。 +
    +
    + 此类充当同步适配器单一实例的工厂。使用 +{@link android.app.Application#onCreate()} 方法实例化同步适配器,并提供一个静态“getter”方法,使单一实例返回同步适配器服务的 +{@link android.app.Service#onBind(Intent) onBind()} 方法。 + + +
    +
    + 可选:一个 {@link android.app.Service} 组件,用于响应系统发出的用户身份验证请求。 + +
    +
    + {@link android.accounts.AccountManager} 会启动此服务以开始身份验证流程。 +该服务的 {@link android.app.Service#onCreate()} 方法会将一个身份验证器对象实例化。 +当系统想要对应用同步适配器的用户帐户进行身份验证时,它会调用该服务的 +{@link android.app.Service#onBind(Intent) onBind()} 方法,为该身份验证器获取一个 +{@link android.os.IBinder}。 +这样,系统便可跨进程调用身份验证器的方法。 + +

    + 在示例同步适配器示例应用中,该服务的类名是 com.example.android.samplesync.authenticator.AuthenticationService。 + + +

    +
    +
    + 可选:一个用于处理身份验证请求的 +{@link android.accounts.AbstractAccountAuthenticator} 具体子类。 + +
    +
    + {@link android.accounts.AccountManager} 就是调用此类所提供的方法向服务器验证用户的凭据。 +详细的身份验证过程会因服务器所采用技术的不同而有很大差异。 +您应该参阅服务器软件的文档,了解有关身份验证的更多信息。 + +

    + 在示例同步适配器示例应用中,身份验证器是在 com.example.android.samplesync.authenticator.Authenticator 类中定义的。 + + +

    +
    +
    + 用于定义系统同步适配器和身份验证器的 XML 文件。 +
    +
    + 之前描述的同步适配器和身份验证器服务组件都是在应用清单文件中的 +<service> +元素内定义的。 +这些元素包含以下用于向系统提供特定数据的 +<meta-data> +子元素: + + +
      +
    • + 同步适配器服务的 +<meta-data> +元素指向 +XML 文件 res/xml/syncadapter.xml。而该文件则指定将与联系人提供程序同步的 Web 服务的 URI,以及指定该 Web 服务的帐户类型。 + + +
    • +
    • + 可选:身份验证器的 +<meta-data> +元素指向 XML 文件 +res/xml/authenticator.xml。而该文件则指定此身份验证器所支持的帐户类型,以及指定身份验证过程中出现的 UI 资源。 + +在此元素中指定的帐户类型必须与为同步适配器指定的帐户类型相同。 + + +
    • +
    +
    +
    +

    社交流数据

    +

    + {@code android.provider.ContactsContract.StreamItems} 表和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表管理来自社交网络的传入数据。 +您可以编写一个同步适配器,用其将您自己社交网络中的流数据添加到这些表中,也可以从这些表读取流数据并将其显示在您的自有应用中,或者同时采用这两种方法。 + +利用这些功能,可以将您的社交网络服务和应用集成到 Android 的社交网络体验之中。 + +

    +

    社交流文本

    +

    + 流项目始终与原始联系人关联。 +{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} 链接到原始联系人的 _ID 值。 +原始联系人的帐户类型和帐户名称也存储在流项目行中。 + +

    +

    + 将您的流数据存储在以下列: +

    +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} +
    +
    + 必备。与该流项目关联的原始联系人对应的用户帐户类型。 +请记得在插入流项目时设置此值。 +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} +
    +
    + 必备。与该流项目关联的原始联系人对应的用户帐户名称。 +请记得在插入流项目时设置此值。 +
    +
    + 标识符列 +
    +
    + 必备。您必须在插入流项目时插入下列标识符列: + +
      +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}:此流项目关联的联系人的 +{@code android.provider.BaseColumns#_ID} 值。 + +
    • +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}:此流项目关联的联系人的 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 值。 + +
    • +
    • + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}:此流项目关联的原始联系人的 +{@code android.provider.BaseColumns#_ID} 值。 + +
    • +
    +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} +
    +
    + 可选。存储可在流项目开头显示的摘要信息。 +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} +
    +
    + 流项目的文本,或为项目来源发布的内容,或是对生成流项目的某项操作的描述。 +此列可包含可由 +{@link android.text.Html#fromHtml(String) fromHtml()} 渲染的任何格式设置和嵌入式资源图像。 +提供程序可能会截断或省略较长内容,但它会尽力避免破坏标记。 + +
    +
    + {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} +
    +
    + 一个包含流项目插入时间或更新时间的文本字符串,以从公元纪年开始计算的毫秒数形式表示。 +此列由插入或更新流项目的应用负责维护;联系人提供程序不会自动对其进行维护。 + + +
    +
    +

    + 要显示您的流项目的标识信息,请使用 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}、 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} 和 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} 链接到您的应用中的资源。 + +

    +

    + {@code android.provider.ContactsContract.StreamItems} 表还包含供同步适配器专用的列 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} 至 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4}。 + +

    +

    社交流照片

    +

    + {@code android.provider.ContactsContract.StreamItemPhotos} 表存储与流项目关联的照片。 +该表的 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID}列链接到 {@code android.provider.ContactsContract.StreamItems} 表 {@code android.provider.BaseColumns#_ID} 列中的值。 + +照片引用存储在表中的以下列: + +

    +
    +
    + {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} 列(一个二进制大型对象)。 +
    +
    + 照片的二进制表示,为便于存储和显示,由提供程序调整了尺寸。 + 此列可用于向后兼容使用它来存储照片的旧版本联系人提供程序。 +不过,在当前版本中,您不应使用此列来存储照片, +而应使用 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} 或 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI}(下文对两者都做了描述)将照片存储在一个文件内。 +此列现在包含可用于读取的照片缩略图。 + +
    +
    + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} +
    +
    + 原始联系人照片的数字标识符。将此值追加到常量 +{@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI},获取指向单一照片文件的内容 URI,然后调用 +{@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 来获取照片文件的句柄。 + +
    +
    + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} +
    +
    + 一个内容 URI,直接指向此行所表示的照片的照片文件。 + 通过此 URI 调用 {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 以获得照片文件的句柄。 +
    +
    +

    使用社交流表

    +

    + 这些表的工作方式与联系人提供程序中的其他主表基本相同,不同的是: +

    +
      +
    • + 这些表需要额外的访问权限。要读取它们的数据,您的应用必须具有 {@code android.Manifest.permission#READ_SOCIAL_STREAM} 权限。 +要修改它们,您的应用必须具有 +{@code android.Manifest.permission#WRITE_SOCIAL_STREAM} 权限。 + +
    • +
    • + 对于 {@code android.provider.ContactsContract.StreamItems} 表,为每一位原始联系人存储的行数有限。 +一旦达到该限制,联系人提供程序即会自动删除 +{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} 最早的行,为新流项目行腾出空间。 + +要获取该限制,请发出对内容 URI +{@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI} 的查询。 +您可以将内容 URI 以外的所有其他参数保持设置为 null。 +查询会返回一个 Cursor,其中包含一行,并且只有 +{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS} 一列。 + +
    • +
    + +

    + {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} 类定义了 + {@code android.provider.ContactsContract.StreamItemPhotos} 的一个子表,其中包含某个流项目的照片行。 + +

    +

    社交流交互

    +

    + 通过将联系人提供程序管理的社交流数据与设备的联系人应用相结合,可以在您的社交网络系统与现有联系人之间建立起有效的连接。 + +这种结合实现了下列功能: +

    +
      +
    • + 您可以通过同步适配器让您的社交网络服务与联系人提供程序同步,检索用户联系人的近期 Activity,并将其存储在 + {@code android.provider.ContactsContract.StreamItems} 表和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表中,以供日后使用。 + +
    • +
    • + 除了定期同步外,您还可以在用户选择某位联系人进行查看时触发您的同步适配器以检索更多数据。 +这样,您的同步适配器便可检索该联系人的高分辨率照片和最近流项目。 + +
    • +
    • + 通过在设备的联系人应用以及联系人提供程序中注册通知功能,您可以在用户查看联系人时收到一个 Intent,并在那时通过您的服务更新联系人的状态。 + +与通过同步适配器执行完全同步相比,此方法可能更快速,占用的带宽也更少。 + +
    • +
    • + 用户可以在查看设备联系人应用中的联系人时,将其添加到您的社交网络服务。 +您可以通过“邀请联系人”功能实现此目的,而该功能则是通过将 Activity 与 XML 文件结合使用来实现的,前者将现有联系人添加到您的社交网络,后者为设备的联系人应用以及联系人提供程序提供有关您的应用的详细信息。 + + + +
    • +
    +

    + 流项目与联系人提供程序的定期同步与其他同步相同。 +如需了解有关同步的更多信息,请参阅 +联系人提供程序同步适配器部分。接下来的两节介绍如何注册通知和邀请联系人。 + +

    +

    通过注册处理社交网络查看

    +

    + 要注册您的同步适配器,以便在用户查看由您的同步适配器管理的联系人时收到通知,请执行以下步骤: + +

    +
      +
    1. + 在您项目的 res/xml/ 目录中创建一个名为 contacts.xml 的文件。 +如果您已有该文件,可跳过此步骤。 +
    2. +
    3. + 在该文件中添加元素 +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 + 如果该元素已存在,可跳过此步骤。 +
    4. +
    5. + 要注册一项服务,以便在用户于设备的联系人应用中打开某位联系人的详细信息页面时通知该服务,请为该元素添加 +viewContactNotifyService="serviceclass" 属性,其中 +serviceclass 是该服务的完全限定类名,应由该服务接收来自设备联系人应用的 Intent。 + +对于这个通知程序服务,请使用一个扩展 {@link android.app.IntentService} 的类,以让该服务能够接收 Intent。 + +传入 Intent 中的数据包含用户点击的原始联系人的内容 URI。 +您可以通过通知程序服务绑定到您的同步适配器,然后调用同步适配器来更新原始联系人的数据。 + +
    6. +
    +

    + 要注册需要在用户点击流项目或照片(或同时点击这两者)时调用的 Activity,请执行以下步骤: +

    +
      +
    1. + 在您项目的 res/xml/ 目录中创建一个名为 contacts.xml 的文件。 +如果您已有该文件,可跳过此步骤。 +
    2. +
    3. + 在该文件中添加元素 +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 + 如果该元素已存在,可跳过此步骤。 +
    4. +
    5. + 要注册某个 Activity,以处理用户在设备联系人应用中点击某个流项目的操作,请为该元素添加 +viewStreamItemActivity="activityclass" 属性,其中 +activityclass 是该 Activity 的完全限定类名,应由该 Activity 接收来自设备联系人应用的 Intent。 + + +
    6. +
    7. + 要注册某个 Activity,以处理用户在设备联系人应用中点击某个流照片的操作,请为该元素添加 +viewStreamItemPhotoActivity="activityclass" 属性,其中 +activityclass 是该 Activity 的完全限定类名,应由该 Activity 接收来自设备联系人应用的 Intent。 + + +
    8. +
    +

    + <ContactsAccountType> 元素部分对 <ContactsAccountType> 元素做了更详尽的描述。 + +

    +

    + 传入 Intent 包含用户点击的项目或照片的内容 URI。 + 要让文本项目和照片具有独立的 Activity,请在同一文件中使用这两个属性。 +

    +

    与您的社交网络服务交互

    +

    + 用户不必为了邀请联系人到您的社交网络网站而离开设备的联系人应用。 +取而代之是,您可以让设备的联系人应用发送一个 Intent,将联系人 +邀请到您的 Activity 之一。要设置此功能,请执行以下步骤: +

    +
      +
    1. + 在您项目的 res/xml/ 目录中创建一个名为 contacts.xml 的文件。 +如果您已有该文件,可跳过此步骤。 +
    2. +
    3. + 在该文件中添加元素 +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">。 + 如果该元素已存在,可跳过此步骤。 +
    4. +
    5. + 添加以下属性: +
        +
      • inviteContactActivity="activityclass"
      • +
      • + inviteContactActionLabel="@string/invite_action_label" +
      • +
      + activityclass 值是应该接收该 Intent 的 Activity 的完全限定类名。 +invite_action_label +值是一个文本字符串,将显示在设备联系人应用的 Add Connection 菜单中。 + +
    6. +
    +

    + 注:ContactsSource 是 +ContactsAccountType 的一个已弃用的标记名称。 +

    +

    contacts.xml 引用

    +

    + 文件 contacts.xml 包含一些 XML 元素,这些元素控制您的同步适配器和应用与联系人应用及联系人提供程序的交互。 +下文对这些元素做了描述。 + +

    +

    <ContactsAccountType> 元素

    +

    + <ContactsAccountType> 元素控制您的应用与联系人应用的交互。 +它采用了以下语法: +

    +
    +<ContactsAccountType
    +        xmlns:android="http://schemas.android.com/apk/res/android"
    +        inviteContactActivity="activity_name"
    +        inviteContactActionLabel="invite_command_text"
    +        viewContactNotifyService="view_notify_service"
    +        viewGroupActivity="group_view_activity"
    +        viewGroupActionLabel="group_action_text"
    +        viewStreamItemActivity="viewstream_activity_name"
    +        viewStreamItemPhotoActivity="viewphotostream_activity_name">
    +
    +

    + 包含它的文件: +

    +

    + res/xml/contacts.xml +

    +

    + 可能包含的内容: +

    +

    + <ContactsDataKind> +

    +

    + 描述: +

    +

    + 声明 Android 组件和 UI 标签,让用户能够邀请他们的一位联系人加入社交网络,在他们的某个社交网络流更新时通知用户,以及执行其他操作。 + + +

    +

    + 请注意,对 <ContactsAccountType> 的属性而言,属性前缀 android: 并非必需的。 + +

    +

    + 属性: +

    +
    +
    {@code inviteContactActivity}
    +
    + 您的应用中某个 Activity 的完全限定类名,您想要在用户于设备的联系人应用中选择 Add connection 时激活该 Activity。 + + +
    +
    {@code inviteContactActionLabel}
    +
    + Add connection 菜单中为 +{@code inviteContactActivity} 中指定的 Activity 显示的文本字符串。 + 例如,您可以使用字符串“Follow in my network”。您可以为此标签使用字符串资源标识符。 + +
    +
    {@code viewContactNotifyService}
    +
    + 您的应用中某项服务的完全限定类名,当用户查看联系人时,应由该服务接收通知。 +此通知由设备的联系人应用发送;您的应用可以根据通知将数据密集型操作推迟到必要时再执行。 + +例如,您的应用对此通知的响应可以是:读入并显示联系人的高分辨率照片和最近的社交流项目。 + +社交流交互部分对此功能做了更详尽的描述。 +您可以在 +SampleSyncAdapter 示例应用的 NotifierService.java 文件中查看通知服务的示例。 + + +
    +
    {@code viewGroupActivity}
    +
    + 您的应用中某个可显示组信息的 Activity 的完全限定类名。 +当用户点击设备联系人应用中的组标签时,将显示此 Activity 的 UI。 + +
    +
    {@code viewGroupActionLabel}
    +
    + 联系人应用为某个 UI 控件显示的标签,用户可通过该控件查看您的应用中的组。 + +

    + 例如,如果您在设备上安装了 Google+ 应用,并将 +Google+ 与联系人应用同步,就会看到 Google+ 圈子以组的形式出现在您的联系人应用的 Groups 选项卡内。 +如果您点击某个 +Google+ 圈子,就会看到该圈子内的联系人以“组”的形式列出。在该显示页面的顶部,您会看到一个 Google+ 图标;如果您点击它,控制权将切换给 +Google+ 应用。联系人应用以 Google+ 图标作为 {@code viewGroupActionLabel} 的值,通过 +{@code viewGroupActivity} 来实现此目的。 + + +

    +

    + 允许使用字符串资源标识符作为该属性的值。 +

    +
    +
    {@code viewStreamItemActivity}
    +
    + 您的应用中某个 Activity 的完全限定类名,设备的联系人应用会在用户点击原始联系人的流项目时启动该 Activity。 + +
    +
    {@code viewStreamItemPhotoActivity}
    +
    + 您的应用中某个 Activity 的完全限定类名,设备的联系人应用会在用户点击原始联系人流项目中的照片时启动该 Activity。 + + +
    +
    +

    <ContactsDataKind> 元素

    +

    + <ContactsDataKind> 元素控制您的应用的自定义数据行在联系人应用 UI 中的显示。它采用了以下语法: + +

    +
    +<ContactsDataKind
    +        android:mimeType="MIMEtype"
    +        android:icon="icon_resources"
    +        android:summaryColumn="column_name"
    +        android:detailColumn="column_name">
    +
    +

    + 包含它的文件: +

    +<ContactsAccountType> +

    + 描述: +

    +

    + 此元素用于让联系人应用将自定义数据行的内容显示为原始联系人详细信息的一部分。 +<ContactsAccountType> 的每个 <ContactsDataKind> 子元素都代表您的同步适配器向 {@link android.provider.ContactsContract.Data} 表添加的某个自定义数据行类型。 + +请为您使用的每个自定义 MIME 类型添加一个 <ContactsDataKind> 元素。 +如果您不想显示任何自定义数据行的数据,则无需添加该元素。 + +

    +

    + 属性: +

    +
    +
    {@code android:mimeType}
    +
    + 您为 +{@link android.provider.ContactsContract.Data} 表中某个自定义数据行类型定义的自定义 MIME 类型。例如,可将值 +vnd.android.cursor.item/vnd.example.locationstatus 作为记录联系人最后已知位置的数据行的自定义 MIME 类型。 + +
    +
    {@code android:icon}
    +
    + 联系人应用在您的数据旁显示的 Android + Drawable资源。 +它用于向用户指示数据来自您的服务。 + +
    +
    {@code android:summaryColumn}
    +
    + 从数据行检索的两个值中第一个值的列名。该值显示为该数据行的第一个输入行。 +第一行专用作数据摘要,不过它是可选项。 +另请参阅 +android:detailColumn。 +
    +
    {@code android:detailColumn}
    +
    + 从数据行检索的两个值中第二个值的列名。该值显示为该数据行的第二个输入行。 +另请参阅 +{@code android:summaryColumn}。 +
    +
    +

    其他联系人提供程序功能

    +

    + 除了上文描述的主要功能外,联系人提供程序还为处理联系人数据提供了下列有用的功能: + +

    +
      +
    • 联系人组
    • +
    • 照片功能
    • +
    +

    联系人组

    +

    + 联系人提供程序可以选择性地为相关联系人集合添加数据标签。 +如果与某个用户帐户关联的服务器想要维护组,则与该帐户的帐户类型对应的同步适配器应在联系人提供程序与服务器之间传送组数据。 + +当用户向服务器添加一个新联系人,然后将该联系人放入一个新组时,同步适配器必须将这个新组添加到 + {@link android.provider.ContactsContract.Groups} 表中。 +原始联系人所属的一个或多个组使用 {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} MIME 类型存储在 {@link android.provider.ContactsContract.Data} 表内。 + + +

    +

    + 如果您设计的同步适配器会将服务器中的原始联系人数据添加到联系人提供程序,并且您不使用组,则需要指示提供程序让您的数据可见。 + +在用户向设备添加帐户时执行的代码中,更新联系人提供程序为该帐户添加的 {@link android.provider.ContactsContract.Settings} +行。 +在该行中,将 +{@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE +Settings.UNGROUPED_VISIBLE} 列的值设置为 1。执行此操作后,即使您不使用组,联系人提供程序也会让您的联系人数据始终可见。 + +

    +

    联系人照片

    +

    + {@link android.provider.ContactsContract.Data} 表通过 MIME 类型 +{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 以行的形式存储照片。该行的 +{@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} 列链接到其所属原始联系人的 +{@code android.provider.BaseColumns#_ID} 列。 + {@link android.provider.ContactsContract.Contacts.Photo} 类定义了一个 +{@link android.provider.ContactsContract.Contacts} 子表,其中包含联系人主要照片(联系人的主要原始联系人的主要照片)的照片信息。 +同样, +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 类定义了一个 {@link android.provider.ContactsContract.RawContacts} 子表,其中包含原始联系人主要照片的照片信息。 + + +

    +

    + {@link android.provider.ContactsContract.Contacts.Photo} 和 +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 参考文档包含检索照片信息的示例。 +并没有可用来检索原始联系人主要缩略图的实用类,但您可以向 +{@link android.provider.ContactsContract.Data} 表发送查询,从而通过选定原始联系人的 +{@code android.provider.BaseColumns#_ID}、 +{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 以及 {@link android.provider.ContactsContract.Data#IS_PRIMARY} +列,找到原始联系人的主要照片行。 + +

    +

    + 联系人的社交流数据也可能包含照片。这些照片存储在 +{@code android.provider.ContactsContract.StreamItemPhotos} 表中,社交流照片部分对该表做了更详尽的描述。 + +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 0000000000000000000000000000000000000000..4c91d3a19c96cda97a775d370d873df1a641461d --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=内容提供程序基础知识 +@jd:body +
    +
    + +

    本文内容

    +
      +
    1. + 概览 +
        +
      1. + 访问提供程序 +
      2. +
      3. + 内容 URI +
      4. +
      +
    2. +
    3. + 从提供程序检索数据 +
        +
      1. + 请求读取访问权限 +
      2. +
      3. + 构建查询 +
      4. +
      5. + 显示查询结果 +
      6. +
      7. + 从查询结果中获取数据 +
      8. +
      +
    4. +
    5. + 内容提供程序权限 +
    6. +
    7. + 插入、更新和删除数据 +
        +
      1. + 插入数据 +
      2. +
      3. + 更新数据 +
      4. +
      5. + 删除数据 +
      6. +
      +
    8. +
    9. + 提供程序数据类型 +
    10. +
    11. + 提供程序访问的替代形式 +
        +
      1. + 批量访问 +
      2. +
      3. + 通过 Intent 访问数据 +
      4. +
      +
    12. +
    13. + 协定类 +
    14. +
    15. + MIME 类型引用 +
    16. +
    + + +

    关键类

    +
      +
    1. + {@link android.content.ContentProvider} +
    2. +
    3. + {@link android.content.ContentResolver} +
    4. +
    5. + {@link android.database.Cursor} +
    6. +
    7. + {@link android.net.Uri} +
    8. +
    + + +

    相关示例

    +
      +
    1. + +游标(联系人) +
    2. +
    3. + +游标(电话) +
    4. +
    + + +

    另请参阅

    +
      +
    1. + +创建内容提供程序 +
    2. +
    3. + +日历提供程序 +
    4. +
    +
    +
    + + +

    + 内容提供程序管理对中央数据存储库的访问。提供程序是 Android 应用的一部分,通常提供自己的 UI 来使用数据。 + +但是,内容提供程序主要旨在供其他应用使用,这些应用使用提供程序客户端对象来访问提供程序。 +提供程序与提供程序客户端共同提供一致的标准数据界面,该界面还可处理跨进程通信并保护数据访问的安全性。 + + +

    +

    + 本主题介绍了以下基础知识: +

    +
      +
    • 内容提供程序的工作方式。
    • +
    • 用于从内容提供程序检索数据的 API。
    • +
    • 用于在内容提供程序中插入、更新或删除数据的 API。
    • +
    • 其他有助于使用提供程序的 API 功能。
    • +
    + + +

    概览

    +

    + 内容提供程序以一个或多个表(与在关系数据库中找到的表类似)的形式将数据呈现给外部应用。 +行表示提供程序收集的某种数据类型的实例,行中的每个列表示为实例收集的每条数据。 + + +

    +

    + 例如,Android 平台的内置提供程序之一是用户字典,它会存储用户想要保存的非标准字词的拼写。 +表 1 描述了数据在此提供程序表中的显示情况: + +

    +

    + 表 1:用户字典示例表格。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    字词应用 id频率区域设置_ID
    mapreduceuser1100en_US1
    precompileruser14200fr_FR2
    appletuser2225fr_CA3
    constuser1255pt_BR4
    intuser5100en_UK5
    +

    + 在表 1 中,每行表示可能无法在标准词典中找到的字词实例。 +每列表示该字词的某些数据,如该字词首次出现时的区域设置。 +列标题是存储在提供程序中的列名称。 +要引用行的区域设置,需要引用其 locale 列。对于此提供程序,_ID 列充当由提供程序自动维护的“主键”列。 + + +

    +

    + :提供程序无需具有主键,也无需将 _ID 用作其主键的列名称(如果存在主键)。 +但是,如果您要将来自提供程序的数据与 {@link android.widget.ListView} 绑定,则其中一个列名称必须是 _ID。 + +显示查询结果部分详细说明了此要求。 + +

    +

    访问提供程序

    +

    + 应用从具有 {@link android.content.ContentResolver} 客户端对象的内容提供程序访问数据。 +此对象具有调用提供程序对象({@link android.content.ContentProvider} 的某个具体子类的实例)中同名方法的方法。 + + +{@link android.content.ContentResolver} 方法可提供持续存储的基本“CRUD”(创建、检索、更新和删除)功能。 + +

    +

    + 客户端应用进程中的 {@link android.content.ContentResolver} 对象和拥有提供程序的应用中的 {@link android.content.ContentProvider} 对象可自动处理跨进程通信。 +{@link android.content.ContentProvider} 还可充当其数据存储库和表格形式的数据外部显示之间的抽象层。 + + + +

    +

    + :要访问提供程序,您的应用通常需要在其清单文件中请求特定权限。 +内容提供程序权限部分详细介绍了此内容。 + +

    +

    + 例如,要从用户字典提供程序中获取字词及其区域设置的列表,则需调用 {@link android.content.ContentResolver#query ContentResolver.query()}。 + + {@link android.content.ContentResolver#query query()} 方法会调用用户字典提供程序所定义的 +{@link android.content.ContentProvider#query ContentProvider.query()} 方法。 +以下代码行显示了 +{@link android.content.ContentResolver#query ContentResolver.query()} 调用: +

    +

    +// Queries the user dictionary and returns results
    +mCursor = getContentResolver().query(
    +    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    +    mProjection,                        // The columns to return for each row
    +    mSelectionClause                    // Selection criteria
    +    mSelectionArgs,                     // Selection criteria
    +    mSortOrder);                        // The sort order for the returned rows
    +
    +

    + 表 2 显示了 + {@link android.content.ContentResolver#query + query(Uri,projection,selection,selectionArgs,sortOrder)} 的参数如何匹配 SQL SELECT 语句: +

    +

    + 表 2:Query() 与 SQL 查询对比。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    query() 参数SELECT 关键字/参数备注
    UriFROM table_nameUri 映射至名为 table_name 的提供程序中的表。
    projectioncol,col,col,... + projection 是应该为检索到的每个行包含的列的数组。 + +
    selectionWHERE col = valueselection 会指定选择行的条件。
    selectionArgs + (没有完全等效项。选择参数会替换选择子句中 ? 的占位符。) + +
    sortOrderORDER BY col,col,... + sortOrder 指定行在返回的 + {@link android.database.Cursor} 中的显示顺序。 +
    +

    内容 URI

    +

    + 内容 URI 是用于在提供程序中标识数据的 URI。内容 URI 包括整个提供程序的符号名称(其权限)和一个指向表的名称(路径)。 + +当您调用客户端方法来访问提供程序中的表时,该表的内容 URI 将是其参数之一。 + + +

    +

    + 在前面的代码行中,常量 + {@link android.provider.UserDictionary.Words#CONTENT_URI} 包含用户字典的“字词”表的内容 URI。 +{@link android.content.ContentResolver} + 对象会分析出 URI 的授权,并通过将该授权与已知提供程序的系统表进行比较,来“解析”提供程序。 +然后, +{@link android.content.ContentResolver} 可以将查询参数分派给正确的提供程序。 + +

    +

    + {@link android.content.ContentProvider} 使用内容 URI 的路径部分来选择要访问的表。 +提供程序通常会为其公开的每个表显示一条路径。 +

    +

    + 在前面的代码行中,“字词”表的完整 URI 是: +

    +
    +content://user_dictionary/words
    +
    +

    + 其中,user_dictionary 字符串是提供程序的授权, +words 字符串是表的路径。字符串 + content://架构)始终显示,并将此标识为内容 URI。 + +

    +

    + 许多提供程序都允许您通过将 ID 值追加到 URI 末尾来访问表中的单个行。例如,要从用户字典中检索 _ID 为 + 4 的行,则可使用此内容 URI: + +

    +
    +Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
    +
    +

    + 在检索到一组行后想要更新或删除其中某一行时通常会用到 ID 值。 + +

    +

    + :{@link android.net.Uri} 和 {@link android.net.Uri.Builder} 类 +包含根据字符串构建格式规范的 URI 对象的便利方法。 +{@link android.content.ContentUris} 包含一些可以将 ID 值轻松追加到 + URI 后的方法。前面的代码段就是使用 {@link android.content.ContentUris#withAppendedId + withAppendedId()} 将 ID 追加到用户字典内容 URI 后。 +

    + + + +

    从提供程序检索数据

    +

    + 本节将以用户字典提供程序为例,介绍如何从提供程序中检索数据。 + +

    +

    + 为了明确进行说明,本节中的代码段将在“UI 线程”上调用 + {@link android.content.ContentResolver#query ContentResolver.query()}。但在实际代码中,您应该在单独线程上异步执行查询。 +执行此操作的方式之一是使用 {@link android.content.CursorLoader} 类,加载器指南中对此有更为详细的介绍。 + + +此外,前述代码行只是代码段;它们不会显示整个应用。 + +

    +

    + 要从提供程序中检索数据,请按照以下基本步骤执行操作: +

    +
      +
    1. + 请求对提供程序的读取访问权限。 +
    2. +
    3. + 定义将查询发送至提供程序的代码。 +
    4. +
    +

    请求读取访问权限

    +

    + 要从提供程序检索数据,您的应需要具备对提供程序的“读取访问”权限。 +您无法在运行时请求此权限;相反,您需要使用<uses-permission>元素和提供程序定义的准确权限名称,在清单文件中指明您需要此权限。 + + + +在您的清单文件中指定此元素后,您将有效地为应用“请求”此权限。 +用户安装您的应用时,会隐式授予允许此请求。 + +

    +

    + 要找出您正在使用的提供程序的读取访问权限的准确名称,以及提供程序使用的其他访问权限的名称,请查看提供程序的文档。 + + +

    +

    + 内容提供程序权限部分详细介绍了权限在访问提供程序过程中的作用。 + +

    +

    + 用户字典提供程序在其清单文件中定义了权限 + android.permission.READ_USER_DICTIONARY,因此希望从提供程序中进行读取的应用必需请求此权限。 + +

    + +

    构建查询

    +

    + 从提供程序中检索数据的下一步是构建查询。第一个代码段定义某些用于访问用户字典提供程序的变量: + +

    +
    +
    +// A "projection" defines the columns that will be returned for each row
    +String[] mProjection =
    +{
    +    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    +    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    +    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
    +};
    +
    +// Defines a string to contain the selection clause
    +String mSelectionClause = null;
    +
    +// Initializes an array to contain selection arguments
    +String[] mSelectionArgs = {""};
    +
    +
    +

    + 下一个代码段以用户字典提供程序为例,显示了如何使用 + {@link android.content.ContentResolver#query ContentResolver.query()}。 +提供程序客户端查询与 SQL 查询类似,并且包含一组要返回的列、一组选择条件和排序顺序。 + +

    +

    + 查询应该返回的列集被称为投影(变量 mProjection)。 + +

    +

    + 用于指定要检索的行的表达式分割为选择子句和选择参数。 +选择子句是逻辑和布尔表达式、列名称和值(变量 mSelectionClause)的组合。 +如果您指定了可替换参数 ? 而非值,则查询方法会从选择参数数组(变量 mSelectionArgs)中检索值。 + + +

    +

    + 在下一个代码段中,如果用户未输入字词,则选择子句将设置为 null,而且查询会返回提供程序中的所有字词。 +如果用户输入了字词,选择子句将设置为 UserDictionary.Words.WORD + " = ?" 且选择参数数组的第一个元素将设置为用户输入的字词。 + + +

    +
    +/*
    + * This defines a one-element String array to contain the selection argument.
    + */
    +String[] mSelectionArgs = {""};
    +
    +// Gets a word from the UI
    +mSearchString = mSearchWord.getText().toString();
    +
    +// Remember to insert code here to check for invalid or malicious input.
    +
    +// If the word is the empty string, gets everything
    +if (TextUtils.isEmpty(mSearchString)) {
    +    // Setting the selection clause to null will return all words
    +    mSelectionClause = null;
    +    mSelectionArgs[0] = "";
    +
    +} else {
    +    // Constructs a selection clause that matches the word that the user entered.
    +    mSelectionClause = UserDictionary.Words.WORD + " = ?";
    +
    +    // Moves the user's input string to the selection arguments.
    +    mSelectionArgs[0] = mSearchString;
    +
    +}
    +
    +// Does a query against the table and returns a Cursor object
    +mCursor = getContentResolver().query(
    +    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    +    mProjection,                       // The columns to return for each row
    +    mSelectionClause                   // Either null, or the word the user entered
    +    mSelectionArgs,                    // Either empty, or the string the user entered
    +    mSortOrder);                       // The sort order for the returned rows
    +
    +// Some providers return null if an error occurs, others throw an exception
    +if (null == mCursor) {
    +    /*
    +     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
    +     * call android.util.Log.e() to log this error.
    +     *
    +     */
    +// If the Cursor is empty, the provider found no matches
    +} else if (mCursor.getCount() < 1) {
    +
    +    /*
    +     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
    +     * an error. You may want to offer the user the option to insert a new row, or re-type the
    +     * search term.
    +     */
    +
    +} else {
    +    // Insert code here to do something with the results
    +
    +}
    +
    +

    + 此查询与 SQL 语句相似: +

    +
    +SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
    +
    +

    + 在此 SQL 语句中,会使用实际的列名称而非协定类常量。 +

    +

    防止恶意输入

    +

    + 如果内容提供程序管理的数据位于 SQL 数据库中,将不受信任的外部数据包括在原始 SQL 语句中可能会导致 SQL 注入。 + +

    +

    + 考虑此选择子句: +

    +
    +// Constructs a selection clause by concatenating the user's input to the column name
    +String mSelectionClause =  "var = " + mUserInput;
    +
    +

    + 如果您执行此操作,则会允许用户将恶意 SQL 串连到 SQL 语句上。 + 例如,用户可以为 mUserInput 输入“nothing; DROP TABLE *;”,这会生成选择子句 var = nothing; DROP TABLE *;。 +由于选择子句是作为 SQL 语句处理,因此这可能会导致提供程序擦除基础 SQLite 数据库中的所有表(除非提供程序设置为可捕获 SQL 注入尝试)。 + + + +

    +

    + 要避免此问题,可使用一个用于将 ? 作为可替换参数的选择子句以及一个单独的选择参数数组。 +执行此操作时,用户输入直接受查询约束,而不解释为 SQL 语句的一部分。 + + 由于用户输入未作为 SQL 处理,因此无法注入恶意 SQL。请使用此选择子句,而不要使用串连来包括用户输入: + +

    +
    +// Constructs a selection clause with a replaceable parameter
    +String mSelectionClause =  "var = ?";
    +
    +

    + 按如下所示设置选择参数数组: +

    +
    +// Defines an array to contain the selection arguments
    +String[] selectionArgs = {""};
    +
    +

    + 按如下所示将值置于选择参数数组中: +

    +
    +// Sets the selection argument to the user's input
    +selectionArgs[0] = mUserInput;
    +
    +

    + 一个用于将 ? 用作可替换参数的选择子句和一个选择参数数组是指定选择的首选方式,即使提供程序并未基于 SQL 数据库。 + + +

    + +

    显示查询结果

    +

    + {@link android.content.ContentResolver#query ContentResolver.query()} 客户端方法始终会返回符合以下条件的 {@link android.database.Cursor}:包含查询的投影为匹配查询选择条件的行指定的列。 + + +{@link android.database.Cursor} 对象为其包含的行和列提供随机读取访问权限。 +通过使用 {@link android.database.Cursor} 方法,您可以循环访问结果中的行、确定每个列的数据类型、从列中获取数据,并检查结果的其他属性。 + +某些 {@link android.database.Cursor} 实现会在提供程序的数据发生更改时自动更新对象和/或在 {@link android.database.Cursor} 更改时触发观察程序对象中的方法。 + + +

    +

    + :提供程序可能会根据发出查询的对象的性质来限制对列的访问。 +例如,联系人提供程序会限定只有同步适配器才能访问某些列,因此不会将它们返回至 Activity 或服务。 + +

    +

    + 如果没有与选择条件匹配的行,则提供程序会返回 {@link android.database.Cursor#getCount Cursor.getCount()} 为 0(空游标)的 {@link android.database.Cursor} 对象。 + + +

    +

    + 如果出现内部错误,查询结果将取决于具体的提供程序。它可能会选择返回 null,或抛出 {@link java.lang.Exception}。 + +

    +

    + 由于 {@link android.database.Cursor} 是行“列表”,因此显示 {@link android.database.Cursor} 内容的良好方式是通过 {@link android.widget.SimpleCursorAdapter} 将其与 {@link android.widget.ListView} 关联。 + + +

    +

    + 以下代码段将延续上一代码段的代码。它会创建一个包含由查询检索到的 {@link android.database.Cursor} 的 {@link android.widget.SimpleCursorAdapter} 对象,并将此对象设置为 {@link android.widget.ListView} 的适配器: + + + +

    +
    +// Defines a list of columns to retrieve from the Cursor and load into an output row
    +String[] mWordListColumns =
    +{
    +    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
    +    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
    +};
    +
    +// Defines a list of View IDs that will receive the Cursor columns for each row
    +int[] mWordListItems = { R.id.dictWord, R.id.locale};
    +
    +// Creates a new SimpleCursorAdapter
    +mCursorAdapter = new SimpleCursorAdapter(
    +    getApplicationContext(),               // The application's Context object
    +    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
    +    mCursor,                               // The result from the query
    +    mWordListColumns,                      // A string array of column names in the cursor
    +    mWordListItems,                        // An integer array of view IDs in the row layout
    +    0);                                    // Flags (usually none are needed)
    +
    +// Sets the adapter for the ListView
    +mWordList.setAdapter(mCursorAdapter);
    +
    +

    + :要通过 {@link android.database.Cursor} 支持 {@link android.widget.ListView},游标必需包含名为 _ID 的列。 + + 正因如此,前文显示的查询会为“字词”表检索 _ID 列,即使 {@link android.widget.ListView} 未显示该列。 + + 此限制也解释了为什么大多数提供程序的每个表都具有 _ID 列。 + +

    + + +

    从查询结果中获取数据

    +

    + 您可以将查询结果用于其他任务,而不是仅显示它们。例如,您可以从用户字典中检索拼写,然后在其他提供程序中查找它们。 + +要执行此操作,您需要在 {@link android.database.Cursor} 中循环访问行: +

    +
    +
    +// Determine the column index of the column named "word"
    +int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
    +
    +/*
    + * Only executes if the cursor is valid. The User Dictionary Provider returns null if
    + * an internal error occurs. Other providers may throw an Exception instead of returning null.
    + */
    +
    +if (mCursor != null) {
    +    /*
    +     * Moves to the next row in the cursor. Before the first movement in the cursor, the
    +     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
    +     * exception.
    +     */
    +    while (mCursor.moveToNext()) {
    +
    +        // Gets the value from the column.
    +        newWord = mCursor.getString(index);
    +
    +        // Insert code here to process the retrieved word.
    +
    +        ...
    +
    +        // end of while loop
    +    }
    +} else {
    +
    +    // Insert code here to report an error if the cursor is null or the provider threw an exception.
    +}
    +
    +

    + {@link android.database.Cursor} 实现包含多个用于从对象中检索不同类型的数据的“获取”方法。 +例如,上一个代码段使用 {@link android.database.Cursor#getString getString()}。 +它们还具有 {@link android.database.Cursor#getType getType()} 方法,该方法会返回指示列的数据类型的值。 + + +

    + + + +

    内容提供程序权限

    +

    + 提供程序的应用可以指定其他应用访问提供程序的数据所必需的权限。 +这些权限可确保用户了解应用将尝试访问的数据。 +根据提供程序的要求,其他应用会请求它们访问提供程序所需的权限。 +最终用户会在安装应用时看到所请求的权限。 + +

    +

    + 如果提供程序的应用未指定任何权限,则其他应用将无权访问提供程序的数据。 +但是,无论指定权限为何,提供程序的应用中的组件始终具有完整的读取和写入访问权限。 + +

    +

    + 如前所述,用户字典提供程序需要 + android.permission.READ_USER_DICTIONARY 权限才能从中检索数据。 + 提供程序具有用于插入、更新或删除数据的单独 android.permission.WRITE_USER_DICTIONARY 权限。 + +

    +

    + 要获取访问提供程序所需的权限,应用将通过其清单文件中的 +<uses-permission> + 元素来请求这些权限。Android 软件包管理器安装应用时,用户必须批准该应用请求的所有权限。 +如果用户批准所有权限,软件包管理器将继续安装;如果用户未批准这些权限,软件包管理器将中止安装。 + + +

    +

    + 以下 +<uses-permission> 元素会请求对用户字典提供程序的读取访问权限: + +

    +
    +    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
    +
    +

    + +安全与权限指南中详细介绍了权限对提供程序访问的影响。 +

    + + + +

    插入、更新和删除数据

    +

    + 与从提供程序检索数据的方式相同,也可以通过提供程序客户端和提供程序 {@link android.content.ContentProvider} 之间的交互来修改数据。 + + 您通过传递到 {@link android.content.ContentProvider} 的对应方法的参数来调用 {@link android.content.ContentResolver} 方法。 +提供程序和提供程序客户端会自动处理安全性和跨进程通信。 + +

    +

    插入数据

    +

    + 要将数据插入提供程序,可调用 + {@link android.content.ContentResolver#insert ContentResolver.insert()} + 方法。此方法会在提供程序中插入新行并为该行返回内容 URI。 + 此代码段显示如何将新字词插入用户字典提供程序: +

    +
    +// Defines a new Uri object that receives the result of the insertion
    +Uri mNewUri;
    +
    +...
    +
    +// Defines an object to contain the new values to insert
    +ContentValues mNewValues = new ContentValues();
    +
    +/*
    + * Sets the values of each column and inserts the word. The arguments to the "put"
    + * method are "column name" and "value"
    + */
    +mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
    +mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
    +mNewValues.put(UserDictionary.Words.WORD, "insert");
    +mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
    +
    +mNewUri = getContentResolver().insert(
    +    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
    +    mNewValues                          // the values to insert
    +);
    +
    +

    + 新行的数据会进入单个 {@link android.content.ContentValues} 对象中,该对象在形式上与单行游标类似。 +此对象中的列不需要具有相同的数据类型,如果您不想指定值,则可以使用 {@link android.content.ContentValues#putNull ContentValues.putNull()} 将列设置为 null。 + + +

    +

    + 代码段不会添加 _ID 列,因为系统会自动维护此列。 +提供程序会向添加的每个行分配唯一的 _ID 值。 +通常,提供程序会将此值用作表的主键。 +

    +

    + newUri 中返回的内容 URI 会按照以下格式标识新添加的行: + +

    +
    +content://user_dictionary/words/<id_value>
    +
    +

    + <id_value> 是新行的 _ID 内容。 + 大多数提供程序都能自动检测这种格式的内容 URI,然后在该特定行上执行请求的操作。 + +

    +

    + 要从返回的 {@link android.net.Uri} 中获取 _ID 的值,请调用 + {@link android.content.ContentUris#parseId ContentUris.parseId()}。 +

    +

    更新数据

    +

    + 要更新行,请按照执行插入的方式使用具有更新值的 {@link android.content.ContentValues} 对象,并按照执行查询的方式使用选择条件。 + + 您使用的客户端方法是 + {@link android.content.ContentResolver#update ContentResolver.update()}。您只需将值添加至您要更新的列的 {@link android.content.ContentValues} 对象。 +如果您要清除列的内容,请将值设置为 null。 + +

    +

    + 以下代码段会将区域设置具有语言“en”的所有行的区域设置更改为 null。 +返回值是已更新的行数: +

    +
    +// Defines an object to contain the updated values
    +ContentValues mUpdateValues = new ContentValues();
    +
    +// Defines selection criteria for the rows you want to update
    +String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
    +String[] mSelectionArgs = {"en_%"};
    +
    +// Defines a variable to contain the number of updated rows
    +int mRowsUpdated = 0;
    +
    +...
    +
    +/*
    + * Sets the updated value and updates the selected words.
    + */
    +mUpdateValues.putNull(UserDictionary.Words.LOCALE);
    +
    +mRowsUpdated = getContentResolver().update(
    +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    +    mUpdateValues                       // the columns to update
    +    mSelectionClause                    // the column to select on
    +    mSelectionArgs                      // the value to compare to
    +);
    +
    +

    + 您还应该在调用 + {@link android.content.ContentResolver#update ContentResolver.update()} 时检查用户输入。如需了解有关此内容的更多详情,请阅读防止恶意输入部分。 + +

    +

    删除数据

    +

    + 删除行与检索行数据类似:为要删除的行指定选择条件,客户端方法会返回已删除的行数。 + + 以下代码段会删除应用 ID 与“用户”匹配的行。该方法会返回已删除的行数。 + +

    +
    +
    +// Defines selection criteria for the rows you want to delete
    +String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
    +String[] mSelectionArgs = {"user"};
    +
    +// Defines a variable to contain the number of rows deleted
    +int mRowsDeleted = 0;
    +
    +...
    +
    +// Deletes the words that match the selection criteria
    +mRowsDeleted = getContentResolver().delete(
    +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    +    mSelectionClause                    // the column to select on
    +    mSelectionArgs                      // the value to compare to
    +);
    +
    +

    + 您还应该在调用 + {@link android.content.ContentResolver#delete ContentResolver.delete()} 时检查用户输入。如需了解有关此内容的更多详情,请阅读防止恶意输入部分。 + +

    + +

    提供程序数据类型

    +

    + 内容提供程序可以提供多种不同的数据类型。用户字典提供程序仅提供文本,但提供程序也能提供以下格式: + +

    +
      +
    • + 整型 +
    • +
    • + 长整型(长) +
    • +
    • + 浮点型 +
    • +
    • + 长浮点型(双倍) +
    • +
    +

    + 提供程序经常使用的另一种数据类型是作为 64KB 字节的数组实施的二进制大型对象 (BLOB)。 +您可以通过查看 + {@link android.database.Cursor} 类“获取”方法看到可用数据类型。 +

    +

    + 提供程序文档中通常都列出了其每个列的数据类型。 + 用户字典提供程序的数据类型列在其协定类 {@link android.provider.UserDictionary.Words} 参考文档中(协定类部分对协定类进行了介绍)。 + + + 您也可以通过调用 {@link android.database.Cursor#getType + Cursor.getType()} 来确定数据类型。 +

    +

    + 提供程序还会维护其定义的每个内容 URI 的 MIME(多用途互联网邮件扩展)数据类型信息。您可以使用 MIME 类型信息查明应用是否可以处理提供程序提供的数据,或根据 MIME 类型选择处理类型。 + +在使用包含复杂数据结构或文件的提供程序时,通常需要 MIME 类型。 + +例如,联系人提供程序中的 {@link android.provider.ContactsContract.Data} + 表会使用 MIME 类型来标记每行中存储的联系人数据类型。 +要获取与内容 URI 对应的 MIME 类型,请调用 + {@link android.content.ContentResolver#getType ContentResolver.getType()}。 +

    +

    + MIME 类型引用部分介绍了标准和自定义 MIME 类型的语法。 + +

    + + + +

    提供程序访问的替代形式

    +

    + 提供程序访问的三种替代形式在应用开发过程中十分重要: +

    +
      +
    • + 批量访问:您可以通过 + {@link android.content.ContentProviderOperation} 类中的方法创建一批访问调用,然后通过 + {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 应用它们。 +
    • +
    • + 异步查询:您应该在单独线程中执行查询。执行此操作的方式之一是使用 {@link android.content.CursorLoader} 对象。 +加载器指南中的示例展示了如何执行此操作。 + + +
    • +
    • + 通过 Intent 访问数据:尽管您无法直接向提供程序发送 Intent,但可以向提供程序的应用发送 Intent,后者通常具有修改提供程序数据的最佳配置。 + + +
    • +
    +

    + 下文将介绍批量访问和修改。 +

    +

    批量访问

    +

    + 批量访问提供程序适用于插入大量行,或通过同一方法调用在多个表中插入行,或者通常用于跨进程界限将一组操作作为事务处理(原子操作)执行。 + + +

    +

    + 要在“批量模式”下访问提供程序, +您可以创建 {@link android.content.ContentProviderOperation} 对象数组,然后使用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} +将其分派给内容提供程序。 +您需将内容提供程序的授权传递给此方法,而不是特定内容 URI。这样可使数组中的每个 {@link android.content.ContentProviderOperation} 对象都能适用于其他表。 + + +调用 {@link android.content.ContentResolver#applyBatch + ContentResolver.applyBatch()} 会返回结果数组。 +

    +

    + {@link android.provider.ContactsContract.RawContacts} 协定类 +的说明包括展示批量注入的代码段。 +联系人管理器示例应用包含在其 ContactAdder.java + 源文件中进行批量访问的示例。 + +

    + +

    通过 Intent 访问数据

    +

    + Intent 可以提供对内容提供程序的间接访问。即使您的应用不具备访问权限,您也可以通过以下方式允许用户访问提供程序中的数据:从具有权限的应用中获取回结果 Intent,或者通过激活具有权限的应用,然后让用户在其中工作。 + + + +

    +

    通过临时权限获取访问权限

    +

    + 即使您没有适当的访问权限,也可以通过以下方式访问内容提供程序中的数据:将 Intent 发送至具有权限的应用,然后接收回包含“URI”权限的结果 Intent。 + + + 这些是特定内容 URI 的权限,将持续至接收该权限的 Activity 结束。 +具有永久权限的应用将通过在结果 Intent 中设置标志来授予临时权限: + +

    +
      +
    • + 读取权限: +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} +
    • +
    • + 写入权限: +{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} +
    • +
    +

    + :这些标志不会为其授权包含在内容 URI 中的提供程序 +提供常规的读取或写入访问权限。访问权限仅适用于 URI 本身。 +

    +

    + 提供程序使用 +<provider> +元素的 +android:grantUriPermission +属性以及 +<provider> +元素的 +<grant-uri-permission> +子元素在其清单文件中定义内容 URI 的 URI 权限。安全与权限指南中“URI 权限”部分更加详细地说明了 URI 权限机制。 + + +

    +

    + 例如,即使您没有 + {@link android.Manifest.permission#READ_CONTACTS} 权限,也可以在联系人提供程序中检索联系人的数据。您可能希望在向联系人发送电子生日祝福的应用中执行此操作。 +您更愿意让用户控制应用所使用的联系人,而不是请求 {@link android.Manifest.permission#READ_CONTACTS},让您能够访问用户的所有联系人及其信息。 + + +要执行此操作,您需要使用以下进程: +

    +
      +
    1. + 您的应用会使用方法 {@link android.app.Activity#startActivityForResult + startActivityForResult()} 发送包含操作 + {@link android.content.Intent#ACTION_PICK} 和“联系人”MIME 类型 + {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} 的 Intent 对象。 + +
    2. +
    3. + 由于此 Intent 与“联系人”应用的“选择” Activity 的 Intent 过滤器相匹配,因此 Activity 会显示在前台。 + +
    4. +
    5. + 在选择 Activity 中,用户选择要更新的联系人。 +发生此情况时,选择 Activity 会调用 + {@link android.app.Activity#setResult setResult(resultcode, intent)} + 以设置用于返回至应用的 Intent。 Intent 包含用户选择的联系人的内容 URI,以及“extras”标志 + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}。 +这些标志会为您的应用授予读取内容 URI 所指向的联系人的数据的 URI + 权限。然后,选择 Activity 会调用 {@link android.app.Activity#finish()} 以返回对应用的控制。 + + +
    6. +
    7. + 您的 Activity 会返回至前台,系统会调用您的 Activity 的 + {@link android.app.Activity#onActivityResult onActivityResult()} + 方法。此方法会收到“联系人”应用中选择 Activity 所创建的结果 Intent。 + +
    8. +
    9. + 通过来自结果 Intent 的内容 URI,您可以读取来自联系人提供程序的联系人数据,即使您未在清单文件中请求对该提供程序的永久读取访问权限。 + +您可以获取联系人的生日信息或其电子邮件地址,然后发送电子祝福。 + +
    10. +
    +

    使用其他应用

    +

    + 允许用户修改您无权访问的数据的简单方法是激活具有权限的应用,让用户在其中执行工作。 + +

    +

    + 例如,日历应用接受 + {@link android.content.Intent#ACTION_INSERT} Intent,这让您可以激活应用的插入 UI。您可以在此 Intent(应用将使用该 Intent 来预先填充 UI)中传递“额外”数据,由于定期事件具有复杂的语法,因此将事件插入日历提供程序的首选方式是激活具有 + {@link android.content.Intent#ACTION_INSERT} 的日历应用,然后让用户在其中插入事件。 + + + +

    + +

    协定类

    +

    + 协定类定义帮助应用使用内容 URI、列名称、 Intent 操作以及内容提供程序的其他功能的常量。 +协定类未自动包含在提供程序中;提供程序的开发者需要定义它们,然后使其可用于其他开发者。 + +Android + 平台中包含的许多提供程序都在软件包 {@link android.provider} 中具有对应的协定类。 +

    +

    + 例如,用户字典提供程序具有包含内容 URI 和列名称常量的协定类 {@link android.provider.UserDictionary}。 + +“字词”表的内容 URI 在常量 + {@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI} 中定义。 + {@link android.provider.UserDictionary.Words} 类也包含列名称常量,本指南的示例代码段中就使用了该常量。 +例如,查询投影可以定义为: + +

    +
    +String[] mProjection =
    +{
    +    UserDictionary.Words._ID,
    +    UserDictionary.Words.WORD,
    +    UserDictionary.Words.LOCALE
    +};
    +
    +

    + 联系人提供程序的 {@link android.provider.ContactsContract} 也是一个协定类。 + 此类的参考文档包括示例代码段。其子类之一 {@link android.provider.ContactsContract.Intents.Insert} 是包含 Intent 和 Intent 数据的协定类。 + + +

    + + + +

    MIME 类型引用

    +

    + 内容提供程序可以返回标准 MIME 媒体类型和/或自定义 MIME 类型字符串。 +

    +

    + MIME 类型具有格式 +

    +
    +type/subtype
    +
    +

    + 例如,众所周知的 MIME 类型 text/html 具有 text 类型和 + html 子类型。如果提供程序为 URI 返回此类型,则意味着使用该 URI 的查询会返回包含 HTML 标记的文本。 + +

    +

    + 自定义 MIME 类型字符串(也称为“特定于供应商”的 MIME 类型)具有更加复杂的类型子类型值。 +类型值始终为 +

    +
    +vnd.android.cursor.dir
    +
    +

    + (多行)或 +

    +
    +vnd.android.cursor.item
    +
    +

    + (单行)。 +

    +

    + 子类型特定于提供程序。Android 内置提供程序通常具有简单的子类型。 +例如,当联系人应用为电话号码创建行时,它会在行中设置以下 MIME 类型: + +

    +
    +vnd.android.cursor.item/phone_v2
    +
    +

    + 请注意,子类型值只是 phone_v2。 +

    +

    + 其他提供程序开发者可能会根据提供程序的授权和表名称创建自己的子类型模式。 +例如,假设提供程序包含列车时刻表。 + 提供程序的授权是 com.example.trains,并包含表 + Line1、Line2 和 Line3。在响应表 Line1 的内容 URI +

    +

    +

    +content://com.example.trains/Line1
    +
    +

    + 时,提供程序会返回 MIME 类型 +

    +
    +vnd.android.cursor.dir/vnd.example.line1
    +
    +

    + 在响应表 Line2 中第 5 行的内容 URI +

    +
    +content://com.example.trains/Line2/5
    +
    +

    + 时,提供程序会返回 MIME 类型 +

    +
    +vnd.android.cursor.item/vnd.example.line2
    +
    +

    + 大多数内容提供程序都会为其使用的 MIME 类型定义协定类常量。例如,联系人提供程序协定类 {@link android.provider.ContactsContract.RawContacts} 会为单个原始联系人行的 MIME 类型定义常量 + {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}。 + + + +

    +

    + +内容 URI 部分介绍了单个行的内容 URI。 +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..6da57435f43dca7bddc743b3f9c514137182be9c --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=创建内容提供程序 +@jd:body +
    +
    + + +

    本文内容

    +
      +
    1. + 设计数据存储 +
    2. +
    3. + 设计内容 URI +
    4. +
    5. + 实现 ContentProvider 类 +
        +
      1. + 必需方法 +
      2. +
      3. + 实现 query() 方法 +
      4. +
      5. + 实现 insert() 方法 +
      6. +
      7. + 实现 delete() 方法 +
      8. +
      9. + 实现 update() 方法 +
      10. +
      11. + 实现 onCreate() 方法 +
      12. +
      +
    6. +
    7. + 实现内容提供程序 MIME 类型 +
        +
      1. + 表的 MIME 类型 +
      2. +
      3. + 文件的 MIME 类型 +
      4. +
      +
    8. +
    9. + 实现协定类 +
    10. +
    11. + 实现内容提供程序权限 +
    12. +
    13. + <Provider> 元素 +
    14. +
    15. + Intent 和数据访问 +
    16. +
    +

    关键类

    +
      +
    1. + {@link android.content.ContentProvider} +
    2. +
    3. + {@link android.database.Cursor} +
    4. +
    5. + {@link android.net.Uri} +
    6. +
    +

    相关示例

    +
      +
    1. + + 记事本示例应用 + +
    2. +
    +

    另请参阅

    +
      +
    1. + + 内容提供程序基础知识 +
    2. +
    3. + + 日历提供程序 +
    4. +
    +
    +
    + + +

    + 内容提供程序管理对中央数据存储库的访问。您将 + 提供程序作为 Android 应用中的一个或多个类(连同清单文件 + 中的元素)实现。其中一个类会实现子类 + {@link android.content.ContentProvider},即您的提供程序与 + 其他应用之间的界面。尽管内容提供程序旨在向其他应用提供 + 数据,但您的应用中必定有这样一些 Activity,它们允许用户 + 查询和修改由提供程序管理的数据。 +

    +

    + 本主题的其余部分列出了开发内容提供程序的基本步骤和 + 需要使用的 API。 +

    + + + +

    着手开发前的准备工作

    +

    + 请在着手开发提供程序之前执行以下操作: +

    +
      +
    1. + 决定您是否需要内容提供程序。如果您想提供下列一项或多项功能,则需要开发内容 + 提供程序: +
        +
      • 您想为其他应用提供复杂的数据或文件
      • +
      • 您想允许用户将复杂的数据从您的应用复制到其他应用中
      • +
      • 您想使用搜索框架提供自定义搜索建议
      • +
      +

      + 如果完全是在 + 您自己的应用中使用,则“根本不”需要提供程序即可使用 SQLite 数据库。 +

      +
    2. +
    3. + 如果您尚未完成此项操作,请阅读 + + 内容提供程序基础知识主题,了解有关提供程序的详情。 +
    4. +
    +

    + 接下来,请按照以下步骤开发您的提供程序: +

    +
      +
    1. + 为您的数据设计原始存储。内容提供程序以两种方式提供数据: +
      +
      + 文件数据 +
      +
      + 通常存储在文件中的数据,如 + 照片、音频或视频。将文件存储在您的应用的私有 + 空间内。您的提供程序可以应其他应用发出的文件请求 + 提供文件句柄。 +
      +
      + “结构化”数据 +
      +
      + 通常存储在数据库、数组或类似结构中的数据。 + 以兼容行列表的形式存储数据。行 + 表示实体,如人员或库存项目。列表示 + 实体的某项数据,如人员的姓名或商品的价格。此类数据通常 + 存储在 SQLite 数据库中,但您可以使用任何类型的 + 持久存储。如需了解有关 + Android 系统中提供的存储类型的更多信息,请参阅 + 设计数据存储部分。 +
      +
      +
    2. +
    3. + 定义 {@link android.content.ContentProvider} 类及其 + 所需方法的具体实现。此类是您的数据与 + Android 系统其余部分之间的界面。如需了解有关此类的详细信息,请参阅 + 实现 ContentProvider 类部分。 +
    4. +
    5. + 定义提供程序的权限字符串、其内容 URI 以及列名称。如果您想让 + 提供程序的应用处理 Intent,则还要定义 Intent 操作、Extra 数据 + 以及标志。此外,还要定义想要访问您的数据的应用必须具备的权限。 +您应该考虑在一个单独的协定类中将所有这些值定义为常量;以后您可以将此类公开给其他开发者。 +如需了解有关内容 URI 的详细信息,请参阅设计内容 URI 部分。 + + + 如需了解有关 Intent 的详细信息,请参阅 Intent 和数据访问部分。 + +
    6. +
    7. + 添加其他可选部分,如示例数据或可以在提供程序与云数据之间同步数据的 {@link android.content.AbstractThreadedSyncAdapter} 实现。 + + +
    8. +
    + + + +

    设计数据存储

    +

    + 内容提供程序是用于访问以结构化格式保存的数据的界面。在您创建该界面之前,必须决定如何存储数据。 +您可以按自己的喜好以任何形式存储数据,然后根据需要设计读写数据的界面。 + +

    +

    + 以下是 Android 中提供的一些数据存储技术: +

    +
      +
    • + Android 系统包括一个 SQLite 数据库 API,Android 自己的提供程序使用它来存储面向表的数据。 +{@link android.database.sqlite.SQLiteOpenHelper} 类可帮助您创建数据库,{@link android.database.sqlite.SQLiteDatabase} 类是用于访问数据库的基类。 + + + +

      + 请记住,您不必使用数据库来实现存储库。提供程序在外部表现为一组表,与关系数据库类似,但这并不是对提供程序内部实现的要求; + + +

      +
    • +
    • + 对于存储文件数据,Android 提供了各种面向文件的 API。 + 如需了解有关文件存储的更多信息,请阅读数据存储主题。 +如果您要设计提供媒体相关数据(如音乐或视频)的提供程序,则可开发一个合并了表数据和文件的提供程序; + + +
    • +
    • + 要想使用基于网络的数据,请使用 {@link java.net} 和 {@link android.net} 中的类。 +您也可以将基于网络的数据与本地数据存储(如数据库)同步,然后以表或文件的形式提供数据。 + + 示例同步适配器示例应用展示了这类同步。 + +
    • +
    +

    + 数据设计考虑事项 +

    +

    + 以下是一些设计提供程序数据结构的技巧: +

    +
      +
    • + 表数据应始终具有一个“主键”列,提供程序将其作为与每行对应的唯一数字值加以维护。 +您可以使用此值将该行链接到其他表中的相关行(将其用作“外键”)。 +尽管您可以为此列使用任何名称,但使用 {@link android.provider.BaseColumns#_ID BaseColumns._ID} 是最佳选择,因为将提供程序查询的结果链接到 {@link android.widget.ListView} 的条件是,检索到的其中一个列的名称必须是 _ID; + + + + +
    • +
    • + 如果您想提供位图图像或其他非常庞大的文件导向型数据,请将数据存储在一个文件中,然后间接提供这些数据,而不是直接将其存储在表中。 + +如果您执行了此操作,则需要告知提供程序的用户,他们需要使用 {@link android.content.ContentResolver} 文件方法来访问数据; + +
    • +
    • + 使用二进制大型对象 (BLOB) 数据类型存储大小或结构会发生变化的数据。 +例如,您可以使用 BLOB 列来存储协议缓冲区JSON 结构。 + + +

      + 您也可以使用 BLOB 来实现独立于架构的表。在这类表中,您需要以 BLOB 形式定义一个主键列、一个 MIME 类型列以及一个或多个通用列。 + +这些 BLOB 列中数据的含义通过 MIME 类型列中的值指示。 +这样一来,您就可以在同一表中存储不同类型的行。 +举例来说,联系人提供程序的“数据”表 {@link android.provider.ContactsContract.Data} 便是一个独立于架构的表。 + + +

      +
    • +
    + +

    设计内容 URI

    +

    + 内容 URI 是用于在提供程序中标识数据的 URI。内容 URI 包括整个提供程序的符号名称(其权限)和一个指向表或文件的名称(路径)。 + +可选 ID 部分指向 + 表中的单个行。 + {@link android.content.ContentProvider} 的每一个数据访问方法都将内容 URI 作为参数;您可以利用这一点确定要访问的表、行或文件。 + +

    +

    + + 内容提供程序基础知识主题中描述了内容 URI 的基础知识。 + +

    +

    设计权限

    +

    + 提供程序通常具有单一权限,该权限充当其 Android 内部名称。为避免与其他提供程序发生冲突,您应该使用 Internet 域所有权(反向)作为提供程序权限的基础。 + +由于此建议也适用于 Android 软件包名称,因此您可以将提供程序权限定义为包含该提供程序的软件包名称的扩展名。 + +例如,如果您的 Android 软件包名称为 + com.example.<appname>,则应为提供程序授予权限 com.example.<appname>.provider。 + +

    +

    设计路径结构

    +

    + 开发者通常通过追加指向单个表的路径来根据权限创建内容 URI。 +例如,如果您有两个表:table1 和 + table2,则可以通过合并上一示例中的权限来生成 + 内容 URI + com.example.<appname>.provider/table1 和 + com.example.<appname>.provider/table2。路径并不限定于单个段,也无需为每一级路径都创建一个表。 + +

    +

    处理内容 URI ID

    +

    + 按照惯例,提供程序通过接受末尾具有行所对应 ID 值的内容 URI 来提供对表中单个行的访问。同样按照惯例,提供程序会将该 ID 值与表的 _ID 列进行匹配,并对匹配的行执行请求的访问。 + + + +

    +

    + 这一惯例为访问提供程序的应用的常见设计模式提供了便利。应用会对提供程序执行查询,并使用 {@link android.widget.CursorAdapter} 以 {@link android.widget.ListView} 显示生成的 {@link android.database.Cursor}。 + + + 定义 {@link android.widget.CursorAdapter} 的条件是, + {@link android.database.Cursor} 中的其中一个列必须是 _ID +

    +

    + 用户随后从 UI 上显示的行中选取其中一行,以查看或修改数据。 +应用会从支持 + {@link android.widget.ListView} 的 {@link android.database.Cursor} 中获取对应行,获取该行的 _ID 值,将其追加到内容 URI,然后向提供程序发送访问请求。 +然后,提供程序便可对用户选取的特定行执行查询或修改。 + +

    +

    内容 URI 模式

    +

    + 为帮助您选择对传入的内容 URI 执行的操作,提供程序 API 加入了实用类 {@link android.content.UriMatcher},它会将内容 URI“模式”映射到整型值。 + +您可以在一个 switch 语句中使用这些整型值,为匹配特定模式的一个或多个内容 URI 选择所需操作。 + +

    +

    + 内容 URI 模式使用通配符匹配内容 URI: +

    +
      +
    • + *:匹配由任意长度的任何有效字符组成的字符串 +
    • +
    • + #:匹配由任意长度的数字字符组成的字符串 +
    • +
    +

    + 以设计和编码内容 URI 处理为例,假设一个具有权限 com.example.app.provider 的提供程序能识别以下指向表的内容 URI: + + +

    +
      +
    • + content://com.example.app.provider/table1:一个名为 table1 的表 +
    • +
    • + content://com.example.app.provider/table2/dataset1:一个名为 + dataset1 的表 +
    • +
    • + content://com.example.app.provider/table2/dataset2:一个名为 + dataset2 的表 +
    • +
    • + content://com.example.app.provider/table3:一个名为 table3 的表 +
    • +
    +

    + 提供程序也能识别追加了行 ID 的内容 URI,例如,content://com.example.app.provider/table3/1 对应由 table31 标识的行的内容 URI。 + + +

    +

    + 可以使用以下内容 URI 模式: +

    +
    +
    + content://com.example.app.provider/* +
    +
    + 匹配提供程序中的任何内容 URI。 +
    +
    + content://com.example.app.provider/table2/*: +
    +
    + 匹配表 dataset1 + 和表 dataset2 的内容 URI,但不匹配 table1 或 + table3 的内容 URI。 +
    +
    + content://com.example.app.provider/table3/#:匹配 + table3 中单个行的内容 URI,如 content://com.example.app.provider/table3/6 对应由 + 6 标识的行的内容 URI。 + +
    +
    +

    + 以下代码段说明了 {@link android.content.UriMatcher} 中方法的工作方式。 + 此代码采用不同方式处理整个表的 URI 与单个行的 URI,它为表使用的内容 URI 模式是 + content://<authority>/<path>,为单个行使用的内容 URI 模式则是 + content://<authority>/<path>/<id>。 + +

    +

    + 方法 {@link android.content.UriMatcher#addURI(String, String, int) addURI()} 会将权限和路径映射到一个整型值。 +方法 {@link android.content.UriMatcher#match(Uri) + match()} 会返回 URI 的整型值。switch 语句会在查询整个表与查询单个记录之间进行选择: + +

    +
    +public class ExampleProvider extends ContentProvider {
    +...
    +    // Creates a UriMatcher object.
    +    private static final UriMatcher sUriMatcher;
    +...
    +    /*
    +     * The calls to addURI() go here, for all of the content URI patterns that the provider
    +     * should recognize. 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
    +     */
    +    sUriMatcher.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.
    +     */
    +    sUriMatcher.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 (sUriMatcher.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 is not recognized, you should do some error handling here.
    +        }
    +        // call the code to actually do the query
    +    }
    +
    +

    + 另一个类 {@link android.content.ContentUris} 会提供一些工具方法,用于处理内容 URI 的 id 部分。 +{@link android.net.Uri} 类和 + {@link android.net.Uri.Builder} 类包括一些工具方法,用于解析现有 + {@link android.net.Uri} 对象和构建新对象。 +

    + + +

    实现 ContentProvider 类

    +

    + {@link android.content.ContentProvider} 实例通过处理来自其他应用的请求来管理对结构化数据集的访问。 +所有形式的访问最终都会调用 {@link android.content.ContentResolver},后者接着调用 + {@link android.content.ContentProvider} 的具体方法来获取访问权限。 + +

    +

    必需方法

    +

    + 抽象类 {@link android.content.ContentProvider} 定义了六个抽象方法,您必须将这些方法作为自己具体子类的一部分加以实现。 +所有这些方法( + {@link android.content.ContentProvider#onCreate() onCreate()} 除外)都由一个尝试访问您的内容提供程序的客户端应用调用: + +

    +
    +
    + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + query()} +
    +
    + 从您的提供程序检索数据。使用参数选择要查询的表、要返回的行和列以及结果的排序顺序。 + + 将数据作为 {@link android.database.Cursor} 对象返回。 +
    +
    + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} +
    +
    + 在您的提供程序中插入一个新行。使用参数选择目标表并获取要使用的列值。 +返回新插入行的内容 URI。 + +
    +
    + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} +
    +
    + 更新您提供程序中的现有行。使用参数选择要更新的表和行,并获取更新后的列值。 +返回已更新的行数。 +
    +
    + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +
    +
    + 从您的提供程序中删除行。使用参数选择要删除的表和行。 +返回已删除的行数。 +
    +
    + {@link android.content.ContentProvider#getType(Uri) getType()} +
    +
    + 返回内容 URI 对应的 MIME 类型。实现内容提供程序 MIME 类型部分对此方法做了更详尽的描述。 + +
    +
    + {@link android.content.ContentProvider#onCreate() onCreate()} +
    +
    + 初始化您的提供程序。Android 系统会在创建您的提供程序后立即调用此方法。 +请注意, + {@link android.content.ContentResolver} 对象尝试访问您的提供程序时,系统才会创建它。 +
    +
    +

    + 请注意,这些方法的签名与同名的 + {@link android.content.ContentResolver} 方法相同。 +

    +

    + 您在实现这些方法时应考虑以下事项: +

    +
      +
    • + 所有这些方法({@link android.content.ContentProvider#onCreate() onCreate()} + 除外)都可由多个线程同时调用,因此它们必须是线程安全方法。如需了解有关多个线程的更多信息,请参阅进程和线程主题; + + + +
    • +
    • + 避免在 {@link android.content.ContentProvider#onCreate() + onCreate()} 中执行长时间操作。将初始化任务推迟到实际需要时进行。 + 实现 onCreate() 方法部分对此做了更详尽的描述; + +
    • +
    • + 尽管您必须实现这些方法,但您的代码只需返回要求的数据类型,无需执行任何其他操作。 +例如,您可能想防止其他应用向某些表插入数据。 +要实现此目的,您可以忽略 + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 调用并返回 0。 + +
    • +
    +

    实现 query() 方法

    +

    + + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 方法必须返回 {@link android.database.Cursor} 对象。如果失败,则会引发 {@link java.lang.Exception}。 +如果您使用 SQLite 数据库作为数据存储,则只需返回由 {@link android.database.sqlite.SQLiteDatabase} 类的其中一个 + query() 方法返回的 {@link android.database.Cursor}。 + + 如果查询不匹配任何行,您应该返回一个 {@link android.database.Cursor} + 实例(其 {@link android.database.Cursor#getCount()} 方法返回 0)。 + 只有当查询过程中出现内部错误时,您才应该返回 null。 +

    +

    + 如果您不使用 SQLite 数据库作为数据存储,请使用 {@link android.database.Cursor} 的其中一个具体子类。 +例如,在 {@link android.database.MatrixCursor} 类实现的游标中,每一行都是一个 {@link java.lang.Object} 数组。 +对于此类,请使用 {@link android.database.MatrixCursor#addRow(Object[]) addRow()} 来添加新行。 + +

    +

    + 请记住,Android 系统必须能够跨进程边界传播 {@link java.lang.Exception}。 +Android 可以为以下异常执行此操作,这些异常可能有助于处理查询错误: + +

    +
      +
    • + {@link java.lang.IllegalArgumentException}(您可以选择在提供程序收到无效的内容 URI 时引发此异常) + +
    • +
    • + {@link java.lang.NullPointerException} +
    • +
    +

    实现 insert() 方法

    +

    + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 方法会使用 {@link android.content.ContentValues} + 参数中的值向相应表中添加新行。 +如果 {@link android.content.ContentValues} 参数中未包含列名称,您可能想在您的提供程序代码或数据库架构中提供其默认值。 + + +

    +

    + 此方法应该返回新行的内容 URI。要想构建此方法,请使用 + {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()} 向表的内容 URI 追加新行的 _ID(或其他主键)值。 + +

    +

    实现 delete() 方法

    +

    + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 方法不需要从您的数据存储中实际删除行。 +如果您将同步适配器与提供程序一起使用,应该考虑为已删除的行添加“删除”标志,而不是将行整个移除。 + +同步适配器可以检查是否存在已删除的行,并将它们从服务器中移除,然后再将它们从提供程序中删除。 + +

    +

    实现 update() 方法

    +

    + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} 方法采用 + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 所使用的相同 {@link android.content.ContentValues} 参数,以及 +{@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 和 + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 所使用的相同 selectionselectionArgs 参数。 +这样一来,您就可以在这些方法之间重复使用代码。 +

    +

    实现 onCreate() 方法

    +

    + Android 系统会在启动提供程序时调用 {@link android.content.ContentProvider#onCreate() + onCreate()}。您只应在此方法中执行运行快速的初始化任务,并将数据库创建和数据加载推迟到提供程序实际收到数据请求时进行。 + +如果您在 + {@link android.content.ContentProvider#onCreate() onCreate()} 中执行长时间的任务,则会减慢提供程序的启动速度, +进而减慢提供程序对其他应用的响应速度。 + +

    +

    + 例如,如果您使用 SQLite 数据库,可以在 + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 中创建一个新的 {@link android.database.sqlite.SQLiteOpenHelper} 对象,然后在首次打开数据库时创建 SQL 表。 + +为简化这一过程,在您首次调用 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase + getWritableDatabase()} 时,它会自动调用 + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} 方法。 + +

    +

    + 以下两个代码段展示了 + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 与 + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} 之间的交互。第一个代码段是 + {@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 的实现: +

    +
    +public class ExampleProvider extends ContentProvider
    +
    +    /*
    +     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
    +     * in a following snippet.
    +     */
    +    private MainDatabaseHelper mOpenHelper;
    +
    +    // Defines the database name
    +    private static final String DBNAME = "mydb";
    +
    +    // Holds the database object
    +    private SQLiteDatabase db;
    +
    +    public boolean onCreate() {
    +
    +        /*
    +         * Creates a new helper object. This method always returns quickly.
    +         * Notice that the database itself isn't created or opened
    +         * until SQLiteOpenHelper.getWritableDatabase is called
    +         */
    +        mOpenHelper = new MainDatabaseHelper(
    +            getContext(),        // the application context
    +            DBNAME,              // the name of the database)
    +            null,                // uses the default SQLite cursor
    +            1                    // the version number
    +        );
    +
    +        return true;
    +    }
    +
    +    ...
    +
    +    // Implements the provider's insert method
    +    public Cursor insert(Uri uri, ContentValues values) {
    +        // Insert code here to determine which table to open, handle error-checking, and so forth
    +
    +        ...
    +
    +        /*
    +         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
    +         *
    +         */
    +        db = mOpenHelper.getWritableDatabase();
    +    }
    +}
    +
    +

    + 下一个代码段是 + {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} 的实现,其中包括一个帮助程序类: +

    +
    +...
    +// A string that defines the SQL statement for creating a table
    +private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    +    "main " +                       // Table's name
    +    "(" +                           // The columns in the table
    +    " _ID INTEGER PRIMARY KEY, " +
    +    " WORD TEXT"
    +    " FREQUENCY INTEGER " +
    +    " LOCALE TEXT )";
    +...
    +/**
    + * Helper class that actually creates and manages the provider's underlying data repository.
    + */
    +protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
    +
    +    /*
    +     * Instantiates an open helper for the provider's SQLite data repository
    +     * Do not do database creation and upgrade here.
    +     */
    +    MainDatabaseHelper(Context context) {
    +        super(context, DBNAME, null, 1);
    +    }
    +
    +    /*
    +     * Creates the data repository. This is called when the provider attempts to open the
    +     * repository and SQLite reports that it doesn't exist.
    +     */
    +    public void onCreate(SQLiteDatabase db) {
    +
    +        // Creates the main table
    +        db.execSQL(SQL_CREATE_MAIN);
    +    }
    +}
    +
    + + + +

    实现 ContentProvider MIME 类型

    +

    + {@link android.content.ContentProvider} 类具有两个返回 MIME 类型的方法: +

    +
    +
    + {@link android.content.ContentProvider#getType(Uri) getType()} +
    +
    + 您必须为任何提供程序实现的必需方法之一。 +
    +
    + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +
    +
    + 系统在您的提供程序提供文件时要求实现的方法。 +
    +
    +

    表的 MIME 类型

    +

    + {@link android.content.ContentProvider#getType(Uri) getType()} 方法会返回一个 MIME 格式的 + {@link java.lang.String},后者描述内容 + URI 参数返回的数据类型。{@link android.net.Uri} 参数可以是模式,而不是特定 URI;在这种情况下,您应该返回与匹配该模式的内容 URI 关联的数据类型。 + + +

    +

    + 对于文本、HTML 或 JPEG 等常见数据类型, + {@link android.content.ContentProvider#getType(Uri) getType()} 应该为该数据返回标准 + MIME 类型。 + IANA MIME Media Types + 网站上提供了这些标准类型的完整列表。 +

    +

    + 对于指向一个或多个表数据行的内容 URI, + {@link android.content.ContentProvider#getType(Uri) getType()} 应该以 Android 供应商特有 MIME 格式返回 + MIME 类型: +

    +
      +
    • + 类型部分:vnd +
    • +
    • + 子类型部分: +
        +
      • + 如果 URI 模式用于单个行:android.cursor.item/ +
      • +
      • + 如果 URI 模式用于多个行:android.cursor.dir/ +
      • +
      +
    • +
    • + 提供程序特有部分:vnd.<name>.<type> +

      + 您提供 <name><type>。 + <name> 值应具有全局唯一性, + <type> 值应在对应的 URI + 模式中具有唯一性。适合选择贵公司的名称或您的应用 Android 软件包名称的某个部分作为 <name>。 +适合选择 URI 关联表的标识字符串作为 <type>。 + + +

      + +
    • +
    +

    + 例如,如果提供程序的权限是 + com.example.app.provider,并且它公开了一个名为 + table1 的表,则 table1 中多个行的 MIME 类型是: +

    +
    +vnd.android.cursor.dir/vnd.com.example.provider.table1
    +
    +

    + 对于 table1 的单个行,MIME 类型是: +

    +
    +vnd.android.cursor.item/vnd.com.example.provider.table1
    +
    +

    文件的 MIME 类型

    +

    + 如果您的提供程序提供文件,请实现 + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}。 + 该方法会为您的提供程序可以为给定内容 URI 返回的文件返回一个 MIME 类型 {@link java.lang.String} 数组。您应该通过 MIME 类型过滤器参数过滤您提供的 MIME 类型,以便只返回客户端想处理的那些 MIME 类型。 + + +

    +

    + 例如,假设提供程序以 .jpg、 + .png.gif 格式文件形式提供照片图像。 + 如果应用调用 {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} 时使用了过滤器字符串 image/*(任何是“图像”的内容),则 {@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} 方法应返回数组: + + +

    +
    +{ "image/jpeg", "image/png", "image/gif"}
    +
    +

    + 如果应用只对 .jpg 文件感兴趣,则可以在调用 + {@link android.content.ContentResolver#getStreamTypes(Uri, String) + ContentResolver.getStreamTypes()} 时使用过滤器字符串 *\/jpeg,{@link android.content.ContentProvider#getStreamTypes(Uri, String) + ContentProvider.getStreamTypes()} 应返回: + +

    +{"image/jpeg"}
    +
    +

    + 如果您的提供程序未提供过滤器字符串中请求的任何 MIME 类型,则 + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} + 应返回 null。 +

    + + + +

    实现协定类

    +

    + 协定类是一种 public final 类,其中包含对 URI、列名称、MIME 类型以及其他与提供程序有关的元数据的常量定义。 +该类可确保即使 URI、列名称等数据的实际值发生变化,也可以正确访问提供程序,从而在提供程序与其他应用之间建立合同。 + + + +

    +

    + 协定类对开发者也有帮助,因为其常量通常采用助记名称,因此可以降低开发者为列名称或 URI 使用错误值的可能性。 +由于它是一种类,因此可以包含 Javadoc 文档。 +集成开发环境(如 + Eclipse)可以根据协定类自动完成常量名称,并为常量显示 Javadoc。 + +

    +

    + 开发者无法从您的应用访问协定类的类文件,但他们可以通过您提供的 .jar 文件将其静态编译到其应用内。 + +

    +

    + 举例来说,{@link android.provider.ContactsContract} 类及其嵌套类便属于协定类。 + +

    +

    实现内容提供程序权限

    +

    + 安全与权限主题中详细描述了 Android 系统各个方面的权限和访问。 + + 数据存储主题也描述了各类存储实行中的安全与权限。 + + 其中的要点简述如下: +

    +
      +
    • + 默认情况下,存储在设备内部存储上的数据文件是您的应用和提供程序的私有数据文件; + +
    • +
    • + 您创建的 {@link android.database.sqlite.SQLiteDatabase} 数据库是您的应用和提供程序的私有数据库; + +
    • +
    • + 默认情况下,您保存到外部存储的数据文件是公用可全局读取的数据文件。 +您无法使用内容提供程序来限制对外部存储内文件的访问,因为其他应用可以使用其他 API 调用来对它们执行读取和写入操作; + +
    • +
    • + 用于在您的设备的内部存储上打开或创建文件或 SQLite 数据库的方法调用可能会为所有其他应用同时授予读取和写入访问权限。 +如果您将内部文件或数据库用作提供程序的存储库,并向其授予“可全局读取”或“可全局写入”访问权限,则您在清单文件中为提供程序设置的权限不会保护您的数据。 + + +内部存储中文件和数据库的默认访问权限是“私有”,对于提供程序的存储库,您不应更改此权限。 + +
    • +
    +

    + 如果您想使用内容提供程序权限来控制对数据的访问,则应将数据存储在内部文件、SQLite 数据库或“云”中(例如,远程服务器上),而且您应该保持文件和数据库为您的应用所私有。 + + +

    +

    实现权限

    +

    + 即使底层数据为私有数据,所有应用仍可从您的提供程序读取数据或向其写入数据,因为在默认情况下,您的提供程序未设置权限。 +要想改变这种情况,请使用属性或 + + <provider> 元素的子元素在您的清单文件中为您的提供程序设置权限。 +您可以设置适用于整个提供程序、特定表、甚至特定记录的权限,或者设置同时适用于这三者的权限。 + +

    +

    + 您可以通过清单文件中的一个或多个 + + <permission> 元素为您的提供程序定义权限。要使权限对您的提供程序具有唯一性,请为 + + android:name 属性使用 Java 风格作用域。 +例如,将读取权限命名为 + com.example.app.provider.permission.READ_PROVIDER。 + +

    +

    + 以下列表描述了提供程序权限的作用域,从适用于整个提供程序的权限开始,然后逐渐细化。 + + 更细化的权限优先于作用域较大的权限: +

    +
    +
    + 统一读写提供程序级别权限 +
    +
    + 一个同时控制对整个提供程序读取和写入访问的权限,通过 + + <provider> 元素的 + android:permission 属性指定。 + +
    +
    + 单独的读取和写入提供程序级别权限 +
    +
    + 针对整个提供程序的读取权限和写入权限。您可以通过 + + <provider> 元素的 + android:readPermission 属性和 + + android:writePermission 属性 + 指定它们。它们优先于 + + android:permission 所需的权限。 +
    +
    + 路径级别权限 +
    +
    + 针对提供程序中内容 URI 的读取、写入或读取/写入权限。您可以通过 + + <provider> 元素的 + <path-permission> 子元素指定您想控制的每个 URI。 + +对于您指定的每个内容 URI,您都可以指定读取/写入权限、读取权限或写入权限,或同时指定所有三种权限。 +读取权限和写入权限优先于读取/写入权限。 +此外,路径级别权限优先于提供程序级别权限。 + +
    +
    + 临时权限 +
    +
    + 一种权限级别,即使应用不具备通常需要的权限,该级别也能授予对应用的临时访问权限。 +临时访问功能可减少应用需要在其清单文件中请求的权限数量。 + +当您启用临时权限时,只有持续访问您的所有数据的应用才需要“永久性”提供程序访问权限。 + + +

    + 假设您需要实现电子邮件提供程序和应用的权限,如果您想允许外部图像查看器应用显示您的提供程序中的照片附件, + +为了在不请求权限的情况下为图像查看器提供必要的访问权限,可以为照片的内容 URI 设置临时权限。 +对您的电子邮件应用进行相应设计,使应用能够在用户想要显示照片时向图像查看器发送一个 Intent,其中包含照片的内容 URI 以及权限标志。 + +图像查看器可随后查询您的电子邮件提供程序以检索照片,即使查看器不具备对您提供程序的正常读取权限,也不受影响。 + + +

    +

    + 要想启用临时权限,请设置 + + <provider> 元素的 + + android:grantUriPermissions 属性,或者向您的 + + <provider> 元素添加一个或多个 + + <grant-uri-permission> 子元素。如果您使用了临时权限,则每当您从提供程序中移除对某个内容 URI 的支持,并且该内容 URI 关联了临时权限时,都需要调用 + {@link android.content.Context#revokeUriPermission(Uri, int) + Context.revokeUriPermission()}。 + +

    +

    + 该属性的值决定可访问的提供程序范围。 + 如果该属性设置为 true,则系统会向整个提供程序授予临时权限,该权限将替代您的提供程序级别或路径级别权限所需的任何其他权限。 + + +

    +

    + 如果此标志设置为 false,则您必须向 + + <provider> 元素添加 + + <grant-uri-permission> 子元素。每个子元素都指定授予的临时权限所对应的一个或多个内容 URI。 + +

    +

    + 要向应用授予临时访问权限, Intent 必须包含 + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 和/或 + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 标志。它们通过 {@link android.content.Intent#setFlags(int) setFlags()} 方法进行设置。 + +

    +

    + 如果 + android:grantUriPermissions 属性不存在,则假设其为 + false。 +

    +
    +
    + + + + +

    <Provider> 元素

    +

    + 与 {@link android.app.Activity} 和 {@link android.app.Service} 组件类似,必须使用 + + <provider> 元素在清单文件中为其应用定义 + {@link android.content.ContentProvider} 的子类。 +Android 系统会从该元素获取以下信息: + +

    +
    + 权限 + ({@code + android:authorities}) +
    +
    + 用于在系统内标识整个提供程序的符号名称。设计内容 URI 部分对此属性做了更详尽的描述。 + + +
    +
    + 提供程序类名 + ( +android:name + ) +
    +
    + 实现 {@link android.content.ContentProvider} 的类。实现 ContentProvider 类中对此类做了更详尽的描述。 + + +
    +
    + 权限 +
    +
    + 指定其他应用访问提供程序的数据所必须具备权限的属性: + + +

    + 实现内容提供程序权限部分对权限及其对应属性做了更详尽的描述。 + + +

    +
    +
    + 启动和控制属性 +
    +
    + 这些属性决定 Android 系统如何以及何时启动提供程序、提供程序的进程特性以及其他运行时设置: + + +

    + 开发指南中针对 + + <provider> + 元素的主题提供了这些属性的完整资料。 +

    +
    +
    + 信息属性 +
    +
    + 提供程序的可选图标和标签: +
      +
    • + + android:icon:包含提供程序图标的 Drawable 资源。 + 该图标出现在设置 > 应用 > 全部 中应用列表内的提供程序标签旁; + +
    • +
    • + + android:label:描述提供程序和/或其数据的信息标签。 +该标签出现在设置 > 应用 > 全部中的应用列表内。 + +
    • +
    +

    + 开发指南中针对 + + <provider> 元素的主题提供了这些属性的完整资料。 +

    +
    +
    + + +

    Intent 和数据访问

    +

    + 应用可以通过 {@link android.content.Intent} 间接访问内容提供程序。 + 应用不会调用 {@link android.content.ContentResolver} 或 + {@link android.content.ContentProvider} 的任何方法,而会发送一个启动 Activity 的 Intent,该 Activity 通常是提供程序自身应用的一部分。 +目标 Activity 负责检索和显示其 UI 中的数据。视 Intent 中的操作而定,目标 Activity 可能还会提示用户对提供程序的数据进行修改。 + + + Intent 可能还包含目标 Activity 在 UI 中显示的“extra”数据;用户随后可以选择更改此数据,然后使用它来修改提供程序中的数据。 + + +

    +

    + +

    +

    + 您可能想使用 Intent 访问权限来帮助确保数据完整性。您的提供程序可能依赖于根据严格定义的业务逻辑插入、更新和删除数据。 +如果是这种情况,则允许其他应用直接修改您的数据可能会导致无效的数据。 + +如果您想让开发者使用 Intent 访问权限,请务必为其提供详尽的参考资料。 + 向他们解释为什么使用自身应用 UI 的 Intent 访问比尝试通过代码修改数据更好。 + +

    +

    + 处理想要修改您的提供程序数据的传入 Intent 与处理其他 Intent 没有区别。 +您可以通过阅读 Intent 和 Intent 过滤器主题了解有关 Intent 用法的更多信息。 + +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-providers.jd new file mode 100644 index 0000000000000000000000000000000000000000..80c778aad4bfeb2c17d3b4b47d4b36972d109c69 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=内容提供程序 +@jd:body + +

    + 内容提供程序管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 +内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。 + +

    +

    + 如果您想要访问内容提供程序中的数据,可以将应用的 {@link android.content.Context} 中的 {@link android.content.ContentResolver} 对象用作客户端来与提供程序通信。 + + + {@link android.content.ContentResolver} 对象会与提供程序对象(即实现 {@link android.content.ContentProvider} 的类实例)通信。 +提供程序对象从客户端接收数据请求,执行请求的操作并返回结果。 + + +

    +

    + 如果您不打算与其他应用共享数据,则无需开发自己的提供程序。 +不过,您需要通过自己的提供程序在您自己的应用中提供自定义搜索建议。 +如果您想将复杂的数据或文件从您的应用复制并粘贴到其他应用中,也需要创建您自己的提供程序。 + +

    +

    + Android 本身包括的内容提供程序可管理音频、视频、图像和个人联系信息等数据。 +android.provider + 软件包参考文档中列出了部分提供程序。 + +任何 Android 应用都可以访问这些提供程序,但会受到某些限制。 + +

    + 以下主题对内容提供程序做了更详尽的描述: +

    +
    +
    + + 内容提供程序基础知识 +
    +
    + 如何访问内容提供程序中以表形式组织的数据。 +
    +
    + + 创建内容提供程序 +
    +
    + 如何创建您自己的内容提供程序。 +
    +
    + + 日历提供程序 +
    +
    + 如何访问作为 Android 平台一部分的日历提供程序。 +
    +
    + + 联系人提供程序 +
    +
    + 如何访问作为 Android 平台一部分的联系人提供程序。 +
    +
    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/zh-cn/guide/topics/providers/document-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..fd36e2963606376d0f0c94202cfadfc4b2f1c12a --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=存储访问框架 +@jd:body + + + +

    Android 4.4(API 19 级)引入了存储访问框架 (SAF)。SAF +让用户能够在其所有首选文档存储提供程序中方便地浏览并打开文档、图像以及其他文件。 +用户可以通过易用的标准 UI,以统一方式在所有应用和提供程序中浏览文件和访问最近使用的文件。 +

    + +

    云存储服务或本地存储服务可以通过实现封装其服务的 +{@link android.provider.DocumentsProvider} 参与此生态系统。只需几行代码,便可将需要访问提供程序文档的客户端应用与 +SAF +集成。

    + +

    SAF 包括以下内容:

    + +
      +
    • 文档提供程序—一种内容提供程序,允许存储服务(如 Google +云端硬盘)显示其管理的文件。文档提供程序作为 +{@link android.provider.DocumentsProvider} +类的子类实现。文档提供程序的架构基于传统文件层次结构,但其实际数据存储方式由您决定。Android +平台包括若干内置文档提供程序,如 Downloads、Images 和 Videos; + +
    • + +
    • 客户端应用—一种自定义应用,它调用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和/或 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} Intent 并接收文档提供程序返回的文件; +
    • + +
    • 选取器—一种系统 +UI,允许用户访问所有满足客户端应用搜索条件的文档提供程序内的文档。
    • +
    + +

    SAF 提供的部分功能如下:

    +
      +
    • 允许用户浏览所有文档提供程序而不仅仅是单个应用中的内容;
    • +
    • 让您的应用获得对文档提供程序所拥有文档的长期、持久性访问权限。 +用户可以通过此访问权限添加、编辑、保存和删除提供程序上的文件; +
    • +
    • 支持多个用户帐户和临时根目录,如只有在插入驱动器后才会出现的 USB +存储提供程序。
    • +
    + +

    概览

    + +

    SAF 围绕的内容提供程序是 +{@link android.provider.DocumentsProvider} 类的一个子类。在文档提供程序内,数据结构采用传统的文件层次结构: +

    +

    data model

    +

    图 1. 文档提供程序数据模型。根目录指向单个文档,后者随即启动整个结构树的扇出。 +

    + +

    请注意以下事项:

    +
      + +
    • 每个文档提供程序都会报告一个或多个作为探索文档结构树起点的“根目录”。每个根目录都有一个唯一的 +{@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID},并且指向表示该根目录下内容的文档(目录)。根目录采用动态设计,以支持多个帐户、临时 +USB +存储设备或用户登录/注销等用例; + + +
    • + +
    • 每个根目录下都有一个文档。该文档指向 1 至 N +个文档,而其中每个文档又可指向 1 至 N 个文档;
    • + +
    • 每个存储后端都会通过使用唯一的 +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 引用各个文件和目录来显示它们。文档 +ID +必须具有唯一性,一旦发放便不得更改,因为它们用于所有设备重启过程中的永久性 +URI 授权;
    • + + +
    • 文档可以是可打开的文件(具有特定 MIME +类型)或包含附加文档的目录(具有 +{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR} MIME 类型);
    • + +
    • 每个文档都可以具有不同的功能,如 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} 所述。例如,{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}、{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE} +和 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}。多个目录中可以包含相同的 +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID}。 + +
    • +
    + +

    控制流

    +

    如前文所述,文档提供程序数据模型基于传统文件层次结构。 +不过,只要可以通过 +{@link android.provider.DocumentsProvider} API +访问数据,您实际上可以按照自己喜好的方式存储数据。例如,您可以使用基于标记的云存储来存储数据。

    + +

    图 2 中的示例展示的是照片应用如何利用 SAF +访问存储的数据:

    +

    app

    + +

    图 2. 存储访问框架流

    + +

    请注意以下事项:

    +
      + +
    • 在 SAF +中,提供程序和客户端并不直接交互。客户端请求与文件交互(即读取、编辑、创建或删除文件)的权限; +
    • + +
    • 交互在应用(在本示例中为照片应用)触发 Intent +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 或 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} 后开始。Intent 可能包括进一步细化条件的过滤器—例如,“为我提供所有 MIME +类型为‘图像’的可打开文件”; +
    • + +
    • Intent 触发后,系统选取器将检索每个已注册的提供程序,并向用户显示匹配的内容根目录; +
    • + +
    • 选取器会为用户提供一个标准的文档访问界面,但底层文档提供程序可能与其差异很大。 +例如,图 2 +显示了一个 Google 云端硬盘提供程序、一个 USB 提供程序和一个云提供程序。
    • +
    + +

    图 3 显示了一个选取器,一位搜索图像的用户在其中选择了一个 +Google 云端硬盘帐户:

    + +

    picker

    + +

    图 3. 选取器

    + +

    当用户选择 Google +云端硬盘时,系统会显示图像,如图 4 所示。从这时起,用户就可以通过提供程序和客户端应用支持的任何方式与它们进行交互。 + + +

    picker

    + +

    图 4. 图像

    + +

    编写客户端应用

    + +

    对于 Android 4.3 +及更低版本,如果您想让应用从其他应用中检索文件,它必须调用 {@link android.content.Intent#ACTION_PICK} +或 {@link android.content.Intent#ACTION_GET_CONTENT} 等 Intent。然后,用户必须选择一个要从中选取文件的应用,并且所选应用必须提供一个用户界面,以便用户浏览和选取可用文件。 + +

    + +

    对于 Android 4.4 及更高版本,您还可以选择使用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} + Intent,后者会显示一个由系统控制的选取器 +UI,用户可以通过它浏览其他应用提供的所有文件。用户只需通过这一个 UI +便可从任何受支持的应用中选取文件。

    + +

    {@link android.content.Intent#ACTION_OPEN_DOCUMENT} +并非设计用于替代 {@link android.content.Intent#ACTION_GET_CONTENT}。应使用的 Intent 取决于应用的需要: +

    + +
      +
    • 如果您只想让应用读取/导入数据,请使用 +{@link android.content.Intent#ACTION_GET_CONTENT}。使用此方法时,应用会导入数据(如图像文件)的副本; +
    • + +
    • 如果您想让应用获得对文档提供程序所拥有文档的长期、持久性访问权限,请使用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT}。 +例如,允许用户编辑存储在文档提供程序中的图像的照片编辑应用。 +
    • + +
    + + +

    本节描述如何编写基于 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} Intent 的客户端应用。

    + + + + +

    +以下代码段使用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} +来搜索包含图像文件的文档提供程序:

    + +
    private static final int READ_REQUEST_CODE = 42;
    +...
    +/**
    + * Fires an intent to spin up the "file chooser" UI and select an image.
    + */
    +public void performFileSearch() {
    +
    +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
    +    // browser.
    +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as a
    +    // file (as opposed to a list of contacts or timezones)
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Filter to show only images, using the image MIME data type.
    +    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
    +    // To search for all documents available via installed storage providers,
    +    // it would be "*/*".
    +    intent.setType("image/*");
    +
    +    startActivityForResult(intent, READ_REQUEST_CODE);
    +}
    + +

    请注意以下事项:

    +
      +
    • 当应用触发 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} + Intent 时,后者会启动一个选取器来显示所有匹配的文档提供程序
    • + +
    • 在 Intent 中添加类别 {@link android.content.Intent#CATEGORY_OPENABLE} +可对结果进行过滤,以仅显示可以打开的文档(如图像文件)
    • + +
    • 语句 {@code intent.setType("image/*")} 可做进一步过滤,以仅显示 +MIME 数据类型为图像的文档
    • +
    + +

    处理结果

    + +

    用户在选取器中选择文档后,系统就会调用 +{@link android.app.Activity#onActivityResult onActivityResult()}。指向所选文档的 URI +包含在 {@code resultData} +参数中。使用 {@link android.content.Intent#getData getData()} +提取 URI。获得 URI 后,即可使用它来检索用户想要的文档。例如: +

    + +
    @Override
    +public void onActivityResult(int requestCode, int resultCode,
    +        Intent resultData) {
    +
    +    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
    +    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
    +    // response to some other intent, and the code below shouldn't run at all.
    +
    +    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
    +        // The document selected by the user won't be returned in the intent.
    +        // Instead, a URI to that document will be contained in the return intent
    +        // provided to this method as a parameter.
    +        // Pull that URI using resultData.getData().
    +        Uri uri = null;
    +        if (resultData != null) {
    +            uri = resultData.getData();
    +            Log.i(TAG, "Uri: " + uri.toString());
    +            showImage(uri);
    +        }
    +    }
    +}
    +
    + +

    检查文档元数据

    + +

    获得文档的 URI 后,即可获得对其元数据的访问权限。以下代码段用于获取 URI +所指定文档的元数据并将其记入日志:

    + +
    public void dumpImageMetaData(Uri uri) {
    +
    +    // The query, since it only applies to a single document, will only return
    +    // one row. There's no need to filter, sort, or select fields, since we want
    +    // all fields for one document.
    +    Cursor cursor = getActivity().getContentResolver()
    +            .query(uri, null, null, null, null, null);
    +
    +    try {
    +    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
    +    // "if there's anything to look at, look at it" conditionals.
    +        if (cursor != null && cursor.moveToFirst()) {
    +
    +            // Note it's called "Display Name".  This is
    +            // provider-specific, and might not necessarily be the file name.
    +            String displayName = cursor.getString(
    +                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
    +            Log.i(TAG, "Display Name: " + displayName);
    +
    +            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
    +            // If the size is unknown, the value stored is null.  But since an
    +            // int can't be null in Java, the behavior is implementation-specific,
    +            // which is just a fancy term for "unpredictable".  So as
    +            // a rule, check if it's null before assigning to an int.  This will
    +            // happen often:  The storage API allows for remote files, whose
    +            // size might not be locally known.
    +            String size = null;
    +            if (!cursor.isNull(sizeIndex)) {
    +                // Technically the column stores an int, but cursor.getString()
    +                // will do the conversion automatically.
    +                size = cursor.getString(sizeIndex);
    +            } else {
    +                size = "Unknown";
    +            }
    +            Log.i(TAG, "Size: " + size);
    +        }
    +    } finally {
    +        cursor.close();
    +    }
    +}
    +
    + +

    打开文档

    + +

    获得文档的 URI +后,即可打开文档或对其执行任何其他您想要执行的操作。

    + +

    位图

    + +

    以下示例展示了如何打开 {@link android.graphics.Bitmap}:

    + +
    private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    +    ParcelFileDescriptor parcelFileDescriptor =
    +            getContentResolver().openFileDescriptor(uri, "r");
    +    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    +    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    +    parcelFileDescriptor.close();
    +    return image;
    +}
    +
    + +

    请注意,您不应在 UI 线程上执行此操作。请使用 +{@link android.os.AsyncTask} 在后台执行此操作。打开位图后,即可在 +{@link android.widget.ImageView} 中显示它。 +

    + +

    获取 InputStream

    + +

    以下示例展示了如何从 URI 中获取 +{@link java.io.InputStream}。在此代码段中,系统将文件行读取到一个字符串中:

    + +
    private String readTextFromUri(Uri uri) throws IOException {
    +    InputStream inputStream = getContentResolver().openInputStream(uri);
    +    BufferedReader reader = new BufferedReader(new InputStreamReader(
    +            inputStream));
    +    StringBuilder stringBuilder = new StringBuilder();
    +    String line;
    +    while ((line = reader.readLine()) != null) {
    +        stringBuilder.append(line);
    +    }
    +    fileInputStream.close();
    +    parcelFileDescriptor.close();
    +    return stringBuilder.toString();
    +}
    +
    + +

    创建新文档

    + +

    您的应用可以使用 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} + Intent 在文档提供程序中创建新文档。要想创建文件,请为您的 Intent 提供一个 MIME +类型和文件名,然后通过唯一的请求代码启动它。系统会为您执行其余操作:

    + + +
    +// Here are some examples of how you might call this method.
    +// The first parameter is the MIME type, and the second parameter is the name
    +// of the file you are creating:
    +//
    +// createFile("text/plain", "foobar.txt");
    +// createFile("image/png", "mypicture.png");
    +
    +// Unique request code.
    +private static final int WRITE_REQUEST_CODE = 43;
    +...
    +private void createFile(String mimeType, String fileName) {
    +    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as
    +    // a file (as opposed to a list of contacts or timezones).
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Create a file with the requested MIME type.
    +    intent.setType(mimeType);
    +    intent.putExtra(Intent.EXTRA_TITLE, fileName);
    +    startActivityForResult(intent, WRITE_REQUEST_CODE);
    +}
    +
    + +

    创建新文档后,即可在 +{@link android.app.Activity#onActivityResult onActivityResult()} 中获取其 +URI,以便继续向其写入内容。

    + +

    删除文档

    + +

    如果您获得了文档的 +URI,并且文档的 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} +包含 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},便可以删除该文档。例如:

    + +
    +DocumentsContract.deleteDocument(getContentResolver(), uri);
    +
    + +

    编辑文档

    + +

    您可以使用 SAF +就地编辑文本文档。以下代码段会触发 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} Intent +并使用类别 {@link android.content.Intent#CATEGORY_OPENABLE} +以仅显示可以打开的文档。它会进一步过滤以仅显示文本文件:

    + +
    +private static final int EDIT_REQUEST_CODE = 44;
    +/**
    + * Open a file for writing and append some text to it.
    + */
    + private void editDocument() {
    +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
    +    // file browser.
    +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    +
    +    // Filter to only show results that can be "opened", such as a
    +    // file (as opposed to a list of contacts or timezones).
    +    intent.addCategory(Intent.CATEGORY_OPENABLE);
    +
    +    // Filter to show only text files.
    +    intent.setType("text/plain");
    +
    +    startActivityForResult(intent, EDIT_REQUEST_CODE);
    +}
    +
    + +

    接下来,您可以从 +{@link android.app.Activity#onActivityResult onActivityResult()}(请参阅处理结果)调用代码以执行编辑。以下代码段可从 +{@link android.content.ContentResolver} 获取 +{@link java.io.FileOutputStream}。默认情况下,它使用“写入”模式。最佳做法是请求获得所需的最低限度访问权限,因此如果您只需要写入权限,就不要请求获得读取/写入权限。 + +

    + +
    private void alterDocument(Uri uri) {
    +    try {
    +        ParcelFileDescriptor pfd = getActivity().getContentResolver().
    +                openFileDescriptor(uri, "w");
    +        FileOutputStream fileOutputStream =
    +                new FileOutputStream(pfd.getFileDescriptor());
    +        fileOutputStream.write(("Overwritten by MyCloud at " +
    +                System.currentTimeMillis() + "\n").getBytes());
    +        // Let the document provider know you're done by closing the stream.
    +        fileOutputStream.close();
    +        pfd.close();
    +    } catch (FileNotFoundException e) {
    +        e.printStackTrace();
    +    } catch (IOException e) {
    +        e.printStackTrace();
    +    }
    +}
    + +

    保留权限

    + +

    当您的应用打开文件进行读取或写入时,系统会为您的应用提供针对该文件的 +URI 授权。该授权将一直持续到用户设备重启时。但假定您的应用是图像编辑应用,而且您希望用户能够直接从应用中访问他们编辑的最后 +5 +张图像。如果用户的设备已经重启,您就需要将用户转回系统选取器以查找这些文件,这显然不是理想的做法。 + +

    + +

    为防止出现这种情况,您可以保留系统为您的应用授予的权限。您的应用实际上是“获取”了系统提供的持久 +URI +授权。这使用户能够通过您的应用持续访问文件,即使设备已重启也不受影响: +

    + + +
    final int takeFlags = intent.getFlags()
    +            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
    +            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    +// Check for the freshest data.
    +getContentResolver().takePersistableUriPermission(uri, takeFlags);
    + +

    还有最后一个步骤。您可能已经保存了应用最近访问的 +URI,但它们可能不再有效—另一个应用可能已删除或修改了文档。 +因此,您应该始终调用 +{@code getContentResolver().takePersistableUriPermission()} +以检查有无最新数据。

    + +

    编写自定义文档提供程序

    + +

    +如果您要开发为文件提供存储服务(如云保存服务)的应用,可以通过编写自定义文档提供程序,通过 +SAF +提供您的文件。本节描述如何执行此操作。 +

    + + +

    清单文件

    + +

    要想实现自定义文档提供程序,请将以下内容添加到您的应用的清单文件: +

    +
      + +
    • 一个 API 19 级或更高级别的目标;
    • + +
    • 一个声明自定义存储提供程序的 +<provider> 元素;
    • + +
    • 提供程序的名称(即其类名),包括软件包名称。例如:com.example.android.storageprovider.MyCloudProvider; +
    • + +
    • 权限的名称,即您的软件包名称(在本例中为 +com.example.android.storageprovider)加内容提供程序的类型 +(documents)。例如,{@code com.example.android.storageprovider.documents};
    • + +
    • 属性 android:exported 设置为 +"true"。您必须导出提供程序,以便其他应用可以看到;
    • + +
    • 属性 android:grantUriPermissions 设置为 +"true"。此设置允许系统向其他应用授予对提供程序中内容的访问权限。 +如需查看有关保留对特定文档授权的阐述,请参阅保留权限; +
    • + +
    • {@code MANAGE_DOCUMENTS} 权限。默认情况下,提供程序对所有人可用。 +添加此权限将限定您的提供程序只能供系统使用。此限制具有重要的安全意义; +
    • + +
    • {@code android:enabled} +属性设置为在资源文件中定义的一个布尔值。此属性的用途是,在运行 Android 4.3 +或更低版本的设备上禁用提供程序。例如,{@code android:enabled="@bool/atLeastKitKat"}。除了在清单文件中加入此属性外,您还需要执行以下操作; + +
        +
      • 在 {@code res/values/} 下的 {@code bool.xml} +资源文件中,添加以下行
        <bool name="atLeastKitKat">false</bool>
      • + +
      • 在 {@code res/values-v19/} 下的 {@code bool.xml} +资源文件中,添加以下行
        <bool name="atLeastKitKat">true</bool>
      • +
    • + +
    • 一个包括 +{@code android.content.action.DOCUMENTS_PROVIDER} 操作的 Intent +过滤器,以便在系统搜索提供程序时让您的提供程序出现在选取器中。
    • + +
    +

    以下是从一个包括提供程序的示例清单文件中摘录的内容:

    + +
    <manifest... >
    +    ...
    +    <uses-sdk
    +        android:minSdkVersion="19"
    +        android:targetSdkVersion="19" />
    +        ....
    +        <provider
    +            android:name="com.example.android.storageprovider.MyCloudProvider"
    +            android:authorities="com.example.android.storageprovider.documents"
    +            android:grantUriPermissions="true"
    +            android:exported="true"
    +            android:permission="android.permission.MANAGE_DOCUMENTS"
    +            android:enabled="@bool/atLeastKitKat">
    +            <intent-filter>
    +                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
    +            </intent-filter>
    +        </provider>
    +    </application>
    +
    +</manifest>
    + +

    支持运行 Android 4.3 及更低版本的设备

    + +

    {@link android.content.Intent#ACTION_OPEN_DOCUMENT} + Intent 仅可用于运行 +Android 4.4 及更高版本的设备。如果您想让应用支持 {@link android.content.Intent#ACTION_GET_CONTENT} +以适应运行 Android 4.3 +及更低版本的设备,则应在您的清单文件中为运行 Android 4.4 +或更高版本的设备禁用 {@link android.content.Intent#ACTION_GET_CONTENT} Intent +过滤器。应将文档提供程序和 +{@link android.content.Intent#ACTION_GET_CONTENT} +视为具有互斥性。如果您同时支持这两者,您的应用将在系统选取器 +UI +中出现两次,提供两种不同的方式来访问您存储的数据。这会给用户造成困惑。

    + +

    建议按照以下步骤为运行 Android 4.4 版或更高版本的设备禁用 +{@link android.content.Intent#ACTION_GET_CONTENT} Intent +过滤器:

    + +
      +
    1. 在 {@code res/values/} 下的 {@code bool.xml} +资源文件中,添加以下行
      <bool name="atMostJellyBeanMR2">true</bool>
    2. + +
    3. 在 {@code res/values-v19/} 下的 {@code bool.xml} +资源文件中,添加以下行
      <bool name="atMostJellyBeanMR2">false</bool>
    4. + +
    5. 添加一个Activity别名,为 +4.4 版(API 19 +级)或更高版本禁用 {@link android.content.Intent#ACTION_GET_CONTENT} Intent +过滤器。例如: + +
      +<!-- This activity alias is added so that GET_CONTENT intent-filter
      +     can be disabled for builds on API level 19 and higher. -->
      +<activity-alias android:name="com.android.example.app.MyPicker"
      +        android:targetActivity="com.android.example.app.MyActivity"
      +        ...
      +        android:enabled="@bool/atMostJellyBeanMR2">
      +    <intent-filter>
      +        <action android:name="android.intent.action.GET_CONTENT" />
      +        <category android:name="android.intent.category.OPENABLE" />
      +        <category android:name="android.intent.category.DEFAULT" />
      +        <data android:mimeType="image/*" />
      +        <data android:mimeType="video/*" />
      +    </intent-filter>
      +</activity-alias>
      +
      +
    6. +
    +

    协定类

    + +

    通常,当您编写自定义内容提供程序时,其中一项任务是实现协定类,如内容提供程序开发者指南中所述。 + + +协定类是一种 {@code public final} 类,它包含对 +URI、列名称、MIME +类型以及其他与提供程序有关的元数据的常量定义。SAF +会为您提供这些协定类,因此您无需自行编写: +

    + +
      +
    • {@link android.provider.DocumentsContract.Document}
    • +
    • {@link android.provider.DocumentsContract.Root}
    • +
    + +

    例如,当系统在您的文档提供程序中查询文档或根目录时,您可能会在游标中返回以下列: +

    + +
    private static final String[] DEFAULT_ROOT_PROJECTION =
    +        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
    +        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
    +        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
    +        Root.COLUMN_AVAILABLE_BYTES,};
    +private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
    +        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
    +        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
    +        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
    +
    + +

    为 DocumentsProvider 创建子类

    + +

    编写自定义文档提供程序的下一步是为抽象类 +{@link android.provider.DocumentsProvider} 创建子类。您至少需要实现以下方法: +

    + +
      +
    • {@link android.provider.DocumentsProvider#queryRoots queryRoots()}
    • + +
    • {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
    • + +
    • {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
    • + +
    • {@link android.provider.DocumentsProvider#openDocument openDocument()}
    • +
    + +

    这些只是您需要严格实现的方法,但您可能还想实现许多其他方法。 +详情请参阅 +{@link android.provider.DocumentsProvider}。

    + +

    实现 queryRoots

    + +

    您实现的 +{@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 必须使用在 +{@link android.provider.DocumentsContract.Root} 中定义的列返回一个指向文档提供程序所有根目录的 {@link android.database.Cursor}。

    + +

    在以下代码段中,{@code projection} +参数表示调用方想要返回的特定字段。代码段会创建一个新游标,并为其添加一行—一个根目录,如 +Downloads 或 Images +等顶层目录。大多数提供程序只有一个根目录。有时您可能有多个根目录,例如,当您具有多个用户帐户时。 +在这种情况下,只需再为游标添加一行。 +

    + +
    +@Override
    +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    +
    +    // Create a cursor with either the requested fields, or the default
    +    // projection if "projection" is null.
    +    final MatrixCursor result =
    +            new MatrixCursor(resolveRootProjection(projection));
    +
    +    // If user is not logged in, return an empty root cursor.  This removes our
    +    // provider from the list entirely.
    +    if (!isUserLoggedIn()) {
    +        return result;
    +    }
    +
    +    // It's possible to have multiple roots (e.g. for multiple accounts in the
    +    // same app) -- just add multiple cursor rows.
    +    // Construct one row for a root called "MyCloud".
    +    final MatrixCursor.RowBuilder row = result.newRow();
    +    row.add(Root.COLUMN_ROOT_ID, ROOT);
    +    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
    +
    +    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
    +    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
    +    // recently used documents will show up in the "Recents" category.
    +    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
    +    // shares.
    +    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
    +            Root.FLAG_SUPPORTS_RECENTS |
    +            Root.FLAG_SUPPORTS_SEARCH);
    +
    +    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
    +    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
    +
    +    // This document id cannot change once it's shared.
    +    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
    +
    +    // The child MIME types are used to filter the roots and only present to the
    +    //  user roots that contain the desired type somewhere in their file hierarchy.
    +    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
    +    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
    +    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
    +
    +    return result;
    +}
    + +

    实现 queryChildDocuments

    + +

    您实现的 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 必须使用在 +{@link android.provider.DocumentsContract.Document} +中定义的列返回一个指向指定目录中所有文件的 +{@link android.database.Cursor}。

    + +

    当您在选取器 UI +中选择应用时,系统会调用此方法。它会获取根目录下某个目录内的子文档。可以在文件层次结构的任何级别调用此方法,并非只能从根目录调用。 +以下代码段可创建一个包含所请求列的新游标,然后向游标添加父目录中每个直接子目录的相关信息。子目录可以是图像、另一个目录乃至任何文件: + + +

    + +
    @Override
    +public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
    +                              String sortOrder) throws FileNotFoundException {
    +
    +    final MatrixCursor result = new
    +            MatrixCursor(resolveDocumentProjection(projection));
    +    final File parent = getFileForDocId(parentDocumentId);
    +    for (File file : parent.listFiles()) {
    +        // Adds the file's display name, MIME type, size, and so on.
    +        includeFile(result, null, file);
    +    }
    +    return result;
    +}
    +
    + +

    实现 queryDocument

    + +

    您实现的 +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} +必须使用在 {@link android.provider.DocumentsContract.Document} 中定义的列返回一个指向指定文件的 +{@link android.database.Cursor}。 +

    + +

    除了特定文件的信息外,{@link android.provider.DocumentsProvider#queryDocument queryDocument()} +方法返回的信息与 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} +中传递的信息相同:

    + + +
    @Override
    +public Cursor queryDocument(String documentId, String[] projection) throws
    +        FileNotFoundException {
    +
    +    // Create a cursor with the requested projection, or the default projection.
    +    final MatrixCursor result = new
    +            MatrixCursor(resolveDocumentProjection(projection));
    +    includeFile(result, documentId, null);
    +    return result;
    +}
    +
    + +

    实现 queryDocument

    + +

    您必须实现 {@link android.provider.DocumentsProvider#openDocument +openDocument()} 以返回表示指定文件的 +{@link android.os.ParcelFileDescriptor}。其他应用可以使用返回的 {@link android.os.ParcelFileDescriptor} +来流式传输数据。用户选择了文件,并且客户端应用通过调用 +{@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()} +来请求对文件的访问权限,系统便会调用此方法。例如: +

    + +
    @Override
    +public ParcelFileDescriptor openDocument(final String documentId,
    +                                         final String mode,
    +                                         CancellationSignal signal) throws
    +        FileNotFoundException {
    +    Log.v(TAG, "openDocument, mode: " + mode);
    +    // It's OK to do network operations in this method to download the document,
    +    // as long as you periodically check the CancellationSignal. If you have an
    +    // extremely large file to transfer from the network, a better solution may
    +    // be pipes or sockets (see ParcelFileDescriptor for helper methods).
    +
    +    final File file = getFileForDocId(documentId);
    +
    +    final boolean isWrite = (mode.indexOf('w') != -1);
    +    if(isWrite) {
    +        // Attach a close listener if the document is opened in write mode.
    +        try {
    +            Handler handler = new Handler(getContext().getMainLooper());
    +            return ParcelFileDescriptor.open(file, accessMode, handler,
    +                        new ParcelFileDescriptor.OnCloseListener() {
    +                @Override
    +                public void onClose(IOException e) {
    +
    +                    // Update the file with the cloud server. The client is done
    +                    // writing.
    +                    Log.i(TAG, "A file with id " +
    +                    documentId + " has been closed!
    +                    Time to " +
    +                    "update the server.");
    +                }
    +
    +            });
    +        } catch (IOException e) {
    +            throw new FileNotFoundException("Failed to open document with id "
    +            + documentId + " and mode " + mode);
    +        }
    +    } else {
    +        return ParcelFileDescriptor.open(file, accessMode);
    +    }
    +}
    +
    + +

    安全性

    + +

    假设您的文档提供程序是受密码保护的云存储服务,并且您想在开始共享用户的文件之前确保其已登录。如果用户未登录,您的应用应该执行哪些操作呢? + +解决方案是在您实现的 +{@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 中返回零个根目录。也就是空的根目录游标:

    + +
    +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
    +...
    +    // If user is not logged in, return an empty root cursor.  This removes our
    +    // provider from the list entirely.
    +    if (!isUserLoggedIn()) {
    +        return result;
    +}
    +
    + +

    另一个步骤是调用 +{@code getContentResolver().notifyChange()}。还记得 {@link android.provider.DocumentsContract} 吗?我们将使用它来创建此 +URI。以下代码段会在每次用户的登录状态发生变化时指示系统查询文档提供程序的根目录。 +如果用户未登录,则调用 +{@link android.provider.DocumentsProvider#queryRoots queryRoots()} +会返回一个空游标,如上文所示。这可以确保只有在用户登录提供程序后其中的文档才可用。 +

    + +
    private void onLoginButtonClick() {
    +    loginOrLogout();
    +    getContentResolver().notifyChange(DocumentsContract
    +            .buildRootsUri(AUTHORITY), null);
    +}
    +
    \ No newline at end of file diff --git a/docs/html-intl/intl/zh-cn/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/zh-cn/guide/topics/resources/accessing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..e8e583a7307f4389705b03f64476d8941f095736 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=访问资源 +parent.title=应用资源 +parent.link=index.html +@jd:body + +
    +
    +

    内容快览

    +
      +
    • 可以使用 +{@code R.drawable.myimage} 等来自 {@code R.java} 的整型数在代码中引用资源
    • +
    • 可以使用 {@code +@drawable/myimage} 等特殊 XML 语法在资源中引用资源
    • +
    • 您还可以通过 +{@link android.content.res.Resources} 中的方法访问您的应用资源
    • +
    + +

    关键类

    +
      +
    1. {@link android.content.res.Resources}
    2. +
    + +

    本文内容

    +
      +
    1. 在代码中访问资源
    2. +
    3. 在 XML 中访问资源 +
        +
      1. 引用样式属性
      2. +
      +
    4. +
    5. 访问平台资源
    6. +
    + +

    另请参阅

    +
      +
    1. 提供资源
    2. +
    3. 资源类型
    4. +
    +
    +
    + + + + +

    您在应用中提供资源后(提供资源中对此做了阐述),可通过引用其资源 ID 来应用该资源。所有资源 ID 都在您项目的 {@code R} 类中定义,后者由 {@code aapt} 工具自动生成。 + +

    + +

    编译应用时,{@code aapt} 会生成 {@code R} 类,其中包含您的 {@code +res/} 目录中所有资源的资源 ID。 +每个资源类型都有对应的 {@code R} +子类(例如,{@code R.drawable} +对应于所有 Drawable 资源),而该类型的每个资源都有对应的静态整型数(例如,{@code R.drawable.icon})。这个整型数就是可用来检索资源的资源 +ID。

    + +

    尽管资源 ID 是在 {@code R} 类中指定的,但您应该永远都不需要在其中查找资源 +ID。资源 ID 始终由以下部分组成:

    +
      +
    • 资源类型:每个资源都被分到一个“类型”组中,例如 {@code +string}、{@code drawable} 和 {@code layout}。如需了解有关不同类型的详细信息,请参阅资源类型。 +
    • +
    • 资源名称,它是不包括扩展名的文件名;或是 XML +{@code android:name} +属性中的值,如果资源是简单值的话(例如字符串)。
    • +
    + +

    访问资源的方法有两种:

    +
      +
    • 在代码中:使用来自 {@code R} +类的某个子类的静态整型数,例如: +
      R.string.hello
      +

      {@code string} 是资源类型,{@code hello} 是资源名称。当您提供此格式的资源 ID 时,有许多 +Android API 可以访问您的资源。请参阅在代码中访问资源。 +

      +
    • +
    • 在 XML 中:使用同样与您 +{@code R} 类中定义的资源 ID 对应的特殊 XML 语法,例如: +
      @string/hello
      +

      {@code string} 是资源类型,{@code hello} 是资源名称。您可以在 XML +资源中任何应该存在您在资源中所提供值的地方使用此语法。请参阅在 XML 中访问资源

      +
    • +
    + + + +

    在代码中访问资源

    + +

    您可以通过以方法参数的形式传递资源 ID,在代码中使用资源。例如,您可以设置一个 +{@link android.widget.ImageView},以利用 {@link android.widget.ImageView#setImageResource(int) setImageResource()} 使用 {@code res/drawable/myimage.png} +资源:

    +
    +ImageView imageView = (ImageView) findViewById(R.id.myimageview);
    +imageView.setImageResource(R.drawable.myimage);
    +
    + +

    您还可以利用 {@link +android.content.res.Resources} 中的方法检索个别资源,您可以通过 {@link android.content.Context#getResources()} +获得资源实例。

    + + + + +

    语法

    + +

    以下是在代码中引用资源的语法:

    + +
    +[<package_name>.]R.<resource_type>.<resource_name>
    +
    + +
      +
    • {@code <package_name>} +是资源所在包的名称(如果引用的资源来自您自己的资源包,则不需要)。
    • +
    • {@code <resource_type>} 是资源类型的 {@code R} 子类。
    • +
    • {@code <resource_name>} +是不带扩展名的资源文件名,或 XML 元素中的 {@code android:name} +属性值(如果资源是简单值)。
    • +
    +

    如需了解有关各资源类型及其引用方法的详细信息,请参阅资源类型。 +

    + + +

    用例

    + +

    有许多方法接受资源 ID 参数,您可以利用 {@link android.content.res.Resources} +中的方法检索资源。您可以通过 {@link android.content.Context#getResources +Context.getResources()} 获得 {@link +android.content.res.Resources} 的实例。

    + + +

    以下是一些在代码中访问资源的示例:

    + +
    +// Load a background for the current screen from a drawable resource
    +{@link android.app.Activity#getWindow()}.{@link
    +android.view.Window#setBackgroundDrawableResource(int)
    +setBackgroundDrawableResource}(R.drawable.my_background_image) ;
    +
    +// Set the Activity title by getting a string from the Resources object, because
    +//  this method requires a CharSequence rather than a resource ID
    +{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence)
    +setTitle}(getResources().{@link android.content.res.Resources#getText(int)
    +getText}(R.string.main_title));
    +
    +// Load a custom layout for the current screen
    +{@link android.app.Activity#setContentView(int)
    +setContentView}(R.layout.main_screen);
    +
    +// Set a slide in animation by getting an Animation from the Resources object
    +mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation)
    +setInAnimation}(AnimationUtils.loadAnimation(this,
    +        R.anim.hyperspace_in));
    +
    +// Set the text on a TextView object using a resource ID
    +TextView msgTextView = (TextView) findViewById(R.id.msg);
    +msgTextView.{@link android.widget.TextView#setText(int)
    +setText}(R.string.hello_message);
    +
    + + +

    注意:切勿手动修改 {@code +R.java} 文件 — 它是在编译您的项目时由 {@code aapt} +工具生成的。您下次编译时所有更改都会被替代。

    + + + +

    在 XML 中访问资源

    + +

    您可以利用对现有资源的引用为某些 XML +属性和元素定义值。创建布局文件时,为给您的小工具提供字符串和图像,您经常要这样做。 +

    + +

    例如,如果您为布局添加一个 +{@link android.widget.Button},应该为按钮文本使用字符串资源

    + +
    +<Button
    +    android:layout_width="fill_parent"
    +    android:layout_height="wrap_content"
    +    android:text="@string/submit" />
    +
    + + +

    语法

    + +

    以下是在 XML 资源中引用资源的语法:

    + +
    +@[<package_name>:]<resource_type>/<resource_name>
    +
    + +
      +
    • {@code <package_name>} +是资源所在包的名称(如果引用的资源来自同一包,则不需要)
    • +
    • {@code <resource_type>} 是资源类型的 +{@code R} 子类
    • +
    • {@code <resource_name>} +是不带扩展名的资源文件名,或 XML 元素中的 {@code android:name} +属性值(如果资源是简单值)。
    • +
    + +

    如需了解有关各资源类型及其引用方法的详细信息,请参阅资源类型。 +

    + + +

    用例

    + +

    在某些情况下,您必须使用资源作为 XML +中的值(例如,对小工具应用可绘制图像),但您也可以在 XML 中任何接受简单值的地方使用资源。例如,如果您具有以下资源文件,其中包括一个颜色资源和一个字符串资源: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +   <color name="opaque_red">#f00</color>
    +   <string name="hello">Hello!</string>
    +</resources>
    +
    + +

    您可以在以下布局文件中使用这些资源来设置文本颜色和文本字符串: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:textColor="@color/opaque_red"
    +    android:text="@string/hello" />
    +
    + +

    在此情况下,您无需在资源引用中指定包名称,因为资源来自您自己的资源包。 +要引用系统资源,您需要加入包名称。 +例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:textColor="@android:color/secondary_text_dark"
    +    android:text="@string/hello" />
    +
    + +

    注:您应该始终使用字符串 +资源,以便将您的应用本地化为其他语言。如需了解有关创建备用资源(例如本地化字符串)的信息,请参阅提供备用资源。 + + +如需查看将您的应用本地化为其他语言的完整指南,请参阅本地化。 +

    + +

    您甚至可以在 XML 中使用资源创建别名。例如,您可以创建一个 Drawable 资源,将其作为另一个 Drawable 资源的别名: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:src="@drawable/other_drawable" />
    +
    + +

    这听起来多余,但对使用备用资源可能很有帮助。阅读更多关于创建别名资源的内容。 +

    + + + +

    引用样式属性

    + +

    您可以通过样式属性资源在当前应用的风格主题中引用某个属性的值。 +通过引用样式属性,您可以不采用为 UI +元素提供硬编码值这种方式,而是通过为 UI +元素设置样式,使其匹配当前风格主题提供的标准变型来定制这些元素的外观。引用样式属性的实质作用是,“在当前风格主题中使用此属性定义的样式”。 +

    + +

    要引用样式属性,名称语法几乎与普通资源格式完全相同,只不过将 at 符号 ({@code @}) 改为问号 ({@code ?}),资源类型部分为可选项。 + +例如:

    + +
    +?[<package_name>:][<resource_type>/]<resource_name>
    +
    + +

    例如,您可以通过以下代码引用一个属性,将文本颜色设置为与系统风格主题的“主要”文本颜色匹配: +

    + +
    +<EditText id="text"
    +    android:layout_width="fill_parent"
    +    android:layout_height="wrap_content"
    +    android:textColor="?android:textColorSecondary"
    +    android:text="@string/hello_world" />
    +
    + +

    在以上代码中,{@code android:textColor} +属性表示当前风格主题中某个样式属性的名称。Android 现在会使用应用于 {@code android:textColorSecondary} +样式属性的值作为 {@code android:textColor} 在这个小工具中的值。由于系统资源工具知道此环境中肯定存在某个属性资源,因此您无需显式声明类型(类型应为 +?android:attr/textColorSecondary)— 您可以将 +{@code attr} +类型排除在外。

    + + + + +

    访问平台资源

    + +

    Android 包含许多标准资源,例如样式、风格主题和布局。要访问这些资源,请通过 +android +包名称限定您的资源引用。例如,您可以将 Android 提供的布局资源用于 +{@link android.widget.ListAdapter} 中的列表项:

    + +
    +{@link android.app.ListActivity#setListAdapter(ListAdapter)
    +setListAdapter}(new {@link
    +android.widget.ArrayAdapter}<String>(this, android.R.layout.simple_list_item_1, myarray));
    +
    + +

    在上例中,{@link android.R.layout#simple_list_item_1} +是平台为 {@link android.widget.ListView} 中的项目定义的布局资源。您可以使用它,而不必自行创建列表项布局。 +如需了解详细信息,请参阅列表视图开发指南。 +

    + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/resources/overview.jd b/docs/html-intl/intl/zh-cn/guide/topics/resources/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..21dbe6ff37a932704fc7a34ea62a803e27e45d59 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=资源概览 +@jd:body + +
    +
    +

    主题

    +
      +
    1. 提供资源
    2. +
    3. 访问资源
    4. +
    5. 处理运行时变更
    6. +
    7. 本地化
    8. +
    + +

    参考文档

    +
      +
    1. 资源类型
    2. +
    +
    +
    + + +

    您应该始终外部化资源,例如图像和应用代码中的字符串,这样有利于您单独维护这些资源。 +此外,通过外部化资源,您还可以提供支持特定设备配置(例如,不同的语言或屏幕尺寸)的备用资源,随着采用 +Android + 技术且配置各异的设备越来越多,这些资源的重要性也日益增加。 +为了提供与不同配置的兼容性,您必须使用各种按类型和配置对资源进行分组的子目录,对项目 +{@code res/} +目录中的资源加以组织。 +

    + +
    + +

    +图 1. 两种不同的设备,均使用默认布局(应用不提供备用布局)。 +

    +
    + +
    + +

    +图 2. 两种不同的设备,分别使用针对不同屏幕尺寸提供的不同布局。 +

    +
    + +

    对于任意类型的资源,您均可以为应用指定默认资源和多个备用资源: +

    +
      +
    • 默认资源系指无论设备配置如何,或者在没有备用资源与当前配置相匹配时,均应使用的资源。 + +
    • +
    • 备用资源系指设计用于特定配置的资源。 +要指明某组资源适用于特定配置,请将相应的配置限定符追加到目录名称。 +
    • +
    + +

    例如,尽管默认 UI +布局保存在 +{@code res/layout/} 目录中,但是您可以指定在屏幕处于横向时要使用的不同布局,方法是将其保存在 {@code res/layout-land/} +目录中。Android +可以通过将设备的当前配置与资源目录名称进行匹配,自动应用合适的资源。

    + +

    图 1 +说明了在没有备用资源可用时,系统如何为两种不同的设备应用相同布局。图 2 +显示的是同一应用针对大屏幕添加了备用布局资源。

    + +

    以下文档提供了有关如何组织应用资源、如何指定备用资源以及如何在应用中访问这些资源等的完整指南: +

    + +
    +
    提供资源
    +
    您可在应用中提供何种资源、可将这些资源保存在何处以及如何为特定设备配置创建备用资源。 +
    +
    访问资源
    +
    如何通过从应用代码或其他 +XML 资源中引用来使用所提供的资源
    +
    处理运行时变更
    +
    如何管理在 Activity 运行时发生的配置变更。
    +
    本地化
    +
    从点到面地指导您使用备用资源本地化应用。尽管这只是备用资源的一种具体运用,但是这对于赢得更多用户非常重要。 + +
    +
    资源类型
    +
    有关您可提供的各种资源类型的参考文档,其中描述了这些资源的 +XML 元素、属性和语法。例如,此参考文档向您展示了如何为应用菜单、可绘制对象、动画等创建资源。 +
    +
    + + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/zh-cn/guide/topics/resources/providing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..ea46d86152a4a854ff1319302ab4105a8d7363bc --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=提供资源 +parent.title=应用资源 +parent.link=index.html +@jd:body + +
    +
    +

    内容快览

    +
      +
    • 不同类型的资源属于 {@code res/} 的不同子目录
    • +
    • 备用资源提供特定于配置的资源文件
    • +
    • 始终包含默认资源,使您的应用不依赖于特定的设备配置 +
    • +
    +

    本文内容

    +
      +
    1. 分组资源类型
    2. +
    3. 提供备用资源 +
        +
      1. 限定符命名规则
      2. +
      3. 创建别名资源
      4. +
      +
    4. +
    5. 利用资源提供最佳设备兼容性
    6. +
    7. Android 如何找到最匹配资源
    8. +
    + +

    另请参阅

    +
      +
    1. 访问资源
    2. +
    3. 资源类型
    4. +
    5. 支持多个屏幕 +
    6. +
    +
    +
    + +

    您应该始终外部化应用资源,例如图像和代码中的字符串,这样有利于您单独维护这些资源。 +此外,您还应该为特定设备配置提供备用资源,方法是将它们分组到专门命名的资源目录中。 +在运行时,Android +会根据当前配置使用适当的资源。例如,您可能需要根据屏幕尺寸提供不同的 +UI +布局,或者根据语言设置提供不同的字符串。

    + +

    外部化应用资源后,即可使用在项目 {@code R} 类中生成的资源 +ID 访问这些资源。有关如何在应用中使用资源,我们将在访问资源中讨论。 + +本文档介绍如何对 Android +项目中的资源进行分组,以及如何为特定的设备配置提供备用资源。

    + + +

    分组资源类型

    + +

    您应将各种资源放入项目 +{@code res/} 目录的特定子目录下。例如,以下是一个简单项目的文件层次结构:

    + +
    +MyProject/
    +    src/  
    +        MyActivity.java  
    +    res/
    +        drawable/  
    +            graphic.png  
    +        layout/  
    +            main.xml
    +            info.xml
    +        mipmap/  
    +            icon.png 
    +        values/  
    +            strings.xml  
    +
    + +

    正如您在此示例中所看到的那样,{@code res/} 目录包含所有资源(在子目录下):一个图像资源、两个布局资源、启动器图标的 +{@code mipmap/} +目录以及一个字符串资源文件。资源目录名称非常重要,将在表 1 +中进行介绍。

    + +

    注:如需了解有关使用 mipmap +文件夹的详细信息,请参阅管理项目概览

    + +

    表 1. 项目 {@code res/} +目录内支持的资源目录

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    目录资源类型
    animator/用于定义属性动画的 XML +文件。
    anim/定义渐变动画的 XML +文件。(属性动画也可以保存在此目录中,但是为了区分这两种类型,属性动画首选 +{@code animator/} +目录。)
    color/用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源 +
    drawable/

    位图文件({@code .png}、{@code .9.png}、{@code .jpg}、{@code .gif})或编译为以下 Drawable 资源子类型的 +XML 文件:

    +
      +
    • 位图文件
    • +
    • 九宫格(可调整大小的位图)
    • +
    • 状态列表
    • +
    • 形状
    • +
    • 动画 Drawable
    • +
    • 其他 Drawable
    • +
    +

    请参阅 Drawable 资源

    +
    mipmap/适用于不同启动器图标密度的 Drawable 文件。如需了解有关使用 {@code mipmap/} 文件夹管理启动器图标的详细信息,请参阅管理项目概览。 + +
    layout/用于定义用户界面布局的 XML 文件。 + 请参阅布局资源
    menu/用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML +文件。请参阅菜单资源
    raw/

    要以原始形式保存的任意文件。要使用原始 +{@link java.io.InputStream} 打开这些资源,请使用资源 ID(即 {@code R.raw.filename})调用 {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()}。

    +

    但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 +{@code +assets/} 目录下(而不是 {@code res/raw/})。{@code assets/} 中的文件没有资源 ID,因此您只能使用 {@link android.content.res.AssetManager} 读取这些文件。 +

    values/

    包含字符串、整型数和颜色等简单值的 XML 文件。

    +

    其他 {@code res/} 子目录中的 XML +资源文件是根据 +XML +文件名定义单个资源,而目录中的 {@code values/} 文件可描述多个资源。对于此目录中的文件,{@code <resources>} 元素的每个子元素均定义一个资源。例如,{@code <string>} 元素创建 +{@code R.string} 资源,{@code <color>} 元素创建 {@code R.color} +资源。

    +

    由于每个资源均用其自己的 XML +元素定义,因此您可以根据自己的需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,您可能需要将独特的资源类型放在不同的文件中。 +例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定: +

    + +

    请参阅字符串资源样式资源更多资源类型。 + +

    +
    xml/可以在运行时通过调用 {@link +android.content.res.Resources#getXml(int) Resources.getXML()} 读取的任意 XML 文件。各种 XML +配置文件(如可搜索配置)都必须保存在此处。 +
    + +

    注意:切勿将资源文件直接保存在 +{@code res/} 目录内,这会导致出现编译错误。

    + +

    如需了解有关某些资源类型的详细信息,请参阅资源类型文档。

    + +

    保存在表 1 +中定义的子目录下的资源是“默认”资源。即,这些资源定义应用的默认设计和内容。但是,采用 +Android +技术的不同设备类型可能需要不同类型的资源。例如,如果设备的屏幕尺寸大于标准屏幕,则应提供不同的布局资源,以充分利用额外的屏幕空间。 +或者,如果设备的语言设置不同,则应提供不同的字符串资源,以转换用户界面中的文本。 + +要为不同的设备配置提供这些不同资源,除了默认资源以外,您还需要提供备用资源。 + +

    + + +

    提供备用资源

    + + +
    + +

    +图 1. 两种不同的设备,均使用不同的布局资源。

    +
    + +

    几乎每个应用都应提供备用资源以支持特定的设备配置。 +例如,对于不同的屏幕密度和语言,您应分别包括备用 Drawable 资源和备用字符串资源。 +在运行时,Android +会检测当前设备配置并为应用加载合适的资源。 +

    + +

    为一组资源指定特定于配置的备用资源:

    +
      +
    1. 在 {@code res/} 中创建一个以 {@code +<resources_name>-<config_qualifier>} 形式命名的新目录。 +
        +
      • {@code <resources_name>} 是相应默认资源的目录名称(如表 1 +中所定义)。
      • +
      • {@code <qualifier>} 是指定要使用这些资源的各个配置的名称(如表 2 +中所定义)。
      • +
      +

      您可以追加多个 {@code <qualifier>}。以短划线将其分隔。 +

      +

      注意:追加多个限定符时,必须按照表 +2 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。 +

      +
    2. +
    3. 将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。 +
    4. +
    + +

    例如,以下是一些默认资源和备用资源:

    + +
    +res/
    +    drawable/   
    +        icon.png
    +        background.png    
    +    drawable-hdpi/  
    +        icon.png
    +        background.png  
    +
    + +

    {@code hdpi} +限定符表示该目录中的资源适用于屏幕密度较高的设备。其中每个 Drawable 目录中的图像已针对特定的屏幕密度调整大小,但是文件名完全相同。 + +这样一来,用于引用 {@code icon.png} 或 {@code +background.png} +图像的资源 ID 始终相同,但是 Android +会通过将设备配置信息与资源目录名称中的限定符进行比较,选择最符合当前设备的各个资源版本。

    + +

    Android +支持若干配置限定符,您可以通过使用短划线分隔每个限定符,向一个目录名称添加多个限定符。表 2 +按优先顺序列出了有效的配置限定符;如果对资源目录使用多个限定符,则必须按照表中列出的顺序将它们添加到目录名称。 + +

    + + +

    表 2. 配置限定符名称。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    配置限定符值描述
    MCC 和 MNC示例:
    + mcc310
    + mcc310-mnc004
    + mcc208-mnc00
    + 等等 +
    +

    移动国家代码 (MCC),(可选)后跟设备 SIM +卡中的移动网络代码 (MNC)。例如,mcc310 是指美国的任一运营商,mcc310-mnc004 是指美国的 Verizon +公司,mcc208-mnc00 是指法国的 Orange +公司。

    +

    如果设备使用无线电连接(GSM 手机),则 MCC 和 MNC 值来自 SIM +卡。

    +

    也可以单独使用 +MCC(例如,将国家/地区特定的合法资源包括在应用中)。如果只需根据语言指定,则改用“语言和区域”限定符(稍后进行介绍)。 +如果决定使用 MCC 和 +MNC 限定符,请谨慎执行此操作并测试限定符是否按预期工作。

    +

    另请参阅配置字段 +{@link +android.content.res.Configuration#mcc} 和 {@link +android.content.res.Configuration#mnc},这两个字段分别表示当前的移动国家代码和移动网络代码。

    +
    语言和区域示例:
    + en
    + fr
    + en-rUS
    + fr-rFR
    + fr-rCA
    + 等等 +

    语言通过由两个字母组成的 ISO +639-1 语言代码定义,(可选)后跟两个字母组成的 +ISO +3166-1-alpha-2 区域码(前带小写字母“{@code r}”)。 +

    + 这些代码不区分大小写;{@code r} +前缀用于区分区域码。 + 不能单独指定区域。

    +

    如果用户更改系统设置中的语言,它有可能在应用生命周期中发生改变。 +如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 +

    +

    有关针对其他语言本地化应用的完整指南,请参阅本地化。 +

    +

    另请参阅 {@link android.content.res.Configuration#locale} 配置字段,该字段表示当前的区域设置。 +

    +
    布局方向ldrtl
    + ldltr
    +

    应用的布局方向。{@code ldrtl} 是指“布局方向从右到左”。{@code ldltr} +是指“布局方向从左到右”,这是默认的隐式值。 +

    +

    它适用于布局、图片或值等任何资源。 +

    +

    例如,若要针对阿拉伯语提供某种特定布局,并针对任何其他“从右到左”语言(如波斯语或希伯来语)提供某种通用布局,则可编码如下: + +

    +
    +res/
    +    layout/   
    +        main.xml  (Default layout)
    +    layout-ar/  
    +        main.xml  (Specific layout for Arabic)
    +    layout-ldrtl/  
    +        main.xml  (Any "right-to-left" language, except
    +                  for Arabic, because the "ar" language qualifier
    +                  has a higher precedence.)
    +
    +

    注:要为应用启用从右到左的布局功能,必须将 {@code +supportsRtl} 设置为 +{@code "true"},并将 {@code targetSdkVersion} 设置为 17 或更高。

    +

    此项为API 级别 17 中新增配置。

    +
    smallestWidthsw<N>dp

    + 示例:
    + sw320dp
    + sw600dp
    + sw720dp
    + 等等 +
    +

    屏幕的基本尺寸,由可用屏幕区域的最小尺寸指定。 +具体来说,设备的 smallestWidth +是屏幕可用高度和宽度的最小尺寸(您也可以将其视为屏幕的“最小可能宽度”)。无论屏幕的当前方向如何,您均可使用此限定符确保应用 +UI 的可用宽度至少为 +{@code <N>}dp。

    +

    例如,如果布局要求屏幕区域的最小尺寸始终至少为 +600dp,则可使用此限定符创建布局资源 {@code +res/layout-sw600dp/}。仅当可用屏幕的最小尺寸至少为 +600dp 时,系统才会使用这些资源,而不考虑 +600dp 所代表的边是用户所认为的高度还是宽度。smallestWidth 是设备的固定屏幕尺寸特性;设备的 smallestWidth +不会随屏幕方向的变化而改变

    +

    设备的 smallestWidth 将屏幕装饰元素和系统 UI +考虑在内。例如,如果设备的屏幕上有一些永久性 UI 元素占据沿 +smallestWidth +轴的空间,则系统会声明 smallestWidth +小于实际屏幕尺寸,因为这些屏幕像素不适用于您的 +UI。因此,使用的值应该是布局所需要的实际最小尺寸(通常,无论屏幕的当前方向如何,此值都是布局支持的“最小宽度”)。

    +

    以下是一些可用于普通屏幕尺寸的值:

    +
      +
    • 320,适用于屏幕配置如下的设备: +
        +
      • 240x320 ldpi(QVGA 手机)
      • +
      • 320x480 mdpi(手机)
      • +
      • 480x800 hdpi(高密度手机)
      • +
      +
    • +
    • 480,适用于 480x800 mdpi 之类的屏幕(平板电脑/手机)。
    • +
    • 600,适用于 600x1024 mdpi 之类的屏幕(7 英寸平板电脑)。
    • +
    • 720,适用于 720x1280 mdpi 之类的屏幕(10 英寸平板电脑)。
    • +
    +

    应用为多个资源目录提供不同的 +smallestWidth 限定符值时,系统会使用最接近(但未超出)设备 +smallestWidth 的值。

    +

    此项为 API 级别 13 中新增配置。

    +

    另请参阅 {@code +android:requiresSmallestWidthDp} 属性和 {@link +android.content.res.Configuration#smallestScreenWidthDp} 配置字段,前者声明与应用兼容的最小 +smallestWidth;后者存放设备的 +smallestWidth 值。

    +

    如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多个屏幕开发者指南。 + +

    +
    可用宽度w<N>dp

    + 示例:
    + w720dp
    + w1024dp
    + 等等 +
    +

    指定资源应该使用的最小可用屏幕宽度,以 {@code dp} +为单位,由 <N> 值定义。在横向和纵向之间切换时,为了匹配当前实际宽度,此配置值也会随之发生变化。 + +

    +

    应用为多个资源目录提供不同的此配置值时,系统会使用最接近(但未超出)设备当前屏幕宽度的值。 + +此处的值考虑到了屏幕装饰元素,因此如果设备显示屏的左边缘或右边缘上有一些永久性 UI +元素,考虑到这些 UI +元素,它会使用小于实际屏幕尺寸的宽度值,这样会减少应用的可用空间。 + +

    +

    此项为 API 级别 13 中新增配置。

    +

    另请参阅 +{@link android.content.res.Configuration#screenWidthDp} 配置字段,该字段存放当前屏幕宽度。

    +

    如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多个屏幕开发者指南。 + +

    +
    可用高度h<N>dp

    + 示例:
    + h720dp
    + h1024dp
    + 等等 +
    +

    指定资源应该使用的最小可用屏幕高度,以“dp”为单位,由 <N> 值定义。 +在横向和纵向之间切换时,为了匹配当前实际高度,此配置值也会随之发生变化。 + +

    +

    应用为多个资源目录提供不同的此配置值时,系统会使用最接近(但未超出)设备当前屏幕高度的值。 + +此处的值考虑到了屏幕装饰元素,因此如果设备显示屏的上边缘或下边缘有一些永久性 UI +元素,考虑到这些 UI +元素,同时为减少应用的可用空间,它会使用小于实际屏幕尺寸的高度值。 + +非固定的屏幕装饰元素(例如,全屏时可隐藏的手机状态栏)并不在考虑范围内,标题栏或操作栏等窗口装饰也不在考虑范围内,因此应用必须准备好处理稍小于其所指定值的空间。 + + + + +

    此项为 API 级别 13 中新增配置。

    +

    另请参阅 +{@link android.content.res.Configuration#screenHeightDp} 配置字段,该字段存放当前屏幕宽度。

    +

    如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多个屏幕开发者指南。 + +

    +
    屏幕尺寸 + small
    + normal
    + large
    + xlarge +
    +
      +
    • {@code small}:尺寸类似于低密度 +QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 +320x426 dp 单位。例如,QVGA 低密度屏幕和 VGA +高密度屏幕。
    • +
    • {@code normal}:尺寸类似于中等密度 +HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 +320x470 dp 单位。例如,WQVGA +低密度屏幕、HVGA 中等密度屏幕、WVGA +高密度屏幕。
    • +
    • {@code large}:尺寸类似于中等密度 +VGA 屏幕的屏幕。 + 大屏幕的最小布局尺寸约为 480x640 dp 单位。 + 例如,VGA 和 WVGA 中等密度屏幕。
    • +
    • {@code xlarge}:明显大于传统中等密度 +HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 +720x960 dp 单位。在大多数情况下,屏幕超大的设备体积过大,不能放进口袋,最常见的是平板式设备。 + +此项为 API 级别 9 中新增配置。
    • +
    +

    注:使用尺寸限定符并不表示资源仅适用于该尺寸的屏幕。 +如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。 + +

    +

    注意:如果所有资源均使用大于当前屏幕的尺寸限定符,则系统会使用这些资源,并且应用在运行时将会崩溃(例如,如果所有布局资源均用 {@code +xlarge} 限定符标记,但设备是标准尺寸的屏幕)。 + +

    +

    此项为 API 级别 4 中新增配置。

    + +

    如需了解详细信息,请参阅支持多个屏幕。 +

    +

    另请参阅 {@link android.content.res.Configuration#screenLayout} 配置字段,该字段表示屏幕是小尺寸、标准尺寸还是大尺寸。 + +

    +
    屏幕纵横比 + long
    + notlong +
    +
      +
    • {@code long}:宽屏,如 WQVGA、WVGA、FWVGA
    • +
    • {@code notlong}:非宽屏,如 QVGA、HVGA 和 VGA
    • +
    +

    此项为 API 级别 4 中新增配置。

    +

    它完全基于屏幕的纵横比(宽屏较宽),而与屏幕方向无关。 +

    +

    另请参阅 {@link android.content.res.Configuration#screenLayout} 配置字段,该字段指示屏幕是否为宽屏。 +

    +
    屏幕方向 + port
    + land +
    +
      +
    • {@code port}:设备处于纵向(垂直)
    • +
    • {@code land}:设备处于横向(水平)
    • + +
    +

    如果用户旋转屏幕,它有可能在应用生命周期中发生改变。 +如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 +

    +

    另请参阅 {@link android.content.res.Configuration#orientation} 配置字段,该字段指示当前的设备方向。 +

    +
    UI 模式 + car
    + desk
    + television
    + appliance +watch +
    +
      +
    • {@code car}:设备正在车载手机座上显示
    • +
    • {@code desk}:设备正在桌面手机座上显示
    • +
    • {@code television}:设备正在电视上显示,为用户提供“十英尺”体验,其 +UI 位于远离用户的大屏幕上,主要面向方向键或其他非指针式交互 + +
    • +
    • {@code appliance}:设备用作不带显示屏的装置 +
    • +
    • {@code watch}:设备配有显示屏,戴在手腕上
    • +
    +

    此项为 API 级别 8 中新增配置,API 13 中新增电视配置,API 20 中新增手表配置。

    +

    如需了解应用在设备插入手机座或从中移除时的响应方式,请阅读确定并监控插接状态和类型。 + +

    +

    如果用户将设备放入手机座中,它有可能在应用生命周期中发生改变。 +可以使用 {@link +android.app.UiModeManager} 启用或禁用其中某些模式。如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 +

    +
    夜间模式 + night
    + notnight +
    +
      +
    • {@code night}:夜间
    • +
    • {@code notnight}:白天
    • +
    +

    此项为 API 级别 8 中新增配置。

    +

    如果夜间模式停留在自动模式(默认),它有可能在应用生命周期中发生改变。在这种情况下,该模式会根据当天的时间进行调整。 +可以使用 +{@link android.app.UiModeManager} 启用或禁用此模式。如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 +

    +
    屏幕像素密度 (dpi) + ldpi
    + mdpi
    + hdpi
    + xhdpi
    + xxhdpi
    + xxxhdpi
    + nodpi
    + tvdpi +
    +
      +
    • {@code ldpi}:低密度屏幕;约为 120dpi。
    • +
    • {@code mdpi}:中等密度(传统 HVGA)屏幕;约为 +160dpi。
    • +
    • {@code hdpi}:高密度屏幕;约为 240dpi。
    • +
    • {@code xhdpi}:超高密度屏幕;约为 320dpi。API +级别 8 中新增配置
    • +
    • {@code xxhdpi}:超超高密度屏幕;约为 480dpi。API +级别 16 中新增配置
    • +
    • {@code xxxhdpi}:超超超高密度屏幕使用(仅限启动器图标,请参阅“支持多个屏幕”中的注释);约为 +640dpi。 +API +级别 18 中新增配置
    • +
    • {@code nodpi}:它可用于您不希望缩放以匹配设备密度的位图资源。 +
    • +
    • {@code tvdpi}:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, +主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 +hdpi +资源便已足够,系统将根据需要对其进行缩放。API 级别 13 中引入了此限定符。
    • +
    +

    六个主要密度之间的缩放比为 3:4:6:8:12:16(忽略 +tvdpi 密度)。因此,9x9 (ldpi) 位图相当于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图,依此类推。 +

    +

    如果您认为图像资源在电视或其他某些设备上呈现的效果不够好,而想尝试使用 tvdpi 资源,则缩放比例为 +1.33*mdpi。例如,mdpi +屏幕的 100px x 100px 图像应该相当于 tvdpi 的133px x 133px。

    +

    注:使用密度限定符并不表示资源仅适用于该密度的屏幕。 +如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。 + +

    +

    如需了解有关如何处理不同屏幕密度以及 Android 如何缩放位图以适应当前密度的详细信息,请参阅支持多个屏幕。 + +

    +
    触摸屏类型 + notouch
    + finger +
    +
      +
    • {@code notouch}:设备没有触摸屏。
    • +
    • {@code finger}:设备有一个专供用户通过手指直接与其交互的触摸屏。 +
    • +
    +

    另请参阅 {@link android.content.res.Configuration#touchscreen} 配置字段,该字段指示设备上的触摸屏类型。 +

    +
    键盘可用性 + keysexposed
    + keyshidden
    + keyssoft +
    +
      +
    • {@code keysexposed}:设备具有可用的键盘。如果设备启用了软键盘(不无可能),那么即使硬键盘没有展示给用户,哪怕设备没有硬键盘,也可以使用此限定符。 + +如果没有提供或已经禁用软键盘,则只有在显示硬键盘时才会使用此限定符。 + +
    • +
    • {@code keyshidden}:设备具有可用的硬键盘,但它处于隐藏状态,且设备没有启用软键盘。 +
    • +
    • {@code keyssoft}:设备已经启用软键盘(无论是否可见)。 +
    • +
    +

    如果提供了 keysexposed 资源,但未提供 keyssoft +资源,那么只要系统已经启用软键盘,就会使用 +keysexposed 资源,而不考虑键盘是否可见。

    +

    如果用户打开硬键盘,它有可能在应用生命周期中发生改变。 +如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更。 +

    +

    另请参阅配置字段 +{@link +android.content.res.Configuration#hardKeyboardHidden} 和 {@link +android.content.res.Configuration#keyboardHidden},这两个字段分别指示硬键盘的可见性和任何一种键盘(包括软键盘)的可见性。

    +
    主要文本输入法 + nokeys
    + qwerty
    + 12key +
    +
      +
    • {@code nokeys}:设备没有用于文本输入的硬按键。
    • +
    • {@code qwerty}:设备具有标准硬键盘(无论是否对用户可见)。 + +
    • +
    • {@code 12key}:设备具有 12 键硬键盘(无论是否对用户可见)。 +
    • +
    +

    另请参阅 {@link android.content.res.Configuration#keyboard} 配置字段,该字段指示可用的主要文本输入法。 +

    +
    平台版本(API 级别)示例:
    + v3
    + v4
    + v7
    + 等等
    +

    设备支持的 API 级别。例如,v1 对应于 API 级别 +1(带有 Android 1.0 或更高版本系统的设备),v4 对应于 API 级别 4(带有 Android +1.6 或更高版本系统的设备)。如需了解有关这些值的详细信息,请参阅 +Android API 级别文档。

    +
    + + +

    注:有些配置限定符是从 Android 1.0 +才开始添加,因此并非所有版本的 Android 系统都支持所有限定符。使用新限定符会隐式添加平台版本限定符,因此较旧版本系统的设备必然会忽略它。 +例如,使用 +w600dp 限定符会自动包括 v13 限定符,因为可用宽度限定符是 API 级别 13 +中的新增配置。为了避免出现任何问题,请始终包含一组默认资源(一组“不带限定符”的资源)。 +如需了解详细信息,请参阅利用资源提供最佳设备兼容性部分。 + +

    + + + +

    限定符命名规则

    + +

    以下是一些关于使用配置限定符名称的规则:

    + +
      +
    • 您可以为单组资源指定多个限定符,并使用短划线分隔。例如,drawable-en-rUS-land +适用于横排美国英语设备。 +
    • +
    • 这些限定符必须遵循表 2 中列出的顺序。例如: + +
        +
      • 错误:drawable-hdpi-port/
      • +
      • 正确:drawable-port-hdpi/
      • +
      +
    • +
    • 不能嵌套备用资源目录。例如,您不能拥有 +res/drawable/drawable-en/
    • +
    • 值不区分大小写。在处理之前,资源编译器会将目录名称转换为小写,以避免不区分大小写的文件系统出现问题。 + +名称中使用的任何大写字母只是为了便于认读。
    • +
    • 对于每种限定符类型,仅支持一个值。例如,若要对西班牙语和法语使用相同的 Drawable 文件,则您肯定不能拥有名为 +drawable-rES-rFR/ +的目录,而是需要两个包含相应文件的资源目录,如 +drawable-rES/ +和 drawable-rFR/。然而,实际上您无需将相同的文件都复制到这两个位置。相反,您可以创建指向资源的别名。 +请参阅下面的创建别名资源。 +
    • +
    + +

    将备用资源保存到以这些限定符命名的目录中之后,Android +会根据当前设备配置在应用中自动应用这些资源。 +每次请求资源时,Android +都会检查备用资源目录是否包含所请求的资源文件,然后找到最匹配资源(下文进行介绍)。 +如果没有与特定设备配置匹配的备用资源,则 +Android +会使用相应的默认资源(一组用于不含配置限定符的特定资源类型的资源)。 +

    + + + +

    创建别名资源

    + +

    如果您想将某一资源用于多种设备配置(但是不想作为默认资源提供),则无需将同一资源放入多个备用资源目录中。 + +相反,您可以(在某些情况下)创建备用资源,充当保存在默认资源目录下的资源的别名。 + +

    + +

    注:并非所有资源都会提供相应机制让您创建指向其他资源的别名。 +特别是,{@code xml/} 目录中的动画资源、菜单资源、原始资源以及其他未指定资源均不提供此功能。 +

    + +

    例如,假设您有一个应用图标 {@code icon.png},并且需要不同区域设置的独特版本。 +但是,加拿大英语和加拿大法语这两种区域设置需要使用同一版本。 +您可能会认为需要将相同的图像复制到加拿大英语和加拿大法语对应的资源目录中,但事实并非如此。 + +相反,您可以将用于二者的图像另存为 {@code icon_ca.png}(除 +{@code icon.png} +以外的任何名称),并将其放入默认 {@code res/drawable/} 目录中。然后,在 {@code +res/drawable-en-rCA/} +和 {@code res/drawable-fr-rCA/} 中创建 {@code icon.xml} 文件,使用 {@code <bitmap>} 元素引用 {@code icon_ca.png}资源。这样,您只需存储 PNG +文件的一个版本和两个指向该版本的小型 XML 文件。(XML 文件示例如下。)

    + + +

    Drawable

    + +

    要创建指向现有 Drawable 的别名,请使用 +{@code <bitmap>} 元素。例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:src="@drawable/icon_ca" />
    +
    + +

    如果将此文件另存为 +{@code icon.xml}(例如,在备用资源目录中,另存为 {@code res/drawable-en-rCA/}),则会编译到可作为 +{@code R.drawable.icon} 引用的资源中,但实际上它是 {@code +R.drawable.icon_ca} 资源(保存在 {@code res/drawable/} 中)的别名。

    + + +

    布局

    + +

    要创建指向现有布局的别名,请使用包装在 {@code <merge>} 中的 +{@code <include>}元素。例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<merge>
    +    <include layout="@layout/main_ltr"/>
    +</merge>
    +
    + +

    如果将此文件另存为 {@code main.xml},则会编译到可作为 +{@code R.layout.main} 引用的资源中,但实际上它是 {@code R.layout.main_ltr} +资源的别名。

    + + +

    字符串和其他简单值

    + +

    要创建指向现有字符串的别名,只需将所需字符串的资源 +ID 用作新字符串的值即可。例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +    <string name="hello">Hello</string>
    +    <string name="hi">@string/hello</string>
    +</resources>
    +
    + +

    {@code R.string.hi} 资源现在是 {@code R.string.hello} 的别名。

    + +

    其他简单值的原理相同。 +例如,颜色:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<resources>
    +    <color name="yellow">#f00</color>
    +    <color name="highlight">@color/red</color>
    +</resources>
    +
    + + + + +

    利用资源提供最佳设备兼容性

    + +

    要使应用支持多种设备配置,则务必为应用使用的每种资源类型提供默认资源,这一点非常重要。 +

    + +

    例如,如果应用支持多种语言,请始终包含不带语言和区域限定符的 {@code +values/} 目录(用于保存字符串)。相反,如果您将所有字符串放入带有语言和区域限定符的目录中,则在语言设置不支持您的字符串的设备上运行应用时,应用将会崩溃。 + +但是,只要提供默认 {@code values/} 资源,应用就会正常运行(即使用户不理解该语言,这也总比崩溃要好)。 + +

    + +

    同样,如果您根据屏幕方向提供不同的布局资源,则应选择一个方向作为默认方向。 +例如,不要在 {@code +layout-land/} 和 {@code layout-port/} 中分别提供横向和纵向的布局资源,而是保留其中之一作为默认设置,例如:{@code layout/} +用于横向,{@code layout-port/} 用于纵向。

    + +

    提供默认资源至关重要,这不仅仅因为应用可能在超出预期的配置上运行,也因为新版 +Android +有时会添加旧版本不支持的配置限定符。若要使用新的资源限定符,又希望维持对旧版 +Android 的代码兼容性,则当旧版 +Android +运行应用时,如果不提供默认资源,应用将会崩溃,这是因为它无法使用以新限定符命名的资源。例如,如果将 {@code +minSdkVersion} 设置为 4,并使用夜间模式({@code night} 或 {@code notnight},API +级别 8 中新增配置)限定所有 Drawable 资源,则 API 级别 4 设备无法访问 Drawable 资源,而且会崩溃。在这种情况下,您可能希望 {@code notnight} 成为默认资源,为此,您应排除该限定符,使 Drawable 资源位于 {@code drawable/} 或 +{@code drawable-night/} +中。

    + +

    因此,为了提供最佳设备兼容性,请始终为应用正确运行所必需的资源提供默认资源。 +然后,使用配置限定符为特定的设备配置创建备用资源。 +

    + +

    这条规则有一个例外:如果应用的 {@code minSdkVersion} 为 4 或更高,则在提供带屏幕密度限定符的备用 Drawable 资源时,不需要默认 Drawable 资源。 + +即使没有默认 Drawable 资源,Android +也可以从备用屏幕密度中找到最佳匹配项并根据需要缩放位图。 +但是,为了在所有类型的设备上提供最佳体验,您应该为所有三种类型的密度提供备用 Drawable。 +

    + + + +

    Android 如何找到最匹配资源

    + +

    当您请求要为其提供备用资源的资源时,Android +会根据当前的设备配置选择要在运行时使用的备用资源。为演示 +Android 如何选择备用资源,假设以下 Drawable 目录分别包含相同图像的不同版本: +

    + +
    +drawable/
    +drawable-en/
    +drawable-fr-rCA/
    +drawable-en-port/
    +drawable-en-notouch-12key/
    +drawable-port-ldpi/
    +drawable-port-notouch-12key/
    +
    + +

    同时,假设设备配置如下:

    + +

    +区域设置 = en-GB
    +屏幕方向 = port
    +屏幕像素密度 = hdpi
    +触摸屏类型 = notouch
    +主要文本输入法 = 12key +

    + +

    通过将设备配置与可用的备用资源进行比较,Android 从 +{@code drawable-en-port} 中选择 Drawable。

    + +

    系统使用以下逻辑决定要使用的资源: +

    + + +
    + +

    图 2. Android +如何找到最匹配资源的流程图。

    +
    + + +
      +
    1. 淘汰与设备配置冲突的资源文件。 +

      drawable-fr-rCA/ 目录与 +en-GB 区域设置冲突,因而被淘汰。

      +
      +drawable/
      +drawable-en/
      +drawable-fr-rCA/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +drawable-port-ldpi/
      +drawable-port-notouch-12key/
      +
      +

      例外:屏幕像素密度是唯一一个未因冲突而被淘汰的限定符。 +尽管设备的屏幕密度为 +hdpi,但是 +drawable-port-ldpi/ 未被淘汰,因为此时每个屏幕密度均视为匹配。如需了解详细信息,请参阅支持多个屏幕文档。 +

    2. + +
    3. 选择列表(表 2)中(下一个)优先级最高的限定符。(先从 MCC +开始,然后下移。)
    4. +
    5. 是否有资源目录包括此限定符?
    6. +
        +
      • 若无,请返回到第 2 步,看看下一个限定符。(在该示例中,除非达到语言限定符,否则答案始终为“否”。) +
      • +
      • 若有,请继续执行第 4 步。
      • +
      + + +
    7. 淘汰不含此限定符的资源目录。在该示例中,系统会淘汰所有不含语言限定符的目录。 +
    8. +
      +drawable/
      +drawable-en/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +drawable-port-ldpi/
      +drawable-port-notouch-12key/
      +
      +

      例外:如果涉及的限定符是屏幕像素密度,则 +Android +会选择最接近设备屏幕密度的选项。通常,Android +倾向于缩小大型原始图像,而不是放大小型原始图像。请参阅支持多个屏幕。 +

      + + +
    9. 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止。在此示例中,屏幕方向是下一个判断是否匹配的限定符。因此,未指定屏幕方向的资源被淘汰: + + +
      +drawable-en/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +
      +

      剩下的目录是 {@code drawable-en-port}。

      +
    10. +
    + +

    尽管对所请求的每个资源均执行此程序,但是系统仍会对某些方面做进一步优化。 +例如,系统一旦知道设备配置,即会淘汰可能永远无法匹配的备用资源。 +比如说,如果配置语言是英语(“en”),则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中(不过,仍会将不带语言限定符的资源目录包含在该池中)。 + + +

    + +

    根据屏幕尺寸限定符选择资源时,如果没有更好的匹配资源,则系统将使用专为小于当前屏幕的屏幕而设计的资源(例如,如有必要,大尺寸屏幕将使用标准尺寸的屏幕资源)。 + +但是,如果唯一可用的资源大于当前屏幕,则系统不会使用这些资源,并且如果没有其他资源与设备配置匹配,应用将会崩溃(例如,如果所有布局资源均用 {@code xlarge} 限定符标记,但设备是标准尺寸的屏幕)。 + + + +

    + +

    注:限定符的优先顺序(表 +2 中)比与设备完全匹配的限定符数量更加重要。例如,在上面的第 +4 步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(方向、触摸屏类型和输入法),而 +drawable-en +只有一个匹配参数(语言)。但是,语言的优先顺序高于其他两个限定符,因此 +drawable-port-notouch-12key 被淘汰。

    + +

    如需了解有关如何在应用中使用资源的更多信息,请转至访问资源

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/zh-cn/guide/topics/resources/runtime-changes.jd new file mode 100644 index 0000000000000000000000000000000000000000..0df99982de9a85194c6fd71fb36f2d86b8d21961 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=处理运行时变更 +page.tags=Activity,生命周期 +@jd:body + + + +

    有些设备配置可能会在运行时发生变化(例如屏幕方向、键盘可用性及语言)。 +发生这种变化时,Android +会重启正在运行的 +{@link android.app.Activity}(先后调用 {@link android.app.Activity#onDestroy()} 和 {@link +android.app.Activity#onCreate(Bundle) onCreate()})。重启行为旨在通过利用与新设备配置匹配的备用资源自动重新加载您的应用,来帮助它适应新配置。 + +

    + +

    要妥善处理重启行为,Activity 必须通过常规的Activity 生命周期恢复其以前的状态,在 Activity 生命周期中,Android +会在销毁 Activity 之前调用 +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()},以便您保存有关应用状态的数据。 + +然后,您可以在 +{@link android.app.Activity#onCreate(Bundle) onCreate()} 或 {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()} 期间恢复 Activity 状态。

    + +

    要测试应用能否在保持应用状态完好的情况下自行重启,您应该在应用中执行各种任务时调用配置变更(例如,更改屏幕方向)。 + +您的应用应该能够在不丢失用户数据或状态的情况下随时重启,以便处理如下事件:配置发生变化,或者用户收到来电并在应用进程被销毁很久之后返回到应用。 + + +要了解如何恢复 Activity 状态,请阅读Activity 生命周期

    + +

    但是,您可能会遇到这种情况:重启应用并恢复大量数据不仅成本高昂,而且给用户留下糟糕的使用体验。 +在这种情况下,您有两个其他选择: +

    + +
      +
    1. 在配置变更期间保留对象 +

      允许 Activity 在配置变更时重启,但是要将有状态对象传递给 Activity 的新实例。 +

      + +
    2. +
    3. 自行处理配置变更 +

      阻止系统在某些配置变更期间重启 Activity,但要在配置确实发生变化时接收回调,这样,您就能够根据需要手动更新 Activity。 + +

      +
    4. +
    + + +

    在配置变更期间保留对象

    + +

    如果重启 Activity 需要恢复大量数据、重新建立网络连接或执行其他密集操作,那么因配置变更而引起的完全重启可能会给用户留下应用运行缓慢的体验。 + +此外,依靠系统通过 {@link +android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} 回调为您保存的 +{@link android.os.Bundle},可能无法完全恢复 Activity 状态,因为它 +并非设计用于携带大型对象(例如位图),而且其中的数据必须先序列化,再进行反序列化, +这可能会消耗大量内存并使得配置变更速度缓慢。在这种情况下,如果 Activity 因配置变更而重启,则可通过保留 +{@link +android.app.Fragment} 来减轻重新初始化 Activity 的负担。此片段可能包含对您要保留的有状态对象的引用。 +

    + +

    当 +Android 系统因配置变更而关闭 Activity 时,不会销毁您已标记为要保留的 Activity 的片段。您可以将此类片段添加到 Activity 以保留有状态的对象。 +

    + +

    要在运行时配置变更期间将有状态的对象保留在片段中,请执行以下操作:

    + +
      +
    1. 扩展 {@link android.app.Fragment} +类并声明对有状态对象的引用。
    2. +
    3. 在创建片段后调用 {@link android.app.Fragment#setRetainInstance(boolean)}。 +
    4. +
    5. 将片段添加到 Activity。
    6. +
    7. 重启 Activity 后,使用 {@link android.app.FragmentManager} +检索片段。
    8. +
    + +

    例如,按如下所示定义片段:

    + +
    +public class RetainedFragment extends Fragment {
    +
    +    // data object we want to retain
    +    private MyDataObject data;
    +
    +    // this method is only called once for this fragment
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // retain this fragment
    +        setRetainInstance(true);
    +    }
    +
    +    public void setData(MyDataObject data) {
    +        this.data = data;
    +    }
    +
    +    public MyDataObject getData() {
    +        return data;
    +    }
    +}
    +
    + +

    注意:尽管您可以存储任何对象,但是切勿传递与 +{@link android.app.Activity} 绑定的对象,例如,{@link +android.graphics.drawable.Drawable}、{@link android.widget.Adapter}、{@link android.view.View} +或其他任何与 {@link android.content.Context} 关联的对象。否则,它将泄漏原始 Activity 实例的所有视图和资源。 +(泄漏资源意味着应用将继续持有这些资源,但是无法对其进行垃圾回收,因此可能会丢失大量内存。) + +

    + +

    然后,使用 {@link android.app.FragmentManager} 将片段添加到 Activity。在运行时配置变更期间再次启动 Activity 时,您可以获得片段中的数据对象。 + +例如,按如下所示定义 Activity:

    + +
    +public class MyActivity extends Activity {
    +
    +    private RetainedFragment dataFragment;
    +
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.main);
    +
    +        // find the retained fragment on activity restarts
    +        FragmentManager fm = getFragmentManager();
    +        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
    +
    +        // create the fragment and data the first time
    +        if (dataFragment == null) {
    +            // add the fragment
    +            dataFragment = new DataFragment();
    +            fm.beginTransaction().add(dataFragment, “data”).commit();
    +            // load the data from the web
    +            dataFragment.setData(loadMyData());
    +        }
    +
    +        // the data is available in dataFragment.getData()
    +        ...
    +    }
    +
    +    @Override
    +    public void onDestroy() {
    +        super.onDestroy();
    +        // store the data in the fragment
    +        dataFragment.setData(collectMyLoadedData());
    +    }
    +}
    +
    + +

    在此示例中,{@link android.app.Activity#onCreate(Bundle) onCreate()} +添加了一个片段或恢复了对它的引用。此外,{@link android.app.Activity#onCreate(Bundle) onCreate()} +还将有状态的对象存储在片段实例内部。{@link android.app.Activity#onDestroy() onDestroy()} +对所保留的片段实例内的有状态对象进行更新。 +

    + + + + + +

    自行处理配置变更

    + +

    如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免重启,则可声明 Activity 将自行处理配置变更,这样可以阻止系统重启 Activity。 + + +

    + +

    注:自行处理配置变更可能导致备用资源的使用更为困难,因为系统不会为您自动应用这些资源。 + +只能在您必须避免Activity因配置变更而重启这一万般无奈的情况下,才考虑采用自行处理配置变更这种方法,而且对于大多数应用并不建议使用此方法。 +

    + +

    要声明由 Activity 处理配置变更,请在清单文件中编辑相应的 {@code <activity>} +元素,以包含 {@code +android:configChanges} +属性以及代表要处理的配置的值。{@code +android:configChanges}属性的文档中列出了该属性的可能值(最常用的值包括 {@code "orientation"} +和 +{@code "keyboardHidden"},分别用于避免因屏幕方向和可用键盘改变而导致重启)。您可以在该属性中声明多个配置值,方法是用管道 +{@code |} 字符分隔这些配置值。

    + +

    例如,以下清单文件代码声明的 Activity 可同时处理屏幕方向变更和键盘可用性变更: +

    + +
    +<activity android:name=".MyActivity"
    +          android:configChanges="orientation|keyboardHidden"
    +          android:label="@string/app_name">
    +
    + +

    现在,当其中一个配置发生变化时,{@code MyActivity} 不会重启。相反,{@code MyActivity} +会收到对 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 的调用。向此方法传递 +{@link android.content.res.Configuration} +对象指定新设备配置。您可以通过读取 +{@link android.content.res.Configuration} +中的字段,确定新配置,然后通过更新界面中使用的资源进行适当的更改。调用此方法时,Activity 的 +{@link android.content.res.Resources} +对象会相应地进行更新,以根据新配置返回资源,这样,您就能够在系统不重启 Activity 的情况下轻松重置 +UI 的元素。

    + +

    注意:从 +Android +3.2(API 级别 13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会发生变化。因此,在开发针对 +API 级别 13 或更高版本系统的应用时,若要避免由于设备方向改变而导致运行时重启(正如 {@code minSdkVersion}{@code targetSdkVersion} +属性中所声明),则除了 {@code +"orientation"} 值以外,您还必须添加 {@code "screenSize"} 值。即,您必须声明 {@code +android:configChanges="orientation|screenSize"}。但是,如果您的应用是面向 API 级别 +12 或更低版本的系统,则 Activity 始终会自行处理此配置变更(即便是在 +Android 3.2 或更高版本的设备上运行,此配置变更也不会重启 Activity)。

    + +

    例如,以下 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 实现 +检查当前设备方向:

    + +
    +@Override
    +public void onConfigurationChanged(Configuration newConfig) {
    +    super.onConfigurationChanged(newConfig);
    +
    +    // Checks the orientation of the screen
    +    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    +        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    +    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
    +        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    +    }
    +}
    +
    + +

    {@link android.content.res.Configuration} +对象代表所有当前配置,而不仅仅是已经变更的配置。大多数时候,您并不在意配置具体发生了哪些变更,而且您可以轻松地重新分配所有资源,为您正在处理的配置提供备用资源。 + +例如,由于 {@link +android.content.res.Resources} 对象现已更新,因此您可以通过 +{@link android.widget.ImageView#setImageResource(int) +setImageResource()} +重置任何 {@link android.widget.ImageView},并且使用适合于新配置的资源(如提供资源中所述)。

    + +

    请注意,{@link +android.content.res.Configuration} 字段中的值是与 +{@link android.content.res.Configuration} 类中的特定常量匹配的整型数。有关要对每个字段使用哪些常量的文档,请参阅 +{@link +android.content.res.Configuration} 参考文档中的相应字段。

    + +

    请谨记:在声明由 Activity 处理配置变更时,您有责任重置要为其提供备用资源的所有元素。 +如果您声明由 Activity 处理方向变更,而且有些图像应该在横向和纵向之间切换,则必须在 +{@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} +期间将每个资源重新分配给每个元素。

    + +

    如果无需基于这些配置变更更新应用,则可不用实现 +{@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}。在这种情况下,仍将使用在配置变更之前用到的所有资源,只是您无需重启 Activity。 + +但是,应用应该始终能够在保持之前状态完好的情况下关闭和重启,因此您不得试图通过此方法来逃避在正常 Activity 生命周期期间保持您的应用状态。 + +这不仅仅是因为还存在其他一些无法禁止重启应用的配置变更,还因为有些事件必须由您处理,例如用户离开应用,而在用户返回应用之前该应用已被销毁。 + + +

    + +

    如需了解有关您可以在 Activity 中处理哪些配置变更的详细信息,请参阅 {@code +android:configChanges} 文档和 {@link android.content.res.Configuration} +类。

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/controls.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/controls.jd new file mode 100644 index 0000000000000000000000000000000000000000..0f1a543d1f66d06775cbb14f364abb69c1680892 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=输入控件 +parent.title=用户界面 +parent.link=index.html +@jd:body + +
    + +
    + +

    输入控件是您的应用用户界面中的交互式组件。Android 提供了多种可在 UI 中使用的控件,如按钮、文本字段、定位栏、复选框、缩放按钮、切换按钮等。 + +

    + +

    向 UI 中添加输入控件与向 XML 布局中添加 XML 元素一样简单。例如,以下是一个包含文本字段和按钮的布局: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:layout_width="fill_parent"
    +    android:layout_height="fill_parent"
    +    android:orientation="horizontal">
    +    <EditText android:id="@+id/edit_message"
    +        android:layout_weight="1"
    +        android:layout_width="0dp"
    +        android:layout_height="wrap_content"
    +        android:hint="@string/edit_message" />
    +    <Button android:id="@+id/button_send"
    +        android:layout_width="wrap_content"
    +        android:layout_height="wrap_content"
    +        android:text="@string/button_send"
    +        android:onClick="sendMessage" />
    +</LinearLayout>
    +
    + +

    每个输入控件都支持一组特定的输入事件,以便您处理用户输入文本或触摸按钮等事件。 +

    + + +

    通用控件

    +

    以下列出了您可以在应用中使用的一些通用控件。点击链接可了解有关各控件用法的详情。 +

    + +

    注:除了此处列出的控件外,Android 还提供了几种其他控件。 +浏览 {@link android.widget} 软件包可发现更多控件。如果您的应用需要特定类型的输入控件,则可以构建您自己的自定义组件。 +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    控件类型描述相关类
    按钮可由用户按压或点击来执行操作的按钮。{@link android.widget.Button Button}
    文本字段一种可编辑的文本字段。您可以使用 AutoCompleteTextView 小工具创建提供自动完成建议的文本输入小工具{@link android.widget.EditText EditText}、{@link android.widget.AutoCompleteTextView}
    复选框可由用户切换的启用/禁用开关。您应该在向用户呈现一组不互斥的可选选项时使用复选框。{@link android.widget.CheckBox CheckBox}
    单选按钮与复选框类似,不同的是只能选择组中的一个选项。{@link android.widget.RadioGroup RadioGroup} +
    {@link android.widget.RadioButton RadioButton}
    切换按钮一种具有指示灯的开/关按钮。{@link android.widget.ToggleButton ToggleButton}
    微调框一种允许用户从值集中选择一个值的下拉列表。{@link android.widget.Spinner Spinner}
    选取器一种供用户通过使用向上/向下按钮或轻扫手势选择值集中单个值的对话框。使用 DatePicker 小工具输入日期(月、日、年)值,或使用 TimePicker 小工具输入时间(小时、分钟、上午/下午)值,系统将根据用户的区域设置自动设置所输入内容的格式。{@link android.widget.DatePicker}、{@link android.widget.TimePicker}
    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000000000000000000000000000000..38e534eb091a571ca55cbfe1afda5aad89c136a0 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=布局 +page.tags=view,viewgroup +@jd:body + +
    +
    +

    本文内容

    +
      +
    1. 编写 XML
    2. +
    3. 加载 XML 资源
    4. +
    5. 属性 +
        +
      1. ID
      2. +
      3. 布局参数
      4. +
      +
    6. +
    7. 布局位置
    8. +
    9. 尺寸、内边距和外边距
    10. +
    11. 常见布局
    12. +
    13. 使用适配器构建布局 +
        +
      1. 使用数据填充适配器视图
      2. +
      3. 处理点击事件
      4. +
      +
    14. +
    + +

    关键类

    +
      +
    1. {@link android.view.View}
    2. +
    3. {@link android.view.ViewGroup}
    4. +
    5. {@link android.view.ViewGroup.LayoutParams}
    6. +
    + +

    另请参阅

    +
      +
    1. 构建简单的用户界面 +
    +
    + +

    布局定义用户界面的视觉结构,如Activity应用小工具的 +UI。您可以通过两种方式声明布局:

    +
      +
    • 在 XML 中声明 UI 元素。Android 提供了对应于 View 类及其子类的简明 XML +词汇,如用于小工具和布局的词汇;
    • +
    • 运行时实例化布局元素。您的应用可以通过编程创建 +View 对象和 ViewGroup 对象(并操纵其属性)。
    • +
    + +

    Android 框架让您可以灵活地使用以下一种或两种方法来声明和管理应用的 UI。例如,您可以在 XML 中声明应用的默认布局,包括将出现在布局中的屏幕元素及其属性。然后,您可以在应用中添加可在运行时修改屏幕对象(包括那些已在 XML 中声明的对象)状态的代码。

    + + + +

    在 XML 中声明 UI 的优点在于,您可以更好地将应用的外观与控制应用行为的代码隔离。您的 UI 描述位于应用代码外部,这意味着您在修改或调整描述时无需修改您的源代码并重新编译。例如,您可以创建适用于不同屏幕方向、不同设备屏幕尺寸和不同语言的 XML 布局。此外,在 XML 中声明布局还能更轻松地显示 UI 的结构,从而简化问题调试过程。因此,本文将侧重于示范如何在 XML 中声明布局。如果您对在运行时实例化 View +对象感兴趣,请参阅 {@link android.view.ViewGroup} 类和 +{@link android.view.View} 类的参考资料。

    + +

    一般而言,用于声明 UI 元素的 XML 词汇严格遵循类和方法的结构和命名方式,其中元素名称对应于类名称,属性名称对应于方法。实际上,这种对应关系往往非常直接,让您可以猜到对应于类方法的 XML 属性,或对应于给定 XML 元素的类。但请注意,并非所有词汇都完全相同。在某些情况下,在命名上略有差异。例如,EditText 元素具有的 +text 属性对应的类方法是 EditText.setText()。 +

    + +

    提示:如需了解有关不同布局类型的更多信息,请参阅常见布局对象。 + +Hello 视图教程指南中也提供了一系列有关构建各种布局的教程。

    + +

    编写 XML

    + +

    您可以利用 Android 的 XML 词汇,按照在 HTML 中创建包含一系列嵌套元素的网页的相同方式快速设计 UI 布局及其包含的屏幕元素。

    + +

    每个布局文件都必须只包含一个根元素,并且该元素必须是视图对象或 ViewGroup 对象。定义根元素之后,即可再以子元素的形式添加其他布局对象或小工具,从而逐步构建定义布局的视图层次结构。例如,以下这个 XML 布局使用垂直 {@link android.widget.LinearLayout} +来储存一个 {@link android.widget.TextView} 和一个 {@link android.widget.Button}:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +              android:layout_width="match_parent"
    +              android:layout_height="match_parent"
    +              android:orientation="vertical" >
    +    <TextView android:id="@+id/text"
    +              android:layout_width="wrap_content"
    +              android:layout_height="wrap_content"
    +              android:text="Hello, I am a TextView" />
    +    <Button android:id="@+id/button"
    +            android:layout_width="wrap_content"
    +            android:layout_height="wrap_content"
    +            android:text="Hello, I am a Button" />
    +</LinearLayout>
    +
    + +

    在 XML 中声明布局后,请在您的 Android 项目 res/layout/ 目录中以 .xml 扩展名保存文件,以便其能够正确编译。 +

    + +

    布局资源文档中提供了有关布局 XML 文件语法的更多信息。

    + +

    加载 XML 资源

    + +

    当您编译应用时,每个 XML 布局文件都会编译到一个 +{@link android.view.View} 资源中。您应该在 +{@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} +回调实现中从您的应用代码加载布局资源。请通过调用 {@link android.app.Activity#setContentView(int) setContentView()},以 +R.layout.layout_file_name +形式向其传递对布局资源的引用来执行此操作。例如,如果您的 +XML 布局保存为 +main_layout.xml,则需要像下面这样为您的 Activity 加载该布局:

    +
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.main_layout);
    +}
    +
    + +

    启动您的 Activity 时,Android +框架会调用 Activity 中的 +onCreate() +回调方法(请参阅Activity文档中有关生命周期的阐述)。

    + + +

    属性

    + +

    每个视图对象和 ViewGroup 对象都支持各自的各类 XML +属性。某些属性是视图对象的专用属性(例如,TextView 支持 textSize +属性),但这些属性也会被任何可以扩展此类的视图对象继承。某些属性通用于所有 View +对象,因为它们继承自根 View 类(如 id +属性)。此外,其他属性被视为“布局参数”,即描述 View +对象特定布局方向的属性,如该对象的父 ViewGroup +对象所定义的属性。

    + +

    ID

    + +

    任何视图对象都可能具有关联的整型 ID,此 ID 用于在结构树中对 View +对象进行唯一标识。编译应用后,此 ID 将作为整型数引用,但在布局 XML +文件中,通常会在 id 属性中为该 ID 赋予字符串值。这是所有 +View 对象共用的 XML 属性(由 {@link android.view.View} +类定义),您会经常用到它。XML 标记内部的 ID +语法是:

    +
    android:id="@+id/my_button"
    + +

    字符串开头处的 @ 符号指示 XML 解析程序应该解析并展开 +ID 字符串的其余部分,并将其标识为 ID 资源。加号 (+) +表示这是一个新的资源名称,必须创建该名称并将其添加到我们的资源(在 R.java 文件中)内。Android 框架还提供了许多其他 ID +资源。引用 Android 资源 ID 时,不需要加号,但必须添加 +android 软件包命名空间,如下所示:

    +
    android:id="@android:id/empty"
    +

    添加 android 软件包命名空间之后,现在,我们将从 android.R +资源类而非本地资源类引用 ID。

    + +

    要想创建视图并从应用中引用它们,常见的模式是:

    +
      +
    1. 在布局文件中定义一个视图/小工具,并为其分配一个唯一的 ID: +
      +<Button android:id="@+id/my_button"
      +        android:layout_width="wrap_content"
      +        android:layout_height="wrap_content"
      +        android:text="@string/my_button_text"/>
      +
      +
    2. +
    3. 然后创建一个 view +对象实例,并从布局中捕获它(通常使用 {@link android.app.Activity#onCreate(Bundle) onCreate()} 方法): +
      +Button myButton = (Button) findViewById(R.id.my_button);
      +
      +
    4. +
    +

    创建 {@link android.widget.RelativeLayout} +时,为 view 对象定义 ID 非常重要。在相对布局中,同级视图可以定义其相对于其他同级视图的布局,同级视图通过唯一的 ID +进行引用。

    +

    ID +不需要在整个结构树中具有唯一性,但在您要搜索的结构树部分应具有唯一性(要搜索的部分往往是整个结构树,因此最好尽可能具有全局唯一性)。 +

    + + +

    布局参数

    + +

    名为 layout_something 的 XML +布局属性可为视图定义与其所在的 ViewGroup 相适的布局参数。

    + +

    每个 ViewGroup 类都会实现一个扩展 {@link +android.view.ViewGroup.LayoutParams} 的嵌套类。此子类包含的属性类型会根据需要为视图组的每个子视图定义尺寸和位置。 + +正如您在图 1 +中所见,父视图组为每个子视图(包括子视图组)定义布局参数。

    + + +

    图 1. 以可视化方式表示的视图层次结构,其中包含与每个视图关联的布局参数。 +

    + +

    请注意,每个 LayoutParams +子类都有自己的值设置语法。每个子元素都必须定义适合其父元素的 +LayoutParams,但父元素也可为其子元素定义不同的 LayoutParams。

    + +

    所有视图组都包括宽度和高度(layout_width 和 +layout_height),并且每个视图都必须定义它们。许多 +LayoutParams 还包括可选的外边距和边框。

    + +

    您可以指定具有确切尺寸的宽度和高度,但您多半不想经常这样做。 +在更多的情况下,您会使用以下常量之一来设置宽度或高度: +

    + +
      +
    • wrap_content 指示您的视图将其大小调整为内容所需的尺寸 +
    • +
    • match_parent (在 API 级别 8 之前名为 fill_parent )指示您的视图尽可能采用其父视图组所允许的最大尺寸 +
    • +
    + +

    一般而言,建议不要使用绝对单位(如像素)来指定布局宽度和高度, +而是使用相对测量单位,如密度无关像素单位 +(dp)、 wrap_content 或 +match_parent,这种方法更好,因为它有助于确保您的应用在各类尺寸的设备屏幕上正确显示。可用资源文档中定义了可接受的测量单位类型。 + + + +

    + + +

    布局位置

    +

    + 视图的几何形状就是矩形的几何形状。视图具有一个位置(以一对水平向左垂直向上坐标表示)和两个尺寸(以宽度和高度表示)。 + +位置和尺寸的单位是像素。 + +

    + +

    + 可以通过调用方法 + {@link android.view.View#getLeft()} 和方法 {@link android.view.View#getTop()} 来检索视图的位置。前者会返回表示视图的矩形的水平向左(或称 X 轴) + 坐标。后者会返回表示视图的矩形的垂直向上(或称 Y 轴)坐标。 +这些方法都会返回视图相对于其父项的位置。 +例如,如果 getLeft() 返回 20,则意味着视图位于其直接父项左边缘向右 20 个像素处。 + + +

    + +

    + 此外,系统还提供了几种便捷方法来避免不必要的计算,即 {@link android.view.View#getRight()} 和 {@link android.view.View#getBottom()}。 + + 这些方法会返回表示视图的矩形的右边缘和下边缘的坐标。 +例如,调用 {@link android.view.View#getRight()} + 类似于进行以下计算:getLeft() + getWidth()。 +

    + + +

    尺寸、内边距和外边距

    +

    + 视图的尺寸通过宽度和高度表示。视图实际上具有两对宽度和高度值。 + +

    + +

    + 第一对称为测量宽度测量高度。 +这些尺寸定义视图想要在其父项内具有的大小。 + + 这些测量尺寸可以通过调用 {@link android.view.View#getMeasuredWidth()} + 和 {@link android.view.View#getMeasuredHeight()} 来获得。 +

    + +

    + 第二对简称为宽度高度,有时称为绘制宽度绘制高度。 +这些尺寸定义视图在绘制时和布局后在屏幕上的实际尺寸。 + +这些值可以(但不必)与测量宽度和测量高度不同。 +宽度和高度可以通过调用 + {@link android.view.View#getWidth()} 和 {@link android.view.View#getHeight()} 来获得。 +

    + +

    + 要想测量其尺寸,视图需要将其内边距考虑在内。内边距以视图左侧、顶部、右侧和底部各部分的像素数表示。 + + 内边距可用于以特定数量的 + 像素弥补视图的内容。例如,左侧内边距为 2,会将视图的内容从左边缘向右推 + 2 个像素。可以使用 + {@link android.view.View#setPadding(int, int, int, int)} 方法设置内边距,并通过调用 + {@link android.view.View#getPaddingLeft()}、{@link android.view.View#getPaddingTop()}、{@link android.view.View#getPaddingRight()} 和 {@link android.view.View#getPaddingBottom()} 进行查询。 + +

    + +

    + 尽管视图可以定义内边距,但它并不支持外边距。 +不过,视图组可以提供此类支持。如需了解更多信息,请参阅 + {@link android.view.ViewGroup} 和 + {@link android.view.ViewGroup.MarginLayoutParams}。 +

    + +

    如需了解有关尺寸的详细信息,请参阅 + 尺寸值。 +

    + + + + + + + + + + + +

    常见布局

    + +

    {@link android.view.ViewGroup} +类的每个子类都提供了一种独特的方式来显示您在其中嵌套的视图。以下是 +Android 平台中内置的一些较为常见的布局类型。

    + +

    注:尽管您可以通过将一个或多个布局嵌套在另一个布局内来实现您的 +UI +设计,但应该使您的布局层次结构尽可能简略。布局的嵌套布局越少,绘制速度越快(扁平的视图层次结构优于深层的视图层次结构)。 +

    + + + + +
    +

    线性布局

    + +

    一种使用单个水平行或垂直行来组织子项的布局。它会在窗口长度超出屏幕长度时创建一个滚动条。 +

    +
    + +
    +

    相对布局

    + +

    让您能够指定子对象彼此之间的相对位置(子对象 A +在子对象 B 左侧)或子对象与父对象的相对位置(与父对象顶部对齐)。

    +
    + +
    +

    Web 视图

    + +

    显示网页。

    +
    + + + + +

    使用适配器构建布局

    + +

    如果布局的内容是属于动态或未预先确定的内容,您可以使用这样一种布局:在运行时通过子类 +{@link android.widget.AdapterView} 用视图填充布局。{@link android.widget.AdapterView} +类的子类使用 {@link android.widget.Adapter} +将数据与其布局绑定。{@link android.widget.Adapter} +充当数据源与 {@link android.widget.AdapterView} +布局之间的中间人—{@link android.widget.Adapter}(从数组或数据库查询等来源)检索数据,并将每个条目转换为可以添加到 {@link android.widget.AdapterView} +布局中的视图。

    + +

    适配器支持的常见布局包括:

    + +
    +

    列表视图

    + +

    显示滚动的单列列表。

    +
    + +
    +

    网格视图

    + +

    显示滚动的行列网格。

    +
    + + + +

    使用数据填充适配器视图

    + +

    您可以通过将 {@link android.widget.AdapterView} 实例与 {@link android.widget.Adapter} 绑定来填充 {@link android.widget.AdapterView}(如 {@link android.widget.ListView} 或 +{@link android.widget.GridView}),此操作会从外部来源检索数据,并创建表示每个数据条目的 +{@link +android.view.View}。

    + +

    Android 提供了几个 {@link android.widget.Adapter} 子类,用于检索不同种类的数据和构建 +{@link android.widget.AdapterView} 的视图。两种最常见的适配器是: +

    + +
    +
    {@link android.widget.ArrayAdapter}
    +
    请在数据源为数组时使用此适配器。默认情况下,{@link +android.widget.ArrayAdapter} 会通过在每个项目上调用 {@link +java.lang.Object#toString()} 并将内容放入 {@link +android.widget.TextView} 来为每个数组项创建视图。 +

    例如,如果您具有想要在 {@link +android.widget.ListView} 中显示的字符串数组,请使用构造函数初始化一个新的 +{@link android.widget.ArrayAdapter},为每个字符串和字符串数组指定布局:

    +
    +ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
    +        android.R.layout.simple_list_item_1, myStringArray);
    +
    +

    此构造函数的参数是:

    +
      +
    • 您的应用 {@link android.content.Context}
    • +
    • 包含数组中每个字符串的 {@link android.widget.TextView} 的布局
    • +
    • 字符串数组
    • +
    +

    然后,只需在您的 {@link android.widget.ListView} 上调用 +{@link android.widget.ListView#setAdapter setAdapter()}:

    +
    +ListView listView = (ListView) findViewById(R.id.listview);
    +listView.setAdapter(adapter);
    +
    + +

    要想自定义每个项的外观,您可以重写数组中各个对象的 {@link +java.lang.Object#toString()} 方法。或者,要想为 +{@link android.widget.TextView} 之外的每个项创建视图(例如,如果您想为每个数组项创建一个 +{@link android.widget.ImageView}),请扩展 {@link +android.widget.ArrayAdapter} 类并重写 {@link android.widget.ArrayAdapter#getView +getView()} 以返回您想要为每个项获取的视图类型。

    + +
    + +
    {@link android.widget.SimpleCursorAdapter}
    +
    请在数据来自 {@link android.database.Cursor} 时使用此适配器。使用 +{@link android.widget.SimpleCursorAdapter} 时,您必须指定要为 {@link android.database.Cursor} +中的每个行使用的布局,以及应该在哪些布局视图中插入 {@link android.database.Cursor} +中的哪些列。例如,如果您想创建人员姓名和电话号码列表,则可以执行一个返回 +{@link +android.database.Cursor}(包含对应每个人的行,以及对应姓名和号码的列)的查询。 +然后,您可以创建一个字符串数组,指定您想要在每个结果的布局中包含 {@link +android.database.Cursor} +中的哪些列,并创建一个整型数组,指定应该将每个列放入的对应视图:

    +
    +String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
    +                        ContactsContract.CommonDataKinds.Phone.NUMBER};
    +int[] toViews = {R.id.display_name, R.id.phone_number};
    +
    +

    当您实例化 {@link android.widget.SimpleCursorAdapter} +时,请传递要用于每个结果的布局、包含结果的 {@link android.database.Cursor} 以及以下两个数组:

    +
    +SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
    +        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    +ListView listView = getListView();
    +listView.setAdapter(adapter);
    +
    +

    然后,{@link android.widget.SimpleCursorAdapter} 会使用提供的布局,将每个 +{@code +fromColumns} 项插入对应的 {@code toViews} 视图,为 {@link android.database.Cursor} 中的每个行创建一个视图。

    .
    +
    + + +

    如果您在应用的生命周期中更改了适配器读取的底层数据,则应调用 +{@link android.widget.ArrayAdapter#notifyDataSetChanged()}。此操作会通知附加的视图,数据发生了变化,它应该自行刷新。 +

    + + + +

    处理点击事件

    + +

    您可以通过实现 {@link android.widget.AdapterView.OnItemClickListener} +界面来响应 {@link android.widget.AdapterView} 中每一项上的点击事件。例如:

    + +
    +// Create a message handling object as an anonymous class.
    +private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
    +    public void onItemClick(AdapterView parent, View v, int position, long id) {
    +        // Do something in response to the click
    +    }
    +};
    +
    +listView.setOnItemClickListener(mMessageClickedHandler);
    +
    + + + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/dialogs.jd new file mode 100644 index 0000000000000000000000000000000000000000..84922b40e4454db00e07d512e3457f274e70dc53 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=对话框 +page.tags=提醒对话框,对话框片段 + +@jd:body + + + +
    +
    +

    本文内容

    +
      +
    1. 创建对话框片段
    2. +
    3. 构建提醒对话框 +
        +
      1. 添加按钮
      2. +
      3. 添加列表
      4. +
      5. 创建自定义布局
      6. +
      +
    4. +
    5. 将事件传递回对话框的宿主
    6. +
    7. 显示对话框
    8. +
    9. 全屏显示对话框或将其显示为嵌入式片段 +
        +
      1. 将 Activity 显示为大屏幕上的对话框
      2. +
      +
    10. +
    11. 清除对话框
    12. +
    + +

    关键类

    +
      +
    1. {@link android.app.DialogFragment}
    2. +
    3. {@link android.app.AlertDialog}
    4. +
    + +

    另请参阅

    +
      +
    1. 对话框设计指南
    2. +
    3. 选取器(日期/时间对话框)
    4. +
    +
    +
    + +

    对话框是提示用户作出决定或输入额外信息的小窗口。 +对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件。 +

    + +
    +

    对话框设计

    +

    如需了解有关如何设计对话框的信息(包括语言建议),请阅读对话框设计指南。 +

    +
    + + + +

    {@link android.app.Dialog} +类是对话框的基类,但您应该避免直接实例化 +{@link android.app.Dialog},而是使用下列子类之一:

    +
    +
    {@link android.app.AlertDialog}
    +
    此对话框可显示标题、最多三个按钮、可选择项列表或自定义布局。 +
    +
    {@link android.app.DatePickerDialog} 或 {@link android.app.TimePickerDialog}
    +
    此对话框带有允许用户选择日期或时间的预定义 UI。
    +
    + + + +

    这些类定义您的对话框的样式和结构,但您应该将 +{@link android.support.v4.app.DialogFragment} +用作对话框的容器。{@link android.support.v4.app.DialogFragment} +类提供您创建对话框和管理其外观所需的所有控件,而不是调用 {@link android.app.Dialog} +对象上的方法。

    + +

    使用 {@link android.support.v4.app.DialogFragment} +管理对话框可确保它能正确处理生命周期事件,如用户按“返回”按钮或旋转屏幕时。 +此外,{@link +android.support.v4.app.DialogFragment} 类还允许您将对话框的 UI 作为嵌入式组件在较大 UI 中重复使用,就像传统 {@link +android.support.v4.app.Fragment} +一样(例如,当您想让对话框 UI +在大屏幕和小屏幕上具有不同外观时)。

    + +

    本指南的后文将描述如何将 {@link +android.support.v4.app.DialogFragment} 与 {@link android.app.AlertDialog} +对象结合使用。如果您想创建一个日期或时间选取器,应改为阅读选取器指南。 +

    + +

    注:由于 +{@link android.app.DialogFragment} 类最初是通过 +Android 3.0(API 11 级)添加的,因此本文描述的是如何使用支持库附带的 {@link +android.support.v4.app.DialogFragment} 类。通过将该库添加到您的应用,您可以在运行 +Android 1.6 或更高版本的设备上使用 {@link android.support.v4.app.DialogFragment} 以及各种其他 +API。如果您的应用支持的最低版本是 +API 11 级或更高版本,则可使用 {@link +android.app.DialogFragment} 的框架版本,但请注意,本文中的链接适用于支持库 +API。使用支持库时,请确保您导入的是 +android.support.v4.app.DialogFragment +类,而“绝对不”android.app.DialogFragment

    + + +

    创建对话框片段

    + +

    您可以完成各种对话框设计—包括自定义布局以及对话框设计指南中描述的布局—通过扩展 +{@link android.support.v4.app.DialogFragment} +并在 +{@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()} +回调方法中创建 {@link android.app.AlertDialog}。

    + +

    例如,以下是一个在 +{@link android.support.v4.app.DialogFragment} 内管理的基础 {@link android.app.AlertDialog}:

    + +
    +public class FireMissilesDialogFragment extends DialogFragment {
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // Use the Builder class for convenient dialog construction
    +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +        builder.setMessage(R.string.dialog_fire_missiles)
    +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // FIRE ZE MISSILES!
    +                   }
    +               })
    +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // User cancelled the dialog
    +                   }
    +               });
    +        // Create the AlertDialog object and return it
    +        return builder.create();
    +    }
    +}
    +
    + +
    + +

    图 1. 一个包含消息和两个操作按钮的对话框。 +

    +
    + +

    现在,当您创建此类的实例并调用该对象上的 {@link +android.support.v4.app.DialogFragment#show show()} 时,对话框将如图 +1 所示。

    + +

    下文将详细描述如何使用 {@link android.app.AlertDialog.Builder} +API 创建对话框。

    + +

    根据对话框的复杂程度,您可以在 +{@link android.support.v4.app.DialogFragment} +中实现各种其他回调方法,包括所有基础 片段生命周期方法。 + + + + + +

    构建提醒对话框

    + + +

    您可以通过 {@link android.app.AlertDialog} +类构建各种对话框设计,并且该类通常是您需要的唯一对话框类。如图 2 +所示,提醒对话框有三个区域:

    + +
    + +

    图 2. 对话框的布局。

    +
    + +
      +
    1. 标题 +

      这是可选项,只应在内容区域被详细消息、列表或自定义布局占据时使用。 +如需陈述的是一条简单消息或问题(如图 1 中的对话框),则不需要标题。 +

    2. +
    3. 内容区域 +

      它可以显示消息、列表或其他自定义布局。

    4. +
    5. 操作按钮 +

      对话框中的操作按钮不应超过三个。

    6. +
    + +

    {@link android.app.AlertDialog.Builder} +类提供的 API 允许您创建具有这几种内容(包括自定义布局)的 +{@link android.app.AlertDialog}。

    + +

    要想构建 {@link android.app.AlertDialog},请执行以下操作:

    + +
    +// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor
    +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +
    +// 2. Chain together various setter methods to set the dialog characteristics
    +builder.setMessage(R.string.dialog_message)
    +       .setTitle(R.string.dialog_title);
    +
    +// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}
    +AlertDialog dialog = builder.create();
    +
    + +

    以下主题介绍如何使用 +{@link android.app.AlertDialog.Builder} 类定义各种对话框属性。

    + + + + +

    添加按钮

    + +

    要想添加如图 2 +所示的操作按钮,请调用 {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} 和 +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()} 方法:

    + +
    +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +// Add the buttons
    +builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    +           public void onClick(DialogInterface dialog, int id) {
    +               // User clicked OK button
    +           }
    +       });
    +builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +           public void onClick(DialogInterface dialog, int id) {
    +               // User cancelled the dialog
    +           }
    +       });
    +// Set other dialog properties
    +...
    +
    +// Create the AlertDialog
    +AlertDialog dialog = builder.create();
    +
    + +

    set...Button() +方法需要一个按钮标题(由字符串资源提供)和一个 +{@link android.content.DialogInterface.OnClickListener},后者用于定义用户按下该按钮时执行的操作。 +

    + +

    您可以添加三种不同的操作按钮:

    +
    +
    肯定
    +
    您应该使用此按钮来接受并继续执行操作(“确定”操作)。
    +
    否定
    +
    您应该使用此按钮来取消操作。
    +
    中性
    +
    您应该在用户可能不想继续执行操作,但也不一定想要取消操作时使用此按钮。 +它出现在肯定按钮和否定按钮之间。 +例如,实际操作可能是“稍后提醒我”。
    +
    + +

    对于每种按钮类型,您只能为 {@link +android.app.AlertDialog} 添加一个该类型的按钮。也就是说,您不能添加多个“肯定”按钮。

    + + + +
    + +

    图 3. 一个包含标题和列表的对话框。 +

    +
    + +

    添加列表

    + +

    可通过 {@link android.app.AlertDialog} API 提供三种列表:

    +
      +
    • 传统单选列表
    • +
    • 永久性单选列表(单选按钮)
    • +
    • 永久性多选列表(复选框)
    • +
    + +

    要想创建如图 3 所示的单选列表,请使用 +{@link android.app.AlertDialog.Builder#setItems setItems()} 方法:

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    builder.setTitle(R.string.pick_color)
    +           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
    +               public void onClick(DialogInterface dialog, int which) {
    +               // The 'which' argument contains the index position
    +               // of the selected item
    +           }
    +    });
    +    return builder.create();
    +}
    +
    + +

    由于列表出现在对话框的内容区域,因此对话框无法同时显示消息和列表,您应该通过 +{@link android.app.AlertDialog.Builder#setTitle setTitle()} +为对话框设置标题。要想指定列表项,请调用 +{@link +android.app.AlertDialog.Builder#setItems setItems()} 来传递一个数组。或者,您也可以使用 +{@link +android.app.AlertDialog.Builder#setAdapter setAdapter()} 指定一个列表。这样一来,您就可以使用 {@link android.widget.ListAdapter} +以动态数据(如来自数据库的数据)支持列表。

    + +

    如果您选择通过 {@link android.widget.ListAdapter} +支持列表,请务必使用 +{@link android.support.v4.content.Loader},以便内容以异步方式加载。使用适配器构建布局加载程序指南中对此做了进一步描述。 + + +

    + +

    注:默认情况下,触摸列表项会清除对话框,除非您使用的是下列其中一种永久性选择列表。 +

    + +
    + +

    图 4. +多选项列表。

    +
    + + +

    添加永久性多选列表或单选列表

    + +

    要想添加多选项(复选框)或单选项(单选按钮)列表,请分别使用 +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} 或 +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} +方法。

    + +

    例如,以下示例展示了如何创建如图 4 +所示的多选列表,将选定项保存在一个 +{@link java.util.ArrayList} 中:

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    mSelectedItems = new ArrayList();  // Where we track the selected items
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    // Set the dialog title
    +    builder.setTitle(R.string.pick_toppings)
    +    // Specify the list array, the items to be selected by default (null for none),
    +    // and the listener through which to receive callbacks when items are selected
    +           .setMultiChoiceItems(R.array.toppings, null,
    +                      new DialogInterface.OnMultiChoiceClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int which,
    +                       boolean isChecked) {
    +                   if (isChecked) {
    +                       // If the user checked the item, add it to the selected items
    +                       mSelectedItems.add(which);
    +                   } else if (mSelectedItems.contains(which)) {
    +                       // Else, if the item is already in the array, remove it 
    +                       mSelectedItems.remove(Integer.valueOf(which));
    +                   }
    +               }
    +           })
    +    // Set the action buttons
    +           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   // User clicked OK, so save the mSelectedItems results somewhere
    +                   // or return them to the component that opened the dialog
    +                   ...
    +               }
    +           })
    +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   ...
    +               }
    +           });
    +
    +    return builder.create();
    +}
    +
    + +

    尽管传统列表和具有单选按钮的列表都能提供“单选”操作,但如果您想持久保存用户的选择,则应使用 +{@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()}。也就是说,如果稍后再次打开对话框时系统应指示用户的当前选择,那么您就需要创建一个具有单选按钮的列表。 + +

    + + + + + +

    创建自定义布局

    + +
    + +

    图 5. 自定义对话框布局。

    +
    + +

    如果您想让对话框具有自定义布局,请创建一个布局,然后通过调用 +{@link +android.app.AlertDialog.Builder} 对象上的 {@link +android.app.AlertDialog.Builder#setView setView()} 将其添加到 {@link android.app.AlertDialog}。

    + +

    默认情况下,自定义布局会填充对话框窗口,但您仍然可以使用 +{@link android.app.AlertDialog.Builder} 方法来添加按钮和标题。

    + +

    例如,以下是图 5 中对话框的布局文件:

    + +

    res/layout/dialog_signin.xml

    +
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:orientation="vertical"
    +    android:layout_width="wrap_content"
    +    android:layout_height="wrap_content">
    +    <ImageView
    +        android:src="@drawable/header_logo"
    +        android:layout_width="match_parent"
    +        android:layout_height="64dp"
    +        android:scaleType="center"
    +        android:background="#FFFFBB33"
    +        android:contentDescription="@string/app_name" />
    +    <EditText
    +        android:id="@+id/username"
    +        android:inputType="textEmailAddress"
    +        android:layout_width="match_parent"
    +        android:layout_height="wrap_content"
    +        android:layout_marginTop="16dp"
    +        android:layout_marginLeft="4dp"
    +        android:layout_marginRight="4dp"
    +        android:layout_marginBottom="4dp"
    +        android:hint="@string/username" />
    +    <EditText
    +        android:id="@+id/password"
    +        android:inputType="textPassword"
    +        android:layout_width="match_parent"
    +        android:layout_height="wrap_content"
    +        android:layout_marginTop="4dp"
    +        android:layout_marginLeft="4dp"
    +        android:layout_marginRight="4dp"
    +        android:layout_marginBottom="16dp"
    +        android:fontFamily="sans-serif"
    +        android:hint="@string/password"/>
    +</LinearLayout>
    +
    + +

    提示:默认情况下,当您将 {@link android.widget.EditText} +元素设置为使用 {@code "textPassword"} +输入类型时,字体系列将设置为固定宽度。因此,您应该将其字体系列更改为 +{@code "sans-serif"},以便两个文本字段都使用匹配的字体样式。

    + +

    要扩展 {@link android.support.v4.app.DialogFragment} +中的布局,请通过 {@link android.app.Activity#getLayoutInflater()} +获取一个 {@link android.view.LayoutInflater} 并调用 +{@link android.view.LayoutInflater#inflate inflate()},其中第一个参数是布局资源 +ID,第二个参数是布局的父视图。然后,您可以调用 +{@link android.app.AlertDialog#setView setView()} +将布局放入对话框。

    + +
    +@Override
    +public Dialog onCreateDialog(Bundle savedInstanceState) {
    +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +    // Get the layout inflater
    +    LayoutInflater inflater = getActivity().getLayoutInflater();
    +
    +    // Inflate and set the layout for the dialog
    +    // Pass null as the parent view because its going in the dialog layout
    +    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
    +    // Add action buttons
    +           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
    +               @Override
    +               public void onClick(DialogInterface dialog, int id) {
    +                   // sign in the user ...
    +               }
    +           })
    +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +               public void onClick(DialogInterface dialog, int id) {
    +                   LoginDialogFragment.this.getDialog().cancel();
    +               }
    +           });      
    +    return builder.create();
    +}
    +
    + +
    +

    提示:如果您想要自定义对话框,可以改用对话框的形式显示 {@link android.app.Activity},而不是使用 {@link android.app.Dialog} +API。 +只需创建一个 Activity,并在 +{@code +<activity>} 清单文件元素中将其主题设置为 +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog}:

    + +
    +<activity android:theme="@android:style/Theme.Holo.Dialog" >
    +
    +

    就这么简单。Activity 现在会显示在一个对话框窗口中,而非全屏显示。

    +
    + + + +

    将事件传回给对话框的宿主

    + +

    当用户触摸对话框的某个操作按钮或从列表中选择某一项时,您的 +{@link android.support.v4.app.DialogFragment} +可能会自行执行必要的操作,但通常您想将事件传递给打开该对话框的 Activity 或片段。 +为此,请定义一个界面,为每种点击事件定义一种方法。然后在从该对话框接收操作事件的宿主组件中实现该界面。 + +

    + +

    例如,以下 {@link android.support.v4.app.DialogFragment} +定义了一个界面,通过该界面将事件传回给宿主 Activity:

    + +
    +public class NoticeDialogFragment extends DialogFragment {
    +    
    +    /* The activity that creates an instance of this dialog fragment must
    +     * implement this interface in order to receive event callbacks.
    +     * Each method passes the DialogFragment in case the host needs to query it. */
    +    public interface NoticeDialogListener {
    +        public void onDialogPositiveClick(DialogFragment dialog);
    +        public void onDialogNegativeClick(DialogFragment dialog);
    +    }
    +    
    +    // Use this instance of the interface to deliver action events
    +    NoticeDialogListener mListener;
    +    
    +    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    +    @Override
    +    public void onAttach(Activity activity) {
    +        super.onAttach(activity);
    +        // Verify that the host activity implements the callback interface
    +        try {
    +            // Instantiate the NoticeDialogListener so we can send events to the host
    +            mListener = (NoticeDialogListener) activity;
    +        } catch (ClassCastException e) {
    +            // The activity doesn't implement the interface, throw exception
    +            throw new ClassCastException(activity.toString()
    +                    + " must implement NoticeDialogListener");
    +        }
    +    }
    +    ...
    +}
    +
    + +

    对话框的宿主 Activity 会通过对话框片段的构造函数创建一个对话框实例,并通过实现的 +{@code NoticeDialogListener} +界面接收对话框的事件:

    + +
    +public class MainActivity extends FragmentActivity
    +                          implements NoticeDialogFragment.NoticeDialogListener{
    +    ...
    +    
    +    public void showNoticeDialog() {
    +        // Create an instance of the dialog fragment and show it
    +        DialogFragment dialog = new NoticeDialogFragment();
    +        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
    +    }
    +
    +    // The dialog fragment receives a reference to this Activity through the
    +    // Fragment.onAttach() callback, which it uses to call the following methods
    +    // defined by the NoticeDialogFragment.NoticeDialogListener interface
    +    @Override
    +    public void onDialogPositiveClick(DialogFragment dialog) {
    +        // User touched the dialog's positive button
    +        ...
    +    }
    +
    +    @Override
    +    public void onDialogNegativeClick(DialogFragment dialog) {
    +        // User touched the dialog's negative button
    +        ...
    +    }
    +}
    +
    + +

    由于宿主 Activity 会实现 +{@code NoticeDialogListener}—由以上显示的 +{@link android.support.v4.app.Fragment#onAttach onAttach()} +回调方法强制执行 — 因此对话框片段可以使用界面回调方法向 Activity 传递点击事件:

    + +
    +public class NoticeDialogFragment extends DialogFragment {
    +    ...
    +
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // Build the dialog and set up the button click handlers
    +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    +        builder.setMessage(R.string.dialog_fire_missiles)
    +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // Send the positive button event back to the host activity
    +                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
    +                   }
    +               })
    +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
    +                   public void onClick(DialogInterface dialog, int id) {
    +                       // Send the negative button event back to the host activity
    +                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
    +                   }
    +               });
    +        return builder.create();
    +    }
    +}
    +
    + + + +

    显示对话框

    + +

    如果您想显示对话框,请创建一个 {@link +android.support.v4.app.DialogFragment} 实例并调用 {@link android.support.v4.app.DialogFragment#show +show()},以传递对话框片段的 {@link android.support.v4.app.FragmentManager} +和标记名称。

    + +

    您可以通过从 {@link android.support.v4.app.FragmentActivity} +调用 {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} 或从 +{@link +android.support.v4.app.Fragment} 调用 {@link +android.support.v4.app.Fragment#getFragmentManager()} 来获取 {@link android.support.v4.app.FragmentManager}。例如:

    + +
    +public void confirmFireMissiles() {
    +    DialogFragment newFragment = new FireMissilesDialogFragment();
    +    newFragment.show(getSupportFragmentManager(), "missiles");
    +}
    +
    + +

    第二个参数 {@code "missiles"} +是系统用于保存片段状态并在必要时进行恢复的唯一标记名称。该标记还允许您通过调用 +{@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()} 获取片段的句柄。

    + + + + +

    全屏显示对话框或将其显示为嵌入式片段

    + +

    您可能采用以下 UI +设计:您想让一部分 UI +在某些情况下显示为对话框,但在其他情况下全屏显示或显示为嵌入式片段(也许取决于设备使用大屏幕还是小屏幕)。{@link android.support.v4.app.DialogFragment} +类便具有这种灵活性,因为它仍然可以充当嵌入式 {@link +android.support.v4.app.Fragment}。

    + +

    但在这种情况下,您不能使用 {@link android.app.AlertDialog.Builder AlertDialog.Builder} +或其他 {@link android.app.Dialog} 对象来构建对话框。如果您想让 {@link android.support.v4.app.DialogFragment} +具有嵌入能力,则必须在布局中定义对话框的 +UI,然后在 +{@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()} 回调中加载布局。

    + +

    以下示例 {@link android.support.v4.app.DialogFragment} 可以显示为对话框或嵌入式片段(使用名为 +purchase_items.xml 的布局):

    + +
    +public class CustomDialogFragment extends DialogFragment {
    +    /** The system calls this to get the DialogFragment's layout, regardless
    +        of whether it's being displayed as a dialog or an embedded fragment. */
    +    @Override
    +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    +            Bundle savedInstanceState) {
    +        // Inflate the layout to use as dialog or embedded fragment
    +        return inflater.inflate(R.layout.purchase_items, container, false);
    +    }
    +  
    +    /** The system calls this only when creating the layout in a dialog. */
    +    @Override
    +    public Dialog onCreateDialog(Bundle savedInstanceState) {
    +        // The only reason you might override this method when using onCreateView() is
    +        // to modify any dialog characteristics. For example, the dialog includes a
    +        // title by default, but your custom layout might not need it. So here you can
    +        // remove the dialog title, but you must call the superclass to get the Dialog.
    +        Dialog dialog = super.onCreateDialog(savedInstanceState);
    +        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    +        return dialog;
    +    }
    +}
    +
    + +

    以下代码可根据屏幕尺寸决定将片段显示为对话框还是全屏 +UI:

    + +
    +public void showDialog() {
    +    FragmentManager fragmentManager = getSupportFragmentManager();
    +    CustomDialogFragment newFragment = new CustomDialogFragment();
    +    
    +    if (mIsLargeLayout) {
    +        // The device is using a large layout, so show the fragment as a dialog
    +        newFragment.show(fragmentManager, "dialog");
    +    } else {
    +        // The device is smaller, so show the fragment fullscreen
    +        FragmentTransaction transaction = fragmentManager.beginTransaction();
    +        // For a little polish, specify a transition animation
    +        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    +        // To make it fullscreen, use the 'content' root view as the container
    +        // for the fragment, which is always the root view for the activity
    +        transaction.add(android.R.id.content, newFragment)
    +                   .addToBackStack(null).commit();
    +    }
    +}
    +
    + +

    如需了解有关执行片段事务的详细信息,请参阅片段指南。 +

    + +

    在本示例中,mIsLargeLayout +布尔值指定当前设备是否应该使用应用的大布局设计(进而将此片段显示为对话框,而不是全屏显示)。 +设置这种布尔值的最佳方法是声明一个布尔资源值,其中包含适用于不同屏幕尺寸的备用资源值。 + +例如,以下两个版本的布尔资源适用于不同的屏幕尺寸: +

    + +

    res/values/bools.xml

    +
    +<!-- Default boolean values -->
    +<resources>
    +    <bool name="large_layout">false</bool>
    +</resources>
    +
    + +

    res/values-large/bools.xml

    +
    +<!-- Large screen boolean values -->
    +<resources>
    +    <bool name="large_layout">true</bool>
    +</resources>
    +
    + +

    然后,您可以在 Activity 的 +{@link android.app.Activity#onCreate onCreate()} 方法执行期间初始化 {@code mIsLargeLayout} 值:

    + +
    +boolean mIsLargeLayout;
    +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    setContentView(R.layout.activity_main);
    +
    +    mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
    +}
    +
    + + + +

    在大屏幕上将 Activity 显示为对话框

    + +

    相对于在小屏幕上将对话框显示为全屏 +UI,您可以通过在大屏幕上将 {@link android.app.Activity} +显示为对话框来达到相同的效果。您选择的方法取决于应用设计,但当应用已经针对小屏幕进行设计,而您想要通过将短生存期 Activity 显示为对话框来改善平板电脑体验时,将 Activity 显示为对话框往往很有帮助。 + + +

    + +

    要想仅在大屏幕上将 Activity 显示为对话框,请将 +{@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} +主题应用于 {@code +<activity>} 清单文件元素:

    + +
    +<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
    +
    + +

    如需了解有关通过主题设置 Activity 样式的详细信息,请参阅样式和主题指南。

    + + + +

    清除对话框

    + +

    当用户触摸使用 +{@link android.app.AlertDialog.Builder} 创建的任何操作按钮时,系统会为您清除对话框。

    + +

    系统还会在用户触摸某个对话框列表项时清除对话框,但列表使用单选按钮或复选框时除外。 +否则,您可以通过在 {@link +android.support.v4.app.DialogFragment} 上调用 +{@link android.support.v4.app.DialogFragment#dismiss()} 来手动清除对话框。

    + +

    如需在对话框消失时执行特定操作,则可以在您的 +{@link +android.support.v4.app.DialogFragment} 中实现 {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} 方法。

    + +

    您还可以取消对话框。这是一个特殊事件,它表示用户显式离开对话框,而不完成任务。 +如果用户按“返回”按钮,触摸对话框区域外部的屏幕,或者您在 {@link +android.app.Dialog} 上显式调用 {@link android.app.Dialog#cancel()}(例如,为了响应对话框中的“取消”按钮),就会发生这种情况。 + +

    + +

    如上例所示,您可以通过在您的 {@link +android.support.v4.app.DialogFragment} 中实现 +{@link android.support.v4.app.DialogFragment#onCancel onCancel()} 来响应取消事件。

    + +

    注:系统会在每个调用 {@link android.support.v4.app.DialogFragment#onCancel onCancel()} +回调的事件发生时立即调用 +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}。不过,如果您调用 {@link android.app.Dialog#dismiss Dialog.dismiss()} 或 +{@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()},系统会调用 +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()},“而绝不会”调用 +{@link android.support.v4.app.DialogFragment#onCancel onCancel()}。因此,当用户在对话框中按“肯定”按钮,从视图中移除对话框时,您通常应该调用 +{@link android.support.v4.app.DialogFragment#dismiss dismiss()}。 +

    + + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/menus.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/menus.jd new file mode 100644 index 0000000000000000000000000000000000000000..b77f3bf497daa663f7332d0f0e6662050520e8c6 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=菜单 +parent.title=用户界面 +parent.link=index.html +@jd:body + +
    +
    +

    本文内容

    +
      +
    1. 使用 XML 定义菜单
    2. +
    3. 创建选项菜单 +
        +
      1. 处理点击事件
      2. +
      3. 在运行时更改菜单项
      4. +
      +
    4. +
    5. 创建上下文菜单 +
        +
      1. 创建浮动上下文菜单
      2. +
      3. 使用上下文操作模式
      4. +
      +
    6. +
    7. 创建弹出菜单 +
        +
      1. 处理点击事件
      2. +
      +
    8. +
    9. 创建菜单组 +
        +
      1. 使用可选中的菜单项
      2. +
      +
    10. +
    11. 添加基于 Intent 的菜单项 +
        +
      1. 允许将 Activity 添加到其他菜单中
      2. +
      +
    12. +
    + +

    关键类

    +
      +
    1. {@link android.view.Menu}
    2. +
    3. {@link android.view.MenuItem}
    4. +
    5. {@link android.view.ContextMenu}
    6. +
    7. {@link android.view.ActionMode}
    8. +
    + +

    另请参阅

    +
      +
    1. 操作栏
    2. +
    3. 菜单资源
    4. +
    5. 从此告别菜单按钮 +
    6. +
    +
    +
    + +

    菜单是许多应用中常见的用户界面组件。要提供熟悉而一致的用户体验,您应使用 {@link android.view.Menu} API +呈现 Activity 中的用户操作和其他选项。 +

    + +

    从 +Android 3.0(API 级别 11)开始,采用 Android 技术的设备不必再提供一个专用“菜单”按钮。随着这种改变,Android +应用需摆脱对包含 +6 个项目的传统菜单面板的依赖,取而代之的是要提供一个操作栏来呈现常见的用户操作。

    + +

    尽管某些菜单项的设计和用户体验已发生改变,但定义一系列操作和选项所使用的语义仍是以 +{@link android.view.Menu} API 为基础。本指南将介绍所有 +Android 版本系统中三种基本菜单或操作呈现效果的创建方法: +

    + +
    +
    选项菜单和操作栏
    +
    选项菜单是某个 Activity 的主菜单项, +供您放置对应用产生全局影响的操作,如“搜索”、“撰写电子邮件”和“设置”。 + +

    如果您的应用是针对 +Android 2.3 或更低版本的系统而开发,则用户可以通过按“菜单”按钮显示选项菜单面板。

    +

    在 +Android 3.0 及更高版本的系统中,操作栏以屏幕操作项和溢出选项的组合形式呈现选项菜单中的各项。从 Android 3.0 开始,“菜单”按钮已弃用(某些设备没有该按钮),因此您应改为使用操作栏,来提供对操作和其他选项的访问。 + + +

    +

    请参阅创建选项菜单部分。

    +
    + +
    上下文菜单和上下文操作模式
    + +
    上下文菜单是用户长按某一元素时出现的浮动菜单。 +它提供的操作将影响所选内容或上下文框架。 + +

    开发针对 Android 3.0 及更高版本系统的应用时,您应改为使用上下文操作模式,以便对所选内容启用操作。此模式在屏幕顶部栏显示影响所选内容的操作项目,并允许用户选择多项。 + +

    +

    请参阅创建上下文菜单部分。

    +
    + +
    弹出菜单
    +
    弹出菜单将以垂直列表形式显示一系列项目,这些项目将锚定到调用该菜单的视图中。 +它特别适用于提供与特定内容相关的大量操作,或者为命令的另一部分提供选项。 +弹出菜单中的操作不会直接影响对应的内容,而上下文操作则会影响。 + +相反,弹出菜单适用于与您 Activity 中的内容区域相关的扩展操作。 + +

    请参阅创建弹出菜单部分。

    +
    +
    + + + +

    使用 XML 定义菜单

    + +

    对于所有菜单类型,Android +提供了标准的 XML 格式来定义菜单项。您应在 +XML 菜单资源中定义菜单及其所有项,而不是在 Activity 的代码中构建菜单。定义后,您可以在 Activity 或片段中扩充菜单资源(将其作为 +{@link android.view.Menu} +对象加载)。

    + +

    使用菜单资源是一种很好的做法,原因如下:

    +
      +
    • 更易于使用 XML 可视化菜单结构
    • +
    • 将菜单内容与应用的行为代码分离
    • +
    • 允许您利用应用资源框架,为不同的平台版本、屏幕尺寸和其他配置创建备用菜单配置 +
    • +
    + +

    要定义菜单,请在项目 res/menu/ +目录内创建一个 XML 文件,并使用以下元素构建菜单:

    +
    +
    <menu>
    +
    定义 {@link android.view.Menu},即菜单项的容器。<menu> +元素必须是该文件的根节点,并且能够包含一个或多个 +<item><group> 元素。
    + +
    <item>
    +
    创建 {@link android.view.MenuItem},此元素表示菜单中的一项,可能包含嵌套的 <menu> +元素,以便创建子菜单。
    + +
    <group>
    +
    {@code <item>} 元素的不可见容器(可选)。它支持您对菜单项进行分类,使其共享活动状态和可见性等属性。 +如需了解详细信息,请参阅创建菜单组部分。 +
    +
    + + +

    以下是名为 game_menu.xml 的菜单示例:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/new_game"
    +          android:icon="@drawable/ic_new_game"
    +          android:title="@string/new_game"
    +          android:showAsAction="ifRoom"/>
    +    <item android:id="@+id/help"
    +          android:icon="@drawable/ic_help"
    +          android:title="@string/help" />
    +</menu>
    +
    + +

    <item> +元素支持多个属性,您可使用这些属性定义项目的外观和行为。上述菜单中的项目包括以下属性:

    + +
    +
    {@code android:id}
    +
    项目特有的资源 +ID,让应用能够在用户选择项目时识别该项目。
    +
    {@code android:icon}
    +
    引用一个要用作项目图标的 Drawable 类。
    +
    {@code android:title}
    +
    引用一个要用作项目标题的字符串。
    +
    {@code android:showAsAction}
    +
    指定此项应作为操作项目显示在操作栏中的时间和方式。
    +
    + +

    这些是您应使用的最重要属性,但还有许多其他属性。有关所有受支持属性的信息,请参阅菜单资源文档。 +

    + +

    您可以通过以 {@code <item>} +子元素的形式添加 {@code <menu>} 元素,向任何菜单(子菜单除外)中的某个菜单项添加子菜单。当应用具有大量可按主题进行组织的功能时,类似于 PC 应用程序菜单栏中的菜单项(“文件”、“编辑”、“视图”等),子菜单非常有用。 + +例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/file"
    +          android:title="@string/file" >
    +        <!-- "file" submenu -->
    +        <menu>
    +            <item android:id="@+id/create_new"
    +                  android:title="@string/create_new" />
    +            <item android:id="@+id/open"
    +                  android:title="@string/open" />
    +        </menu>
    +    </item>
    +</menu>
    +
    + +

    要在 Activity 中使用菜单,您需要使用 {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()} 扩充菜单资源(将 XML +资源转换为可编程对象)。在下文中,您将了解如何扩充每种类型的菜单。 +

    + + + +

    创建选项菜单

    + +
    + +

    图 1. Android 2.3 系统上浏览器 +中的选项菜单。

    +
    + +

    在选项菜单中,您应当包括与当前 Activity 上下文相关的操作和其他选项,如“搜索”、“撰写电子邮件”和“设置”。 +

    + +

    选项菜单中的项目在屏幕上的显示位置取决于您开发的应用所适用的 Android 版本: +

    + +
      +
    • 如果您开发的应用是用于 +Android +2.3.x(API 级别 10)或更低版本的系统,则当用户按“菜单”按钮时,选项菜单的内容会出现在屏幕底部,如图 1 所示。打开时,第一个可见部分是图标菜单,其中包含多达 6 个菜单项。 + +如果菜单包括 6 个以上项目,则 +Android +会将第六项和其余项目放入溢出菜单。用户可以通过选择“更多”打开该菜单。
    • + +
    • 如果您开发的应用是用于 Android +3.0(API 级别 11)及更高版本的系统,则选项菜单中的项目将出现在操作栏中。默认情况下,系统会将所有项目均放入操作溢出菜单中。用户可以使用操作栏右侧的操作溢出菜单图标(或者,通过按设备“菜单”按钮(如有))显示操作溢出菜单。 + +要支持快速访问重要操作,您可以将 +{@code android:showAsAction="ifRoom"} +添加到对应的 +{@code <item>} 元素,从而将几个项目提升到操作栏中(请参阅图 +2)。

      如需了解有关操作项目和其他操作栏行为的详细信息,请参阅操作栏指南。

      +

      注:即使您的应用不是针对 +Android 3.0 或更高版本的系统而开发,但仍可构建自己的操作栏布局,以获得类似的效果。有关如何使用操作栏支持旧版 +Android +的示例,请参阅操作栏兼容性示例。

      +
    • +
    + + +

    图 2. 摘自 +Honeycomb Gallery 应用的操作栏,其中显示了导航选项卡和相机操作项目(以及操作溢出菜单按钮)。

    + +

    您可以通过 {@link android.app.Activity} +子类或 {@link android.app.Fragment} 子类为选项菜单声明项目。如果您的 Activity 和片段均为选项菜单声明项目,则这些项目将合并到 +UI 中。系统将首先显示 Activity 的项目,随后按每个片段添加到 Activity 中的顺序显示该片段的项目。 + +如有必要,您可以使用 {@code android:orderInCategory} +属性,对需要移动的每个 {@code <item>} 中的菜单项重新排序。

    + +

    要为 Activity 指定选项菜单,请重写 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}(Fragment 会提供自己的 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} 回调)。通过此方法,您可以将菜单资源(使用 +XML 定义)扩充到回调中提供的 {@link +android.view.Menu} 中。例如:

    + +
    +@Override
    +public boolean onCreateOptionsMenu(Menu menu) {
    +    MenuInflater inflater = {@link android.app.Activity#getMenuInflater()};
    +    inflater.inflate(R.menu.game_menu, menu);
    +    return true;
    +}
    +
    + +

    此外,您还可以使用 {@link android.view.Menu#add(int,int,int,int) +add()} 添加菜单项,并使用 +{@link android.view.Menu#findItem findItem()} 检索项目,以便使用 {@link android.view.MenuItem} API 修改其属性。

    + +

    如果您开发的应用是用于 Android 2.3.x 及更低版本的系统,则当用户首次打开选项菜单时,系统会调用 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} +来创建该菜单。如果您开发的应用是用于 +Android 3.0 及更高版本的系统,则系统将在启动 Activity 时调用 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()},以便向操作栏显示项目。

    + + + +

    处理点击事件

    + +

    用户从选项菜单中选择项目(包括操作栏中的操作项目)时,系统将调用 Activity 的 +{@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} 方法。此方法将传递所选的 {@link android.view.MenuItem}。您可以通过调用 +{@link android.view.MenuItem#getItemId()} 方法来识别项目,该方法将返回菜单项的唯一 ID(由菜单资源中的 {@code android:id} 属性定义,或通过提供给 {@link android.view.Menu#add(int,int,int,int) add()} +方法的整型数定义)。 +您可以将此 +ID 与已知的菜单项匹配,以执行适当的操作。例如:

    + +
    +@Override
    +public boolean onOptionsItemSelected(MenuItem item) {
    +    // Handle item selection
    +    switch (item.getItemId()) {
    +        case R.id.new_game:
    +            newGame();
    +            return true;
    +        case R.id.help:
    +            showHelp();
    +            return true;
    +        default:
    +            return super.onOptionsItemSelected(item);
    +    }
    +}
    +
    + +

    成功处理菜单项后,系统将返回 {@code true}。如果未处理菜单项,则应调用 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} +的超类实现(默认实现将返回 false)。 +

    + +

    如果 Activity 包括片段,则系统将依次为 Activity 和每个片段(按照每个片段的添加顺序)调用 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} +,直到有一返回结果为 +{@code true} 或所有片段均调用完毕为止。

    + +

    提示:Android 3.0 新增了一项功能,支持您在 XML 中使用 {@code android:onClick} +属性为菜单项定义点击行为。该属性的值必须是 Activity 使用菜单定义的方法的名称。 +该方法必须是公用的,且接受单个 +{@link android.view.MenuItem} +参数;当系统调用此方法时,它会传递所选的菜单项。如需了解详细信息和示例,请参阅菜单资源文档。

    + +

    提示:如果应用包含多个 Activity,且其中某些 Activity 提供相同的选项菜单,则可考虑创建一个仅实现 +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 和 {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} +方法的 Activity。然后,为每个应共享相同选项菜单的 Activity 扩展此类。 +通过这种方式,您可以管理一个用于处理菜单操作的代码集,且每个子级类均会继承菜单行为。若要将菜单项添加到一个子级 Activity,请重写该 Activity 中的 +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}。 + +调用 {@code super.onCreateOptionsMenu(menu)},以便创建原始菜单项,然后使用 +{@link +android.view.Menu#add(int,int,int,int) menu.add()} 添加新菜单项。此外,您还可以替代各个菜单项的超类行为。 +

    + + +

    在运行时更改菜单项

    + +

    系统调用 +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 后,将保留您填充的 {@link android.view.Menu} 实例。除非菜单由于某些原因而失效,否则不会再次调用 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}。但是,您仅应使用 +{@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} 来创建初始菜单状态,而不应使用它在 Activity 生命周期中执行任何更改。

    + +

    如需根据在 Activity 生命周期中发生的事件修改选项菜单,则可通过 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} +方法执行此操作。此方法向您传递 +{@link android.view.Menu} +对象(因为该对象目前存在),以便您能够对其进行修改,如添加、删除或禁用项目。(此外,片段还提供 {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()} 回调。)

    + +

    在 +Android 2.3.x 及更低版本中,每当用户打开选项菜单时(按“菜单”按钮),系统均会调用 {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()}。

    + +

    在 +Android 3.0 及更高版本中,当菜单项显示在操作栏中时,选项菜单被视为始终处于打开状态。发生事件时,如果您要执行菜单更新,则必须调用 +{@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()} 来请求系统调用 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}。

    + +

    注:切勿根据目前处于焦点的 +{@link android.view.View} +更改选项菜单中的项目。处于触摸模式(用户未使用轨迹球或方向键)时,视图无法形成焦点,因此切勿根据焦点修改选项菜单中的项目。 + +若要为 {@link +android.view.View} 提供上下文相关的菜单项,请使用上下文菜单

    + + + + +

    创建上下文菜单

    + +
    + +

    图 3. 浮动上下文菜单(左)和上下文操作栏(右)的屏幕截图。 +

    +
    + +

    上下文菜单提供了许多操作,这些操作影响 UI 中的特定项目或上下文框架。您可以为任何视图提供上下文菜单,但这些菜单通常用于 +{@link +android.widget.ListView}、{@link android.widget.GridView} +或用户可直接操作每个项目的其他视图集合中的项目。

    + +

    提供上下文操作的方法有两种:

    +
      +
    • 使用浮动上下文菜单。用户长按(按住)一个声明支持上下文菜单的视图时,菜单显示为菜单项的浮动列表(类似于对话框)。 + +用户一次可对一个项目执行上下文操作。 +
    • + +
    • 使用上下文操作模式。此模式是 +{@link android.view.ActionMode} +的系统实现,它将在屏幕顶部显示上下文操作栏,其中包括影响所选项的操作项目。当此模式处于活动状态时,用户可以同时对多项执行操作(如果应用允许)。 +
    • +
    + +

    注:上下文操作模式可用于 +Android 3.0(API +级别 11)及更高版本的系统,是显示上下文操作(如果可用)的首选方法。如果应用支持低于 +3.0 版本的系统,则应在这些设备上回退到浮动上下文菜单。

    + + +

    创建浮动上下文菜单

    + +

    要提供浮动上下文菜单,请执行以下操作:

    +
      +
    1. 通过调用 +{@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()},注册应与上下文菜单关联的 +{@link android.view.View} 并将其传递给 {@link android.view.View}。 +

      如果 Activity 使用 {@link android.widget.ListView} 或 {@link android.widget.GridView} +且您希望每个项目均提供相同的上下文菜单,请通过将 +{@link android.widget.ListView} 或 {@link android.widget.GridView} 传递给 {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()},为上下文菜单注册所有项目。

      +
    2. + +
    3. 在 {@link android.app.Activity} 或 {@link android.app.Fragment} 中实现 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +方法。 +

      当注册后的视图收到长按事件时,系统将调用您的 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} +方法。在此方法中,您通常可通过扩充菜单资源来定义菜单项。例如: +

      +
      +@Override
      +public void onCreateContextMenu(ContextMenu menu, View v,
      +                                ContextMenuInfo menuInfo) {
      +    super.onCreateContextMenu(menu, v, menuInfo);
      +    MenuInflater inflater = getMenuInflater();
      +    inflater.inflate(R.menu.context_menu, menu);
      +}
      +
      + +

      {@link android.view.MenuInflater} 允许您通过菜单资源扩充上下文菜单。回调方法参数包括用户所选的 +{@link android.view.View},以及一个提供有关所选项的附加信息的 +{@link android.view.ContextMenu.ContextMenuInfo} +对象。如果 Activity 有多个视图,每个视图均提供不同的上下文菜单,则可使用这些参数确定要扩充的上下文菜单。 + +

      +
    4. + +
    5. 实现 {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}。 +

      用户选择菜单项时,系统将调用此方法,以便您能够执行适当的操作。 +例如:

      + +
      +@Override
      +public boolean onContextItemSelected(MenuItem item) {
      +    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
      +    switch (item.getItemId()) {
      +        case R.id.edit:
      +            editNote(info.id);
      +            return true;
      +        case R.id.delete:
      +            deleteNote(info.id);
      +            return true;
      +        default:
      +            return super.onContextItemSelected(item);
      +    }
      +}
      +
      + +

      {@link android.view.MenuItem#getItemId()} +方法将查询所选菜单项的 ID,您应使用 {@code +android:id} 属性将此 ID 分配给 XML 中的每个菜单项,如使用 +XML 定义菜单部分所示。

      + +

      成功处理菜单项后,系统将返回 {@code true}。如果未处理菜单项,则应将菜单项传递给超类实现。 +如果 Activity 包括片段,则 Activity 将先收到此回调。 +通过在未处理的情况下调用超类,系统将事件逐一传递给每个片段中相应的回调方法(按照每个片段的添加顺序),直到返回 +{@code true} 或 {@code false} +为止。({@link android.app.Activity} +和 {@code android.app.Fragment} 的默认实现返回 {@code +false},因此您始终应在未处理的情况下调用超类。)

      +
    6. +
    + + +

    使用上下文操作模式

    + +

    上下文操作模式是 +{@link android.view.ActionMode} 的一种系统实现,它将用户交互的重点转到执行上下文操作上。用户通过选择项目启用此模式时,屏幕顶部将出现一个“上下文操作栏”,显示用户可对当前所选项执行的操作。 + +启用此模式后,用户可以选择多个项目(若您允许)、取消选择项目以及继续在 Activity 内导航(在您允许的最大范围内)。 + +当用户取消选择所有项目、按“返回”按钮或选择操作栏左侧的“完成”操作时,该操作模式将会禁用,且上下文操作栏将会消失。 + +

    + +

    注:上下文操作栏不一定与操作栏相关联。 +尽管表面上看来上下文操作栏取代了操作栏的位置,但事实上二者独立运行。 + +

    + +

    如果您的应用是针对 +Android 3.0(API 级别 11)或更高版本的系统而开发,则通常应使用上下文操作模式(而不是浮动上下文菜单)显示上下文操作。

    + +

    对于提供上下文操作的视图,当出现以下两个事件(或之一)时,您通常应调用上下文操作模式: +

    +
      +
    • 用户长按视图。
    • +
    • 用户选中复选框或视图内的类似 UI 组件。
    • +
    + +

    应用如何调用上下文操作模式以及如何定义每个操作的行为,具体取决于您的设计。 +设计基本上分为两种:

    +
      +
    • 针对单个任意视图的上下文操作。
    • +
    • 针对 {@link +android.widget.ListView} 或 {@link android.widget.GridView} +中项目组的批处理上下文操作(允许用户选择多个项目并针对所有项目执行操作)。
    • +
    + +

    下文介绍每种场景所需的设置。

    + + +

    为单个视图启用上下文操作模式

    + +

    如果希望仅当用户选择特定视图时才调用上下文操作模式,则应: +

    +
      +
    1. 实现 {@link android.view.ActionMode.Callback} 接口。在其回调方法中,您既可以为上下文操作栏指定操作,又可以响应操作项目的点击事件,还可以处理操作模式的其他生命周期事件。 + +
    2. +
    3. 当需要显示操作栏时(例如,用户长按视图),请调用 +{@link android.app.Activity#startActionMode startActionMode()}。
    4. +
    + +

    例如:

    + +
      +
    1. 实现 {@link android.view.ActionMode.Callback ActionMode.Callback} 接口: +
      +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
      +
      +    // Called when the action mode is created; startActionMode() was called
      +    @Override
      +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
      +        // Inflate a menu resource providing context menu items
      +        MenuInflater inflater = mode.getMenuInflater();
      +        inflater.inflate(R.menu.context_menu, menu);
      +        return true;
      +    }
      +
      +    // Called each time the action mode is shown. Always called after onCreateActionMode, but
      +    // may be called multiple times if the mode is invalidated.
      +    @Override
      +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      +        return false; // Return false if nothing is done
      +    }
      +
      +    // Called when the user selects a contextual menu item
      +    @Override
      +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
      +        switch (item.getItemId()) {
      +            case R.id.menu_share:
      +                shareCurrentItem();
      +                mode.finish(); // Action picked, so close the CAB
      +                return true;
      +            default:
      +                return false;
      +        }
      +    }
      +
      +    // Called when the user exits the action mode
      +    @Override
      +    public void onDestroyActionMode(ActionMode mode) {
      +        mActionMode = null;
      +    }
      +};
      +
      + +

      请注意,这些事件回调与选项菜单的回调几乎完全相同,只是其中每个回调还会传递与事件相关联的 {@link +android.view.ActionMode} 对象。您可以使用 +{@link +android.view.ActionMode} API 对 CAB 进行各种更改,例如:使用 {@link android.view.ActionMode#setTitle setTitle()} +和 {@link +android.view.ActionMode#setSubtitle setSubtitle()}(这对指示要选择多少个项目非常有用)修改标题和副标题。

      + +

      另请注意,操作模式被销毁时,上述示例会将 {@code mActionMode} +变量设置为 null。在下一步中,您将了解如何初始化该变量,以及保存 Activity 或片段中的成员变量有何作用。 +

      +
    2. + +
    3. 调用 {@link android.app.Activity#startActionMode startActionMode()} +以便适时启用上下文操作模式,例如:响应对 {@link +android.view.View} 的长按操作:

      + +
      +someView.setOnLongClickListener(new View.OnLongClickListener() {
      +    // Called when the user long-clicks on someView
      +    public boolean onLongClick(View view) {
      +        if (mActionMode != null) {
      +            return false;
      +        }
      +
      +        // Start the CAB using the ActionMode.Callback defined above
      +        mActionMode = getActivity().startActionMode(mActionModeCallback);
      +        view.setSelected(true);
      +        return true;
      +    }
      +});
      +
      + +

      当您调用 {@link android.app.Activity#startActionMode startActionMode()} 时,系统将返回已创建的 +{@link android.view.ActionMode}。通过将其保存在成员变量中,您可以更改上下文操作栏来响应其他事件。 +在上述示例中, +{@link android.view.ActionMode} +用于在启动操作模式之前检查成员是否为空,以确保当 {@link android.view.ActionMode} +实例已激活时不再重建该实例。

      +
    4. +
    + + + +

    在 ListView 或 GridView 中启用批处理上下文操作

    + +

    如果您在 {@link android.widget.ListView} 或 {@link +android.widget.GridView} 中有一组项目(或 {@link android.widget.AbsListView} +的其他扩展),且需要允许用户执行批处理操作,则应:

    + +
      +
    • 实现 +{@link android.widget.AbsListView.MultiChoiceModeListener} 接口,并使用 {@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()} 为视图组设置该接口。在侦听器的回调方法中,您既可以为上下文操作栏指定操作,也可以响应操作项目的点击事件,还可以处理从 +{@link android.view.ActionMode.Callback} +接口继承的其他回调。
    • + +
    • 使用 {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL} 参数调用 {@link android.widget.AbsListView#setChoiceMode setChoiceMode()}。
    • +
    + +

    例如:

    + +
    +ListView listView = getListView();
    +listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    +listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
    +
    +    @Override
    +    public void onItemCheckedStateChanged(ActionMode mode, int position,
    +                                          long id, boolean checked) {
    +        // Here you can do something when items are selected/de-selected,
    +        // such as update the title in the CAB
    +    }
    +
    +    @Override
    +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    +        // Respond to clicks on the actions in the CAB
    +        switch (item.getItemId()) {
    +            case R.id.menu_delete:
    +                deleteSelectedItems();
    +                mode.finish(); // Action picked, so close the CAB
    +                return true;
    +            default:
    +                return false;
    +        }
    +    }
    +
    +    @Override
    +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    +        // Inflate the menu for the CAB
    +        MenuInflater inflater = mode.getMenuInflater();
    +        inflater.inflate(R.menu.context, menu);
    +        return true;
    +    }
    +
    +    @Override
    +    public void onDestroyActionMode(ActionMode mode) {
    +        // Here you can make any necessary updates to the activity when
    +        // the CAB is removed. By default, selected items are deselected/unchecked.
    +    }
    +
    +    @Override
    +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    +        // Here you can perform updates to the CAB due to
    +        // an {@link android.view.ActionMode#invalidate} request
    +        return false;
    +    }
    +});
    +
    + +

    就这么简单。现在,当用户通过长按选择项目时,系统即会调用 {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +方法,并显示包含指定操作的上下文操作栏。当上下文操作栏可见时,用户可以选择其他项目。 +

    + +

    在某些情况下,如果上下文操作提供常用的操作项目,则您可能需要添加一个复选框或类似的 +UI +元素来支持用户选择项目,这是因为他们可能没有发现长按行为。用户选中该复选框时,您可以通过使用 +{@link android.widget.AbsListView#setItemChecked setItemChecked()} +将相应的列表项设置为选中状态,以此调用上下文操作模式。

    + + + + +

    创建弹出菜单

    + +
    + +

    图 4. Gmail 应用中的弹出菜单,锚定到右上角的溢出按钮。 +

    +
    + +

    {@link android.widget.PopupMenu} +是锚定到 {@link android.view.View} 的模态菜单。如果空间足够,它将显示在定位视图下方,否则显示在其上方。它适用于:

    +
      +
    • 为与特定内容确切相关的操作提供溢出样式菜单(例如,Gmail +的电子邮件标头,如图 4 所示)。 +

      注:这与上下文菜单不同,后者通常用于影响所选内容的操作。 +对于影响所选内容的操作,请使用上下文操作模式浮动上下文菜单。 +

    • +
    • 提供命令语句的另一部分(例如,标记为“添加”且使用不同的“添加”选项生成弹出菜单的按钮)。 +
    • +
    • 提供类似于 +{@link android.widget.Spinner} 且不保留永久选择的下拉菜单。
    • +
    + + +

    注:{@link android.widget.PopupMenu} 在 API +级别 11 及更高版本中可用。

    + +

    如果使用 XML 定义菜单,则显示弹出菜单的方法如下:

    +
      +
    1. 实例化 +{@link android.widget.PopupMenu} 及其构造函数,该函数将提取当前应用的 {@link android.content.Context} 以及菜单应锚定到的 +{@link android.view.View}。
    2. +
    3. 使用 {@link android.view.MenuInflater} 将菜单资源扩充到 {@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()} 返回的 {@link +android.view.Menu} 对象中。在 API 级别 14 及更高版本中,您可以改为使用 +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()}。
    4. +
    5. 调用 {@link android.widget.PopupMenu#show() PopupMenu.show()}。
    6. +
    + +

    例如,以下是一个使用 +{@link android.R.attr#onClick android:onClick} 属性显示弹出菜单的按钮:

    + +
    +<ImageButton
    +    android:layout_width="wrap_content" 
    +    android:layout_height="wrap_content" 
    +    android:src="@drawable/ic_overflow_holo_dark"
    +    android:contentDescription="@string/descr_overflow_button"
    +    android:onClick="showPopup" />
    +
    + +

    稍后,Activity 可按照如下方式显示弹出菜单:

    + +
    +public void showPopup(View v) {
    +    PopupMenu popup = new PopupMenu(this, v);
    +    MenuInflater inflater = popup.getMenuInflater();
    +    inflater.inflate(R.menu.actions, popup.getMenu());
    +    popup.show();
    +}
    +
    + +

    在 API 级别 14 及更高版本中,您可以将两行合在一起,使用 {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()} 扩充菜单。

    + +

    当用户选择项目或触摸菜单以外的区域时,系统即会清除此菜单。 +您可使用 {@link +android.widget.PopupMenu.OnDismissListener} 侦听清除事件。

    + +

    处理点击事件

    + +

    要在用户选择菜单项时执行操作,您必须实现 +{@link +android.widget.PopupMenu.OnMenuItemClickListener} 接口,并通过调用 {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()} 将其注册到 {@link +android.widget.PopupMenu}。用户选择项目时,系统会在接口中调用 {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} +回调。

    + +

    例如:

    + +
    +public void showMenu(View v) {
    +    PopupMenu popup = new PopupMenu(this, v);
    +
    +    // This activity implements OnMenuItemClickListener
    +    popup.setOnMenuItemClickListener(this);
    +    popup.inflate(R.menu.actions);
    +    popup.show();
    +}
    +
    +@Override
    +public boolean onMenuItemClick(MenuItem item) {
    +    switch (item.getItemId()) {
    +        case R.id.archive:
    +            archive(item);
    +            return true;
    +        case R.id.delete:
    +            delete(item);
    +            return true;
    +        default:
    +            return false;
    +    }
    +}
    +
    + + +

    创建菜单组

    + +

    菜单组是指一系列具有某些共同特征的菜单项。通过菜单组,您可以: +

    +
      +
    • 使用 {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()} 显示或隐藏所有项目
    • +
    • 使用 {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()} 启用或禁用所有项目
    • +
    • 使用 {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()} 指定所有项目是否可选中
    • +
    + +

    通过将 {@code <item>} 元素嵌套在菜单资源中的 {@code <group>} +元素内,或者通过使用 {@link +android.view.Menu#add(int,int,int,int) add()} 方法指定组 ID,您可以创建组。

    + +

    以下是包含组的菜单资源示例:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <item android:id="@+id/menu_save"
    +          android:icon="@drawable/menu_save"
    +          android:title="@string/menu_save" />
    +    <!-- menu group -->
    +    <group android:id="@+id/group_delete">
    +        <item android:id="@+id/menu_archive"
    +              android:title="@string/menu_archive" />
    +        <item android:id="@+id/menu_delete"
    +              android:title="@string/menu_delete" />
    +    </group>
    +</menu>
    +
    + +

    组中的项目出现在与第一项相同的级别,即:菜单中的所有三项均为同级。 +但是,您可以通过引用组 ID +并使用上面列出的方法,修改组中两项的特征。此外,系统也绝不会分离已分组的项目。 +例如,如果为每个项目声明 +{@code +android:showAsAction="ifRoom"},则它们会同时显示在操作栏或操作溢出菜单中。

    + + +

    使用可选中的菜单项

    + +
    + +

    图 5. 含可选中项目的子菜单的屏幕截图。 +

    +
    + +

    作为启用/禁用选项的接口,菜单非常实用,既可针对独立选项使用复选框,也可针对互斥选项组使用单选按钮。 + +图 5 显示了一个子菜单,其中的项目可使用单选按钮选中。 +

    + +

    注:“图标菜单”(在选项菜单中)的菜单项无法显示复选框或单选按钮。 +如果您选择使“图标菜单”中的项目可选中,则必须在选中状态每次发生变化时交换图标和/或文本,手动指出该状态。 + +

    + +

    您可以使用 {@code <item>} 元素中的 {@code +android:checkable} 属性为各个菜单项定义可选中的行为,或者使用 {@code <group>} 元素中的 +{@code android:checkableBehavior} 属性为整个组定义可选中的行为。例如,此菜单组中的所有项目均可使用单选按钮选中: +

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<menu xmlns:android="http://schemas.android.com/apk/res/android">
    +    <group android:checkableBehavior="single">
    +        <item android:id="@+id/red"
    +              android:title="@string/red" />
    +        <item android:id="@+id/blue"
    +              android:title="@string/blue" />
    +    </group>
    +</menu>
    +
    + +

    {@code android:checkableBehavior} 属性接受以下任一选项: +

    +
    {@code single}
    +
    组中只有一个项目可以选中(单选按钮)
    +
    {@code all}
    +
    所有项目均可选中(复选框)
    +
    {@code none}
    +
    所有项目均无法选中
    +
    + +

    您可以使用 {@code <item>} +元素中的 {@code android:checked} 属性将默认的选中状态应用于项目,并可使用 {@link +android.view.MenuItem#setChecked(boolean) setChecked()} 方法在代码中更改此默认状态。

    + +

    选择可选中项目后,系统将调用所选项目的相应回调方法(例如,{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()})。 +此时,您必须设置复选框的状态,因为复选框或单选按钮不会自动更改其状态。 + +您可以使用 {@link android.view.MenuItem#isChecked()} +查询项目的当前状态(正如用户选择该项目之前一样),然后使用 +{@link android.view.MenuItem#setChecked(boolean) setChecked()} 设置选中状态。例如:

    + +
    +@Override
    +public boolean onOptionsItemSelected(MenuItem item) {
    +    switch (item.getItemId()) {
    +        case R.id.vibrate:
    +        case R.id.dont_vibrate:
    +            if (item.isChecked()) item.setChecked(false);
    +            else item.setChecked(true);
    +            return true;
    +        default:
    +            return super.onOptionsItemSelected(item);
    +    }
    +}
    +
    + +

    如果未通过这种方式设置选中状态,则项目的可见状态(复选框或单选按钮)不会因为用户选择它而发生变化。 + +如果已设置该状态,则 Activity 会保留项目的选中状态。这样一来,当用户稍后打开菜单时,您设置的选中状态将会可见。 + +

    + +

    注:可选中菜单项的使用往往因会话而异,且在应用销毁后不予保存。 + +如果您想为用户保存某些应用设置,则应使用共享首选项存储数据。 +

    + + + +

    添加基于 Intent 的菜单项

    + +

    有时,您希望菜单项通过使用 +{@link android.content.Intent} 启动 Activity(无论该 Activity 是位于您的应用还是其他应用中)。如果您知道自己要使用的 Intent,且具有启动 Intent 的特定菜单项,则可在相应的 +on-item-selected 回调方法(例如,{@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} 回调)期间使用 +{@link android.app.Activity#startActivity(Intent) startActivity()} +执行 Intent。

    + +

    但是,如果不确定用户的设备是否包含可处理 Intent 的应用,则添加调用 Intent 的菜单项可能会导致该菜单项无法正常工作,这是因为 Intent 可能无法解析为 Activity。 + + +为了解决这一问题,当 +Android 在设备上找到可处理 Intent 的 Activity 时,则允许您向菜单动态添加菜单项。

    + +

    要根据接受 Intent 的可用 Activity 添加菜单项,请执行以下操作:

    +
      +
    1. 使用类别 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} 和/或 +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 以及任何其他要求定义 Intent。
    2. +
    3. 调用 {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}。Android +随后即会搜索能够执行 Intent 的所有应用,并将其添加到菜单中。
    4. +
    + +

    如果未安装可处理 Intent 的应用,则不会添加任何菜单项。 +

    + +

    注: +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} +用于处理屏幕上当前所选的元素。因此,只有在 {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()} 中创建菜单时,才能使用它。

    + +

    例如:

    + +
    +@Override
    +public boolean onCreateOptionsMenu(Menu menu){
    +    super.onCreateOptionsMenu(menu);
    +
    +    // Create an Intent that describes the requirements to fulfill, to be included
    +    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
    +    Intent intent = new Intent(null, dataUri);
    +    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
    +
    +    // Search and populate the menu with acceptable offering applications.
    +    menu.addIntentOptions(
    +         R.id.intent_group,  // Menu group to which new items will be added
    +         0,      // Unique item ID (none)
    +         0,      // Order for the items (none)
    +         this.getComponentName(),   // The current activity name
    +         null,   // Specific items to place first (none)
    +         intent, // Intent created above that describes our requirements
    +         0,      // Additional flags to control items (none)
    +         null);  // Array of MenuItems that correlate to specific items (none)
    +
    +    return true;
    +}
    + +

    如果发现 Activity 提供的 Intent 过滤器与定义的 Intent 匹配,则会添加菜单项,并使用 Intent 过滤器 +android:label +中的值作为菜单项标题,使用应用图标作为菜单项图标。{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} +方法将返回已添加的菜单项数量。

    + +

    注:调用 +{@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} 方法时,它将使用第一个参数中指定的菜单组替代所有菜单项。

    + + +

    允许将 Activity 添加到其他菜单中

    + +

    此外,您还可以为其他应用提供您的 Activity 服务,以便您的应用能够包含在其他应用的菜单中(与上述角色相反)。 +

    + +

    要包含在其他应用菜单中,您需要按常规方式定义 Intent 过滤器,但请确保为 Intent 过滤器类别添加 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} +和/或 {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} +值。例如:

    +
    +<intent-filter label="@string/resize_image">
    +    ...
    +    <category android:name="android.intent.category.ALTERNATIVE" />
    +    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    +    ...
    +</intent-filter>
    +
    + +

    请仔细阅读 Intent 和 Intent 过滤器文档中更多有关编写 Intent 过滤器的内容。 +

    + +

    有关使用此方法的应用示例,请参阅记事本示例代码。 + +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 0000000000000000000000000000000000000000..c0bd74cdd3ac3cc3f8533d335b96aa401e7edde8 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=通知 +@jd:body + + +

    + 通知是您可以在应用的常规 +UI +外部向用户显示的消息。当您告知系统发出通知时,它将先以图标的形式显示在通知区域中。用户可以打开抽屉式通知栏查看通知的详细信息。 +通知区域和抽屉式通知栏均是由系统控制的区域,用户可以随时查看。 + +

    + +

    + 图 1. 通知区域中的通知。 +

    + +

    + 图 2. 抽屉式通知栏中的通知。 +

    + +

    注:除非特别注明,否则本指南均引用版本 +4 支持库中的 {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +类。Android +3.0(API 级别 11)中已添加类 +{@link android.app.Notification.Builder Notification.Builder}。

    + +

    设计注意事项

    + +

    作为 Android 用户界面的一个重要组成部分,通知具有自己的设计指导方针。Android +5.0(API 级别 21)中引入的材料设计变更尤为重要,您应查阅材料设计培训资料了解详细信息。 + +要了解如何设计通知及其交互,请阅读通知设计指南。 +

    + +

    创建通知

    + +

    您可以在 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +对象中为通知指定 UI 信息和操作。要创建通知,请调用 +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()},它将返回包含您的具体规范的 +{@link android.app.Notification} 对象。要发出通知,请通过调用 +{@link android.app.NotificationManager#notify NotificationManager.notify()} 将 {@link android.app.Notification} +对象传递给系统。

    + +

    必需的通知内容

    +

    + {@link android.app.Notification} 对象必须包含以下内容: +

    +
      +
    • + 小图标,由 +{@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} 设置 +
    • +
    • + 标题,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} 设置 +
    • +
    • + 详细文本,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} 设置 +
    • +
    +

    可选通知内容和设置

    +

    + 所有其他通知设置和内容都是可选的。如需了解有关它们的更多详情,请参阅 +{@link android.support.v4.app.NotificationCompat.Builder} 参考文档。 +

    + +

    通知操作

    +

    + 尽管通知操作都是可选的,但是您至少应向通知添加一个操作。 + 操作允许用户直接从通知转到应用中的 +{@link android.app.Activity},他们可在其中查看一个或多个事件或执行进一步的操作。 + +

    +

    + 一个通知可以提供多个操作。您应该始终定义一个当用户点击通知时会触发的操作;通常,此操作会在应用中打开 +{@link android.app.Activity}。 +您也可以向通知添加按钮来执行其他操作,例如,暂停闹铃或立即答复短信;此功能自 +Android +4.1 起可用。如果使用其他操作按钮,则您还必须使这些按钮的功能在应用的 +{@link android.app.Activity} +中可用;请参阅处理兼容性部分,以了解更多详情。 +

    +

    + 在 {@link android.app.Notification} 内部,操作本身由 +{@link android.app.PendingIntent} 定义,后者包含在应用中启动 +{@link android.app.Activity} +的 {@link android.content.Intent}。要将 +{@link android.app.PendingIntent} 与手势相关联,请调用 +{@link android.support.v4.app.NotificationCompat.Builder} 的适当方法。例如,如果您要在用户点击抽屉式通知栏中的通知文本时启动 +{@link android.app.Activity},则可通过调用 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()} +来添加 {@link android.app.PendingIntent}。 +

    +

    + 在用户点击通知时启动 {@link android.app.Activity} +是最常见的操作场景。此外,您还可以在用户清除通知时启动 +{@link android.app.Activity}。在 Android 4.1 及更高版本中,您可以通过操作按钮启动 +{@link android.app.Activity}。如需了解更多信息,请阅读参考指南的 +{@link android.support.v4.app.NotificationCompat.Builder} 部分。 +

    + +

    通知优先级

    +

    + 您可以根据需要设置通知的优先级。优先级充当一个提示,提醒设备 +UI 应该如何显示通知。 + 要设置通知的优先级,请调用 {@link + android.support.v4.app.NotificationCompat.Builder#setPriority(int) + NotificationCompat.Builder.setPriority()} 并传入一个 {@link + android.support.v4.app.NotificationCompat} 优先级常量。有五个优先级别,范围从 {@link + android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) 到 {@link + android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2);如果未设置,则优先级默认为 {@link + android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0)。 + + +

    +

    有关设置适当优先级别的信息,请参阅通知设计指南中的“正确设置和管理通知优先级”。 + + +

    + +

    创建简单通知

    +

    + 以下代码段说明了一个指定某项 Activity 在用户点击通知时打开的简单通知。 +请注意,该代码将创建 +{@link android.support.v4.app.TaskStackBuilder} 对象并使用它来为操作创建 +{@link android.app.PendingIntent}。启动 Activity 时保留导航部分对此模式做了更详尽的阐述: + + +

    +
    +NotificationCompat.Builder mBuilder =
    +        new NotificationCompat.Builder(this)
    +        .setSmallIcon(R.drawable.notification_icon)
    +        .setContentTitle("My notification")
    +        .setContentText("Hello World!");
    +// Creates an explicit intent for an Activity in your app
    +Intent resultIntent = new Intent(this, ResultActivity.class);
    +
    +// The stack builder object will contain an artificial back stack for the
    +// started Activity.
    +// This ensures that navigating backward from the Activity leads out of
    +// your application to the Home screen.
    +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    +// Adds the back stack for the Intent (but not the Intent itself)
    +stackBuilder.addParentStack(ResultActivity.class);
    +// Adds the Intent that starts the Activity to the top of the stack
    +stackBuilder.addNextIntent(resultIntent);
    +PendingIntent resultPendingIntent =
    +        stackBuilder.getPendingIntent(
    +            0,
    +            PendingIntent.FLAG_UPDATE_CURRENT
    +        );
    +mBuilder.setContentIntent(resultPendingIntent);
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// mId allows you to update the notification later on.
    +mNotificationManager.notify(mId, mBuilder.build());
    +
    +

    就这么简单。您的用户现已收到通知。

    + +

    将扩展布局应用于通知

    +

    + 要使通知出现在展开视图中,请先创建一个带有所需普通视图选项的 +{@link android.support.v4.app.NotificationCompat.Builder} +对象。接下来,调用以扩展布局对象作为其参数的 {@link android.support.v4.app.NotificationCompat.Builder#setStyle + Builder.setStyle()}。 +

    +

    + 请记住,扩展通知在 Android 4.1 之前的平台上不可用。要了解如何处理针对 +Android 4.1 及更早版本平台的通知,请阅读处理兼容性部分。 + +

    +

    + 例如,以下代码段演示了如何更改在前面的代码段中创建的通知,以便使用扩展布局: + +

    +
    +NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
    +    .setSmallIcon(R.drawable.notification_icon)
    +    .setContentTitle("Event tracker")
    +    .setContentText("Events received")
    +NotificationCompat.InboxStyle inboxStyle =
    +        new NotificationCompat.InboxStyle();
    +String[] events = new String[6];
    +// Sets a title for the Inbox in expanded layout
    +inboxStyle.setBigContentTitle("Event tracker details:");
    +...
    +// Moves events into the expanded layout
    +for (int i=0; i < events.length; i++) {
    +
    +    inboxStyle.addLine(events[i]);
    +}
    +// Moves the expanded layout object into the notification object.
    +mBuilder.setStyle(inBoxStyle);
    +...
    +// Issue the notification here.
    +
    + +

    处理兼容性

    + +

    + 并非所有通知功能都可用于某特定版本,即便用于设置这些功能的方法位于支持库类 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} +中也是如此。 + 例如,依赖于扩展通知的操作按钮仅会显示在 Android +4.1 及更高版本的系统中,这是因为扩展通知本身仅在 +Android 4.1 及更高版本的系统中可用。 +

    +

    + 为了确保最佳兼容性,请使用 +{@link android.support.v4.app.NotificationCompat NotificationCompat} 及其子类(特别是 +{@link android.support.v4.app.NotificationCompat.Builder + NotificationCompat.Builder})创建通知。此外,在实现通知时,请遵循以下流程: +

    +
      +
    1. + 为所有用户提供通知的全部功能,无论他们使用何种版本的 Android 系统。 +为此,请验证是否可从应用的 +{@link android.app.Activity} 中获得所有功能。要执行此操作,您可能需要添加新的 +{@link android.app.Activity}。 +

      + 例如,若要使用 +{@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} 提供停止和启动媒体播放的控件,请先在应用的 +{@link android.app.Activity} +中实现此控件。 +

      +
    2. +
    3. + 确保所有用户均可通过点击通知启动 {@link android.app.Activity} 来获得该Activity中的功能。 +为此,请为 +{@link android.app.Activity} +创建 {@link android.app.PendingIntent}。调用 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} 以将 {@link android.app.PendingIntent} 添加到通知。 +
    4. +
    5. + 现在,将要使用的扩展通知功能添加到通知。请记住,您添加的任何功能还必须在用户点击通知时启动的 +{@link android.app.Activity} +中可用。 +
    6. +
    + + + + +

    管理通知

    +

    + 当您需要为同一类型的事件多次发出同一通知时,应避免创建全新的通知, +而是应考虑通过更改之前通知的某些值和/或为其添加某些值来更新通知。 + +

    +

    + 例如,Gmail 通过增加未读消息计数并将每封电子邮件的摘要添加到通知,通知用户收到了新的电子邮件。 +这称为“堆叠”通知;通知设计指南对此进行了更详尽的描述。 + + +

    +

    + 注:此 +Gmail 功能需要“收件箱”扩展布局,该布局是自 Android 4.1 版本起可用的扩展通知功能的一部分。 +

    +

    + 下文介绍如何更新和删除通知。 +

    +

    更新通知

    +

    + 要将通知设置为能够更新,请通过调用 +{@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()} 发出带有通知 ID 的通知。 + 要在发出之后更新此通知,请更新或创建 +{@link android.support.v4.app.NotificationCompat.Builder} +对象,从该对象构建 {@link android.app.Notification} 对象,并发出与之前所用 ID 相同的 +{@link android.app.Notification}。如果之前的通知仍然可见,则系统会根据 +{@link android.app.Notification} +对象的内容更新该通知。相反,如果之前的通知已被清除,系统则会创建一个新通知。 + +

    +

    + 以下代码段演示了经过更新以反映所发生事件数量的通知。 +它将通知堆叠并显示摘要: +

    +
    +mNotificationManager =
    +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +// Sets an ID for the notification, so it can be updated
    +int notifyID = 1;
    +mNotifyBuilder = new NotificationCompat.Builder(this)
    +    .setContentTitle("New Message")
    +    .setContentText("You've received new messages.")
    +    .setSmallIcon(R.drawable.ic_notify_status)
    +numMessages = 0;
    +// Start of a loop that processes data and then notifies the user
    +...
    +    mNotifyBuilder.setContentText(currentText)
    +        .setNumber(++numMessages);
    +    // Because the ID remains unchanged, the existing notification is
    +    // updated.
    +    mNotificationManager.notify(
    +            notifyID,
    +            mNotifyBuilder.build());
    +...
    +
    + + +

    删除通知

    +

    + 除非发生以下情况之一,否则通知仍然可见: +

    +
      +
    • + 用户单独或通过使用“全部清除”清除了该通知(如果通知可以清除)。 + +
    • +
    • + 用户点击通知,且您在创建通知时调用了 +{@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()}。 + +
    • +
    • + 您针对特定的通知 ID 调用了 +{@link android.app.NotificationManager#cancel(int) cancel()}。此方法还会删除当前通知。 +
    • +
    • + 您调用了 +{@link android.app.NotificationManager#cancelAll() cancelAll()} 方法,该方法将删除之前发出的所有通知。 +
    • +
    + + +

    启动 Activity 时保留导航

    +

    + 从通知中启动 +{@link android.app.Activity} 时,您必须保留用户的预期导航体验。 点击“返回”应该使用户将应用的正常工作流回退到主屏幕,而点击“最新动态”则应将 + {@link android.app.Activity} 显示为单独的任务。 +要保留导航体验,您应该在全新任务中启动 +{@link android.app.Activity}。如何设置 +{@link android.app.PendingIntent} 以获得全新任务取决于正在启动的 +{@link android.app.Activity} 的性质。一般有两种情况: +

    +
    +
    + 常规 Activity +
    +
    + 您要启动的 +{@link android.app.Activity} 是应用的正常工作流的一部分。在这种情况下,请设置 {@link android.app.PendingIntent} +以启动全新任务并为 +{@link android.app.PendingIntent}提供返回栈,这将重现应用的正常“返回”行为。 +

    + Gmail 应用中的通知演示了这一点。点击一封电子邮件消息的通知时,您将看到消息具体内容。 +触摸返回将使您从 +Gmail 回退到主屏幕,就好像您是从主屏幕(而不是通知)进入 +Gmail 一样。 +

    +

    + 无论您触摸通知时处于哪个应用,都会发生这种情况。 +例如,如果您在 +Gmail 中撰写消息时点击了一封电子邮件的通知,则会立即转到该电子邮件。 + 触摸“返回”会依次转到收件箱和主屏幕,而不是转到您在撰写的邮件。 + +

    +
    +
    + 特殊 Activity +
    +
    + 仅当从通知中启动时,用户才会看到此 {@link android.app.Activity}。 + 从某种意义上说,{@link android.app.Activity} +是通过提供很难显示在通知本身中的信息来扩展通知。对于这种情况,请将 +{@link android.app.PendingIntent} 设置为在全新任务中启动。但是,由于启动的 +{@link android.app.Activity} +不是应用 Activity 流程的一部分,因此无需创建返回栈。点击“返回”仍会将用户带到主屏幕。 + +
    +
    + +

    设置常规 Activity PendingIntent

    +

    + 要设置可启动直接进入 {@link android.app.Activity} 的 +{@link android.app.PendingIntent},请执行以下步骤: +

    +
      +
    1. + 在清单文件中定义应用的 {@link android.app.Activity} 层次结构。 +
        +
      1. + 添加对 Android 4.0.3 及更低版本的支持。为此,请通过添加 +<meta-data> +元素作为 +<activity>的子项来指定正在启动的 +{@link android.app.Activity} 的父项。 +

        + 对于此元素,请设置 +android:name="android.support.PARENT_ACTIVITY"。 + 设置 +android:value="<parent_activity_name>",其中,<parent_activity_name> +是父 +<activity> +元素的 +android:name +值。请参阅下面的 XML 示例。 +

        +
      2. +
      3. + 同样添加对 Android 4.1 及更高版本的支持。为此,请将 +android:parentActivityName +属性添加到正在启动的 +{@link android.app.Activity} 的 +<activity> 元素中。 +
      4. +
      +

      + 最终的 XML 应如下所示: +

      +
      +<activity
      +    android:name=".MainActivity"
      +    android:label="@string/app_name" >
      +    <intent-filter>
      +        <action android:name="android.intent.action.MAIN" />
      +        <category android:name="android.intent.category.LAUNCHER" />
      +    </intent-filter>
      +</activity>
      +<activity
      +    android:name=".ResultActivity"
      +    android:parentActivityName=".MainActivity">
      +    <meta-data
      +        android:name="android.support.PARENT_ACTIVITY"
      +        android:value=".MainActivity"/>
      +</activity>
      +
      +
    2. +
    3. + 根据可启动 +{@link android.app.Activity} 的 {@link android.content.Intent} 创建返回栈: +
        +
      1. + 创建 {@link android.content.Intent} 以启动 {@link android.app.Activity}。 +
      2. +
      3. + 通过调用 {@link android.app.TaskStackBuilder#create + TaskStackBuilder.create()} 创建堆栈生成器。 +
      4. +
      5. + 通过调用 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} 将返回栈添加到堆栈生成器。 + 对于在清单文件中所定义层次结构内的每个 +{@link android.app.Activity},返回栈均包含可启动 {@link android.app.Activity} 的 +{@link android.content.Intent} 对象。此方法还会添加一些可在全新任务中启动堆栈的标志。 + +

        + 注:尽管 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} +的参数是对已启动 +{@link android.app.Activity} 的引用,但是方法调用不会添加可启动 {@link android.app.Activity} 的 +{@link android.content.Intent},而是留待下一步进行处理。 +

        +
      6. +
      7. + 通过调用 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()},添加可从通知中启动 {@link android.app.Activity} +的 {@link android.content.Intent}。 + 将在第一步中创建的 {@link android.content.Intent} +作为 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()} 的参数传递。 +
      8. +
      9. + 如需,请通过调用 {@link android.support.v4.app.TaskStackBuilder#editIntentAt + TaskStackBuilder.editIntentAt()} 向堆栈中的 {@link android.content.Intent} +对象添加参数。有时,需要确保目标 {@link android.app.Activity} 在用户使用“返回”导航回它时会显示有意义的数据。 + + +
      10. +
      11. + 通过调用 +{@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()} 获得此返回栈的 {@link android.app.PendingIntent}。 + 然后,您可以使用此 {@link android.app.PendingIntent} 作为 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} 的参数。 +
      12. +
      +
    4. +
    +

    + 以下代码段演示了该流程: +

    +
    +...
    +Intent resultIntent = new Intent(this, ResultActivity.class);
    +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    +// Adds the back stack
    +stackBuilder.addParentStack(ResultActivity.class);
    +// Adds the Intent to the top of the stack
    +stackBuilder.addNextIntent(resultIntent);
    +// Gets a PendingIntent containing the entire back stack
    +PendingIntent resultPendingIntent =
    +        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    +...
    +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    +builder.setContentIntent(resultPendingIntent);
    +NotificationManager mNotificationManager =
    +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +mNotificationManager.notify(id, builder.build());
    +
    + +

    设置特殊 Activity PendingIntent

    +

    + 下文介绍如何设置特殊 Activity +{@link android.app.PendingIntent}。 +

    +

    + 特殊 +{@link android.app.Activity} 无需返回栈,因此您不必在清单文件中定义其 +{@link android.app.Activity} 层次结构,也不必调用 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} +来构建返回栈。取而代之的是,您可使用清单文件设置 +{@link android.app.Activity} 任务选项,并通过调用 {@link android.app.PendingIntent#getActivity getActivity()} +创建 {@link android.app.PendingIntent}: +

    +
      +
    1. + 在清单文件中,将以下属性添加到 {@link android.app.Activity} 的 +<activity> +元素 +
      +
      +android:name="activityclass" +
      +
      + Activity 的完全限定类名。 +
      +
      +android:taskAffinity="" +
      +
      + 与您在代码中设置的 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} +标志相结合,这可确保此 {@link android.app.Activity} +不会进入应用的默认任务。任何具有应用默认关联的现有任务均不受影响。 + +
      +
      +android:excludeFromRecents="true" +
      +
      + 将新任务从“最新动态”中排除,这样用户就不会在无意中导航回它。 + +
      +
      +

      + 以下代码段显示了该元素: +

      +
      +<activity
      +    android:name=".ResultActivity"
      +...
      +    android:launchMode="singleTask"
      +    android:taskAffinity=""
      +    android:excludeFromRecents="true">
      +</activity>
      +...
      +
      +
    2. +
    3. + 构建并发出通知: +
        +
      1. + 创建可启动 {@link android.app.Activity}的 +{@link android.content.Intent}。 +
      2. +
      3. + 通过使用 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} +和 +{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK} 标志调用 +{@link android.content.Intent#setFlags setFlags()},将 {@link android.app.Activity} 设置为在新的空任务中启动。 +
      4. +
      5. + 为 {@link android.content.Intent} 设置所需的任何其他选项。 +
      6. +
      7. + 通过调用 +{@link android.app.PendingIntent#getActivity getActivity()} 从 {@link android.content.Intent} 中创建 {@link android.app.PendingIntent}。 + 然后,您可以使用此 {@link android.app.PendingIntent} 作为 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent + setContentIntent()} 的参数。 +
      8. +
      +

      + 以下代码段演示了该流程: +

      +
      +// Instantiate a Builder object.
      +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
      +// Creates an Intent for the Activity
      +Intent notifyIntent =
      +        new Intent(this, ResultActivity.class);
      +// Sets the Activity to start in a new, empty task
      +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
      +                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
      +// Creates the PendingIntent
      +PendingIntent notifyPendingIntent =
      +        PendingIntent.getActivity(
      +        this,
      +        0,
      +        notifyIntent,
      +        PendingIntent.FLAG_UPDATE_CURRENT
      +);
      +
      +// Puts the PendingIntent into the notification builder
      +builder.setContentIntent(notifyPendingIntent);
      +// Notifications are issued by sending them to the
      +// NotificationManager system service.
      +NotificationManager mNotificationManager =
      +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +// Builds an anonymous Notification object from the builder, and
      +// passes it to the NotificationManager
      +mNotificationManager.notify(id, builder.build());
      +
      +
    4. +
    + + +

    在通知中显示进度

    +

    + 通知可能包括动画形式的进度指示器,向用户显示正在进行的操作状态。 +如果您可以估计操作所需的时间以及任意时刻的完成进度,则使用“限定”形式的指示器(进度栏)。 + +如果无法估计操作的时长,则使用“非限定”形式的指示器(Activity 指示器)。 + +

    +

    + 平台的 +{@link android.widget.ProgressBar} 类实现中显示有进度指示器。 +

    +

    + 要在 Android 4.0 及更高版本的平台上使用进度指示器,需调用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()}。对于早期版本,您必须创建包括 {@link android.widget.ProgressBar} +视图的自定义通知布局。 + +

    +

    + 下文介绍如何使用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 在通知中显示进度。 +

    + +

    显示持续时间固定的进度指示器

    +

    + 要显示限定形式的进度栏,请通过调用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(max, progress, false)} 将进度栏添加到通知,然后发出通知。随着操作继续进行,递增 +progress 并更新通知。操作结束时, +progress 应该等于 max。调用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} +的常见方法是将 max 设置为 100,然后将 progress +作为操作的“完成百分比”值递增。 +

    +

    + 您可以在操作完成后仍保留显示进度栏,也可以将其删除。无论哪种情况,都请记住更新通知文本以显示操作已完成。 + + 要删除进度栏,请调用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress + setProgress(0, 0, false)}。例如: +

    +
    +...
    +mNotifyManager =
    +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    +mBuilder = new NotificationCompat.Builder(this);
    +mBuilder.setContentTitle("Picture Download")
    +    .setContentText("Download in progress")
    +    .setSmallIcon(R.drawable.ic_notification);
    +// Start a lengthy operation in a background thread
    +new Thread(
    +    new Runnable() {
    +        @Override
    +        public void run() {
    +            int incr;
    +            // Do the "lengthy" operation 20 times
    +            for (incr = 0; incr <= 100; incr+=5) {
    +                    // Sets the progress indicator to a max value, the
    +                    // current completion percentage, and "determinate"
    +                    // state
    +                    mBuilder.setProgress(100, incr, false);
    +                    // Displays the progress bar for the first time.
    +                    mNotifyManager.notify(0, mBuilder.build());
    +                        // Sleeps the thread, simulating an operation
    +                        // that takes time
    +                        try {
    +                            // Sleep for 5 seconds
    +                            Thread.sleep(5*1000);
    +                        } catch (InterruptedException e) {
    +                            Log.d(TAG, "sleep failure");
    +                        }
    +            }
    +            // When the loop is finished, updates the notification
    +            mBuilder.setContentText("Download complete")
    +            // Removes the progress bar
    +                    .setProgress(0,0,false);
    +            mNotifyManager.notify(ID, mBuilder.build());
    +        }
    +    }
    +// Starts the thread by calling the run() method in its Runnable
    +).start();
    +
    + + +

    显示持续 Activity 指示器

    +

    + 要显示非限定形式的 Activity 指示器,请使用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)} +将其添加到通知(忽略前两个参数),然后发出通知。这样一来,指示器的样式就与进度栏相同,只是其动画还在继续。 + +

    +

    + 在操作开始之际发出通知。除非您修改通知,否则动画将一直运行。 +操作完成后,调用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)},然后更新通知以删除 Activity 指示器。 + + 请务必这样做;否则,即使操作完成,动画仍将运行。同时,请记得更改通知文本,以表明操作已完成。 + +

    +

    + 要了解 Activity 指示器的工作方式,请参阅上述代码段。找到以下几行: +

    +
    +// Sets the progress indicator to a max value, the current completion
    +// percentage, and "determinate" state
    +mBuilder.setProgress(100, incr, false);
    +// Issues the notification
    +mNotifyManager.notify(0, mBuilder.build());
    +
    +

    + 将找到的这几行替换为以下几行: +

    +
    + // Sets an activity indicator for an operation of indeterminate length
    +mBuilder.setProgress(0, 0, true);
    +// Issues the notification
    +mNotifyManager.notify(0, mBuilder.build());
    +
    + +

    通知元数据

    + +

    通知可根据您使用以下 +{@link android.support.v4.app.NotificationCompat.Builder} 方法分配的元数据进行排序:

    + +
      +
    • 当设备处于“优先”模式时,{@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} +会告知系统如何处理应用通知(例如,通知代表传入呼叫、即时消息还是闹铃)。 +
    • +
    • 如果优先级字段设置为 +{@code PRIORITY_MAX} 或 {@code PRIORITY_HIGH} 的通知还有声音或振动,则 {@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} +会将其显示在小型浮动窗口中。
    • +
    • {@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} +允许您向通知添加人员名单。您的应用可以使用此名单指示系统将指定人员发出的通知归成一组,或者将这些人员发出的通知视为更重要的通知。 + +
    • +
    + +
    + +

    + 图 3. 显示浮动通知的全屏 Activity +

    +
    + +

    浮动通知

    + +

    对于 Android 5.0(API 级别 21),当设备处于活动状态时(即,设备未锁定且其屏幕已打开),通知可以显示在小型浮动窗口中(也称为“浮动通知”)。 + +这些通知看上去类似于精简版的通知​​,只是浮动通知还显示操作按钮。 + +用户可以在不离开当前应用的情况下处理或清除浮动通知。 +

    + +

    可能触发浮动通知的条件示例包括:

    + +
      +
    • 用户的 Activity 处于全屏模式中(应用使用 +{@link android.app.Notification#fullScreenIntent}),或者
    • +
    • 通知具有较高的优先级并使用铃声或振动 +
    • +
    + +

    锁定屏幕通知

    + +

    随着 +Android 5.0(API 级别 21)的发布,通知现在还可显示在锁定屏幕上。您的应用可以使用此功能提供媒体播放控件以及其他常用操作。 +用户可以通过“设置”选择是否将通知显示在锁定屏幕上,并且您可以指定您应用中的通知在锁定屏幕上是否可见。 +

    + +

    设置可见性

    + +

    您的应用可以控制在安全锁定屏幕上显示的通知中可见的详细级别。 +调用 {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()} +并指定以下值之一:

    + +
      +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC} +显示通知的完整内容。
    • +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET} +不会在锁定屏幕上显示此通知的任何部分。
    • +
    • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} +显示通知图标和内容标题等基本信息,但是隐藏通知的完整内容。
    • +
    + +

    设置 {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} +后,您还可以提供其中隐藏了某些详细信息的替换版本通知内容。例如,短信 +应用可能会显示一条通知,指出“您有 +3 条新短信”,但是隐藏了短信内容和发件人。要提供此替换版本的通知,请先使用 +{@link android.support.v4.app.NotificationCompat.Builder} 创建替换通知。创建专用通知对象时,请通过 +{@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} +方法为其附加替换通知。 +

    + +

    在锁定屏幕上控制媒体播放

    + +

    在 Android 5.0(API 级别 21)中,锁定屏幕不再基于 +{@link android.media.RemoteControlClient}(现已弃用)显示媒体控件。取而代之的是,将 +{@link android.app.Notification.MediaStyle} 模板与 +{@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +方法结合使用,后者可将操作转换为可点击的图标。

    + +

    注:该模板和 +{@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} +方法未包含在支持库中,因此这些功能只能在 Android 5.0 及更高版本的系统上运行。

    + +

    要在 Android 5.0 系统的锁定屏幕上显示媒体播放控件,请将可见性设置为 +{@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC},如上文所述。然后,添加操作并设置 +{@link android.app.Notification.MediaStyle} +模板,如以下示例代码中所述:

    + +
    +Notification notification = new Notification.Builder(context)
    +    // Show controls on lock screen even when user hides sensitive content.
    +    .setVisibility(Notification.VISIBILITY_PUBLIC)
    +    .setSmallIcon(R.drawable.ic_stat_player)
    +    // Add media control buttons that invoke intents in your media service
    +    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
    +    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
    +    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
    +    // Apply the media style template
    +    .setStyle(new Notification.MediaStyle()
    +    .setShowActionsInCompactView(1 /* #1: pause button */)
    +    .setMediaSession(mMediaSession.getSessionToken())
    +    .setContentTitle("Wonderful music")
    +    .setContentText("My Awesome Band")
    +    .setLargeIcon(albumArtBitmap)
    +    .build();
    +
    + +

    注:弃用 {@link android.media.RemoteControlClient} +会对控制媒体产生进一步的影响。如需了解有关用于管理媒体会话和控制播放的新 API 的详细信息,请参阅媒体播放控件。 + +

    + + + +

    自定义通知布局

    +

    + 您可以利用通知框架定义自定义通知布局,由该布局定义通知在 +{@link android.widget.RemoteViews} 对象中的外观。 + 自定义布局通知类似于常规通知,但是它们是基于 XML 布局文件中所定义的 +{@link android.widget.RemoteViews}。 +

    +

    + 自定义通知布局的可用高度取决于通知视图。普通视图布局限制为 +64 dp,扩展视图布局限制为 256 dp。 +

    +

    + 要定义自定义通知布局,请首先实例化 +{@link android.widget.RemoteViews} 对象来扩充 XML 布局文件。然后,调用 +{@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()},而不是调用 +{@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} +等方法。要在自定义通知中设置内容详细信息,请使用 +{@link android.widget.RemoteViews} +中的方法设置视图子项的值: +

    +
      +
    1. + 在单独的文件中为通知创建 XML 布局。您可以根据需要使用任何文件名,但必须使用扩展名 +.xml。 +
    2. +
    3. + 在您的应用中,使用 {@link android.widget.RemoteViews} +方法定义通知的图标和文本。通过调用 +{@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()} 将此 {@link android.widget.RemoteViews} 对象放入 +{@link android.support.v4.app.NotificationCompat.Builder} 中。避免在 +{@link android.widget.RemoteViews} 对象上设置背景 +{@link android.graphics.drawable.Drawable},因为文本颜色可能使文本变得难以阅读。 +
    4. +
    +

    + 此外,{@link android.widget.RemoteViews} 类中还有一些方法可供您轻松将 +{@link android.widget.Chronometer} 或 {@link android.widget.ProgressBar} +添加到通知布局。如需了解有关为通知创建自定义布局的详细信息,请参阅 +{@link android.widget.RemoteViews} 参考文档。 +

    +

    + 注意:使用自定义通知布局时,要特别注意确保自定义布局适用于不同的设备方向和分辨率。 +尽管这条建议适用于所有“视图”布局,但对通知尤为重要,因为抽屉式通知栏中的空间非常有限。 + +不要让自定义布局过于复杂,同时确保在各种配置中对其进行测试。 + +

    + +

    对自定义通知文本使用样式资源

    +

    + 始终对自定义通知的文本使用样式资源。通知的背景颜色可能因设备和系统版本的不同而异,使用样式资源有助于您充分考虑到这一点。 + +从 +Android 2.3 开始,系统定义了标准通知布局文本的样式。若要在面向 Android +2.3 或更高版本系统的多个应用中使用相同样式,则应确保文本在显示背景上可见。 +

    diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/overview.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..5097c76e4027957c8b3cf4ea70681873eed31f91 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=UI 概览 +@jd:body + + +

    Android 应用中的所有用户界面元素都是使用 {@link android.view.View} 和 +{@link android.view.ViewGroup} 对象构建而成。{@link android.view.View} +对象用于在屏幕上绘制可供用户交互的内容。{@link android.view.ViewGroup} +对象用于储存其他 {@link android.view.View}(和 +{@link android.view.ViewGroup})对象,以便定义界面的局部。

    + +

    Android 提供了一系列 +{@link android.view.View} 和 {@link +android.view.ViewGroup} 子类,可为您提供常用输入控件(如按钮和文本字段)和各种布局模式(如线性布局或相对布局)。

    + + +

    用户界面布局

    + +

    如图 1 所示,每个应用组件的用户界面都是使用 {@link +android.view.View} 和 {@link android.view.ViewGroup} 对象的层次结构定义的。每个视图组都是一个用于组织子视图的不可见容器,而子视图可以是输入控件或其他可绘制某一 +UI +部分的小工具。此层次结构树可繁可简,随需而定(但是简单的结构可提供最佳性能)。 + +

    + + +

    图 1. 视图层次结构的图示,它定义了一个 UI +布局。

    + +

    要声明布局,您可以实例化代码中的 {@link android.view.View} 对象并开始构建树,但是定义布局最简单且最有效的方法是使用 XML +文件。如同 HTML 一样,XML +也为布局提供了一种用户可读结构。

    + +

    视图的 XML 元素名称与其代表的 Android 类相对应。因此, +<TextView> 元素用于在 UI 中创建一个 {@link android.widget.TextView} 小工具,而 +<LinearLayout> 元素用于创建一个 {@link android.widget.LinearLayout} +视图组。

    + +

    例如,包含文本视图和按钮的简单垂直布局如下所示:

    +
    +<?xml version="1.0" encoding="utf-8"?>
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +              android:layout_width="fill_parent" 
    +              android:layout_height="fill_parent"
    +              android:orientation="vertical" >
    +    <TextView android:id="@+id/text"
    +              android:layout_width="wrap_content"
    +              android:layout_height="wrap_content"
    +              android:text="I am a TextView" />
    +    <Button android:id="@+id/button"
    +            android:layout_width="wrap_content"
    +            android:layout_height="wrap_content"
    +            android:text="I am a Button" />
    +</LinearLayout>
    +
    + +

    在应用中加载布局资源时,Android +会将布局的每个节点初始化为运行时对象,供您定义其他行为、查询对象状态或修改布局。 +

    + +

    有关创建 UI 布局的完整指南,请参阅 XML +布局。 + + +

    用户界面组件

    + +

    您无需使用 {@link android.view.View} 和 {@link +android.view.ViewGroup} 对象构建所有 UI。Android 提供了几个带有标准 UI 布局的应用组件,您只需定义内容。 +这些 UI +组件均拥有一组唯一的 API,具体描述可参阅相应的文档,如操作栏对话框状态通知

    + + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/settings.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/settings.jd new file mode 100644 index 0000000000000000000000000000000000000000..f9be97be8c2e1b91e6f1dfe0df627b4e2aa6cb2d --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=设置 +page.tags=首选项,首选项 Activity,首选项片段 + +@jd:body + + +
    +
    + +

    本文内容

    +
      +
    1. 概览 +
        +
      1. 首选项
      2. +
      +
    2. +
    3. 使用 XML 定义首选项 +
        +
      1. 创建设置组
      2. +
      3. 使用 Intent
      4. +
      +
    4. +
    5. 创建首选项 Activity
    6. +
    7. 使用首选项片段
    8. +
    9. 设置默认值
    10. +
    11. 使用首选项标头 +
        +
      1. 创建标头文件
      2. +
      3. 显示标头
      4. +
      5. 使用首选项标头支持旧版本
      6. +
      +
    12. +
    13. 读取首选项 +
        +
      1. 侦听首选项变更
      2. +
      +
    14. +
    15. 管理网络使用情况
    16. +
    17. 构建自定义首选项 +
        +
      1. 指定用户界面
      2. +
      3. 保存设置的值
      4. +
      5. 初始化当前值
      6. +
      7. 提供默认值
      8. +
      9. 保存和恢复首选项的状态
      10. +
      +
    18. +
    + +

    关键类

    +
      +
    1. {@link android.preference.Preference}
    2. +
    3. {@link android.preference.PreferenceActivity}
    4. +
    5. {@link android.preference.PreferenceFragment}
    6. +
    + + +

    另请参阅

    +
      +
    1. 设置设计指南
    2. +
    +
    +
    + + + + +

    应用通常包括允许用户修改应用特性和行为的设置。例如,有些应用允许用户指定是否启用通知,或指定应用与云端同步数据的频率。 + +

    + +

    若要为应用提供设置,您应该使用 +Android 的 {@link android.preference.Preference} API +构建一个与其他 Android 应用中的用户体验一致的界面(包括系统设置)。本文旨在介绍如何使用 +{@link android.preference.Preference} API 构建应用设置。

    + +
    +

    设置设计

    +

    有关如何设计设置的信息,请阅读设置设计指南。

    +
    + + + +

    图 1. 来自 Android 信息应用的设置的屏幕截图。 +选择由 {@link android.preference.Preference} +定义的项目将打开一个用于更改设置的界面。

    + + + + +

    概览

    + +

    设置是使用您在 XML 文件中声明的 +{@link android.preference.Preference} +类的各种子类构建而成,而不是使用 {@link android.view.View} 对象构建用户界面。

    + +

    {@link android.preference.Preference} +对象是单个设置的构建基块。每个 {@link android.preference.Preference} 均作为项目显示在列表中,并提供适当的 +UI 供用户修改设置。例如,{@link +android.preference.CheckBoxPreference} 可创建一个列表项用于显示复选框,{@link +android.preference.ListPreference} 可创建一个项目用于打开包含选择列表的对话框。

    + +

    您添加的每个 {@link android.preference.Preference} 都有一个相应的键值对,可供系统用来将设置保存在应用设置的默认 +{@link android.content.SharedPreferences} +文件中。当用户更改设置时,系统会为您更新 +{@link android.content.SharedPreferences} 文件中的相应值。您只应在需要读取值以根据用户设置确定应用的行为时,才与关联的 +{@link android.content.SharedPreferences} +文件直接交互。

    + +

    为每个设置保存在 {@link android.content.SharedPreferences} +中的值可能是以下数据类型之一:

    + +
      +
    • 布尔型
    • +
    • 浮点型
    • +
    • 整型
    • +
    • 长整型
    • +
    • 字符串
    • +
    • 字符串 {@link java.util.Set}
    • +
    + +

    由于应用的设置 UI 是使用 {@link android.preference.Preference} +对象(而非 +{@link android.view.View} 对象)构建而成,因此您需要使用专门的 {@link android.app.Activity} 或 +{@link android.app.Fragment} 子类显示列表设置:

    + +
      +
    • 如果应用支持早于 3.0(API 级别 10 及更低级别)的 Android 版本,则您必须将 Activity 构建为 +{@link android.preference.PreferenceActivity} 类的扩展。
    • +
    • 对于 Android 3.0 及更高版本,您应改用传统 +{@link android.app.Activity},以托管可显示应用设置的 {@link android.preference.PreferenceFragment}。但是,如果您拥有多组设置,则还可以使用 +{@link android.preference.PreferenceActivity} +为大屏幕创建双窗格布局。
    • +
    + +

    创建首选项 Activity使用首选项片段 +部分将讨论如何设置 {@link android.preference.PreferenceActivity} 以及 {@link +android.preference.PreferenceFragment} 实例。

    + + +

    首选项

    + +

    所有应用设置均由 {@link +android.preference.Preference} 类的特定子类表示。每个子类均包括一组核心属性,允许您指定设置标题和默认值等内容。 +此外,每个子类还提供自己的专用属性和用户界面。 +例如,图 1 显示的是“信息” +应用的设置屏幕截图。设置屏幕中的每个列表项均由不同的 {@link +android.preference.Preference} 对象提供支持。

    + +

    一些最常用的首选项如下:

    + +
    +
    {@link android.preference.CheckBoxPreference}
    +
    显示一个包含已启用或已禁用设置复选框的项目。保存的值是布尔型(如果选中则为 +true)。
    + +
    {@link android.preference.ListPreference}
    +
    打开一个包含单选按钮列表的对话框。保存的值可以是任一受支持的值类型(如上所列)。 +
    + +
    {@link android.preference.EditTextPreference}
    +
    打开一个包含 {@link android.widget.EditText} 小工具的对话框。保存的值是 {@link +java.lang.String}。
    +
    + +

    有关所有其他子类及其对应属性的列表,请参阅 {@link android.preference.Preference} +类。

    + +

    当然,内置类不能满足所有需求,您的应用可能需要更专业化的内容。 +例如,该平台目前不提供用于选取数字或日期的 {@link +android.preference.Preference} 类。因此,您可能需要定义自己的 +{@link android.preference.Preference} 子类。如需有关执行此操作的帮助,请参阅构建自定义首选项部分。

    + + + +

    使用 XML 定义首选项

    + +

    虽然您可以在运行时实例化新的 {@link android.preference.Preference} +对象,不过您还是应该使用 {@link android.preference.Preference} +对象的层次结构在 XML 中定义设置列表。使用 XML +文件定义设置的集合是首选方法,因为该文件提供了一个便于更新的易读结构。此外,应用的设置通常是预先确定的,不过您仍可在运行时修改此集合。 +

    + +

    每个 {@link android.preference.Preference} 子类均可以使用与类名(如 {@code <CheckBoxPreference>})匹配的 XML 元素来声明。 +

    + +

    您必须将 XML 文件保存在 {@code res/xml/} 目录中。尽管您可以随意命名该文件,但它通常命名为 +{@code preferences.xml}。您通常只需一个文件,因为层次结构中的分支(可打开各自的设置列表)是使用 +{@link android.preference.PreferenceScreen} +的嵌套实例声明的。

    + +

    注:若要为设置创建多窗格布局,则需要为每个片段提供单独的 +XML 文件。

    + +

    XML 文件的根节点必须是一个 {@link android.preference.PreferenceScreen +<PreferenceScreen>} 元素。您可以在此元素内添加每个 {@link +android.preference.Preference}。在 +{@link android.preference.PreferenceScreen <PreferenceScreen>} +元素内添加的每个子项均将作为单独的项目显示在设置列表中。

    + +

    例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <CheckBoxPreference
    +        android:key="pref_sync"
    +        android:title="@string/pref_sync"
    +        android:summary="@string/pref_sync_summ"
    +        android:defaultValue="true" />
    +    <ListPreference
    +        android:dependency="pref_sync"
    +        android:key="pref_syncConnectionType"
    +        android:title="@string/pref_syncConnectionType"
    +        android:dialogTitle="@string/pref_syncConnectionType"
    +        android:entries="@array/pref_syncConnectionTypes_entries"
    +        android:entryValues="@array/pref_syncConnectionTypes_values"
    +        android:defaultValue="@string/pref_syncConnectionTypes_default" />
    +</PreferenceScreen>
    +
    + +

    在此示例中,有一个 {@link android.preference.CheckBoxPreference} 和 {@link +android.preference.ListPreference}。这两项均包括以下三个属性:

    + +
    +
    {@code android:key}
    +
    对于要保留数据值的首选项,必须拥有此属性。它指定系统在将此设置的值保存在 +{@link +android.content.SharedPreferences} 中时所用的唯一键(字符串)。 +

    不需要此属性的仅有情形是:首选项是 +{@link android.preference.PreferenceCategory} 或{@link android.preference.PreferenceScreen},或者首选项指定要调用的 +{@link android.content.Intent}(使用 {@code <intent>} 元素)或要显示的 {@link android.app.Fragment}(使用 {@code +android:fragment} 属性)。

    +
    +
    {@code android:title}
    +
    此属性为设置提供用户可见的名称。
    +
    {@code android:defaultValue}
    +
    此属性指定系统应该在 {@link +android.content.SharedPreferences} 文件中设置的初始值。您应该为所有设置提供默认值。 +
    +
    + +

    有关所有其他受支持属性的信息,请参阅 {@link +android.preference.Preference}(和相应子类)文档。

    + + +
    + +

    图 2. 带标题的设置类别。 +
    1.类别由 {@link +android.preference.PreferenceCategory <PreferenceCategory>} 元素指定。
    2.标题由 +{@code android:title} 属性指定。

    +
    + + +

    当设置列表超过 10 项时,您可能需要添加标题来定义设置组或在单独的屏幕中显示这些组。 + +下文将介绍这些选项。

    + + +

    创建设置组

    + +

    如果您提供的列表包含 10 +项或更多设置,则用户可能难以浏览、理解和处理这些设置。若要弥补这一点,您可以将部分或全部设置分成若干组,从而有效地将一个长列表转化为多个短列表。 + +可以通过下列两种方法之一提供一组相关设置:

    + + + +

    您可以使用其中一种或两种分组方法来组织应用的设置。决定要使用的技巧以及如何拆分设置时,应遵循 Android 设计的设置指南中的准则。 + +

    + + +

    使用标题

    + +

    若要以分隔线分隔两组设置并为其提供标题(如图 2 所示),请将每组 +{@link android.preference.Preference} 对象放入 {@link +android.preference.PreferenceCategory} 内。

    + +

    例如:

    + +
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <PreferenceCategory 
    +        android:title="@string/pref_sms_storage_title"
    +        android:key="pref_key_storage_settings">
    +        <CheckBoxPreference
    +            android:key="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_auto_delete"
    +            android:title="@string/pref_title_auto_delete"
    +            android:defaultValue="false"... />
    +        <Preference 
    +            android:key="pref_key_sms_delete_limit"
    +            android:dependency="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_delete_limit"
    +            android:title="@string/pref_title_sms_delete"... />
    +        <Preference 
    +            android:key="pref_key_mms_delete_limit"
    +            android:dependency="pref_key_auto_delete"
    +            android:summary="@string/pref_summary_delete_limit"
    +            android:title="@string/pref_title_mms_delete" ... />
    +    </PreferenceCategory>
    +    ...
    +</PreferenceScreen>
    +
    + + +

    使用子屏幕

    + +

    若要将设置组放入子屏幕(如图 3 所示),请将 +{@link android.preference.Preference} 对象组放入 {@link +android.preference.PreferenceScreen} 内。

    + + +

    图 3. 设置子屏幕。{@code +<PreferenceScreen>} 元素创建的项目选中后,即会打开一个单独的列表来显示嵌套设置。 +

    + +

    例如:

    + +
    +<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    +    <!-- opens a subscreen of settings -->
    +    <PreferenceScreen
    +        android:key="button_voicemail_category_key"
    +        android:title="@string/voicemail"
    +        android:persistent="false">
    +        <ListPreference
    +            android:key="button_voicemail_provider_key"
    +            android:title="@string/voicemail_provider" ... />
    +        <!-- opens another nested subscreen -->
    +        <PreferenceScreen
    +            android:key="button_voicemail_setting_key"
    +            android:title="@string/voicemail_settings"
    +            android:persistent="false">
    +            ...
    +        </PreferenceScreen>
    +        <RingtonePreference
    +            android:key="button_voicemail_ringtone_key"
    +            android:title="@string/voicemail_ringtone_title"
    +            android:ringtoneType="notification" ... />
    +        ...
    +    </PreferenceScreen>
    +    ...
    +</PreferenceScreen>
    +
    + + +

    使用 Intent

    + +

    在某些情况下,您可能需要首选项来打开不同的 Activity(而不是 Web +浏览器等设置屏幕)或查看网页。要在用户选择首选项时调用 {@link +android.content.Intent},请将{@code <intent>} +元素添加为相应 {@code <Preference>} 元素的子元素。

    + +

    例如,您可以按如下方法使用首选项打开网页:

    + +
    +<Preference android:title="@string/prefs_web_page" >
    +    <intent android:action="android.intent.action.VIEW"
    +            android:data="http://www.example.com" />
    +</Preference>
    +
    + +

    您可以使用以下属性创建隐式和显式 Intent:

    + +
    +
    {@code android:action}
    +
    要分配的操作(按照 {@link android.content.Intent#setAction setAction()} +方法)。
    +
    {@code android:data}
    +
    要分配的数据(按照 {@link android.content.Intent#setData setData()} 方法)。
    +
    {@code android:mimeType}
    +
    要分配的 MIME 类型(按照 {@link android.content.Intent#setType setType()} +方法)。
    +
    {@code android:targetClass}
    +
    组件名称的类部分(按照 {@link android.content.Intent#setComponent +setComponent()} 方法)。
    +
    {@code android:targetPackage}
    +
    组件名称的软件包部分(按照 {@link +android.content.Intent#setComponent setComponent()} 方法)。
    +
    + + + +

    创建首选项 Activity

    + +

    要在 Activity 中显示您的设置,请扩展 {@link +android.preference.PreferenceActivity} 类。这是传统 {@link +android.app.Activity} 类的扩展,该类根据 {@link +android.preference.Preference} 对象的层次结构显示设置列表。当用户进行更改时,{@link android.preference.PreferenceActivity} +会自动保留与每个 {@link +android.preference.Preference} 相关的设置。

    + +

    注:如果您是开发针对 Android 3.0 及 +更高版本系统的应用,则应改为使用 {@link android.preference.PreferenceFragment}。转到下文有关使用首选项片段的部分。 +

    + +

    请记住最重要的一点,就是不要在 {@link +android.preference.PreferenceActivity#onCreate onCreate()} 回调期间加载视图的布局。相反,请调用 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 以将在 +XML 文件中声明的首选项添加到 Activity。例如,一个能够正常工作的 +{@link android.preference.PreferenceActivity} 至少需要如下代码:

    + +
    +public class SettingsActivity extends PreferenceActivity {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +}
    +
    + +

    实际上,对于某些应用而言此,代码就已足够,因为用户修改某首选项后,系统会立即将所做的更改保存到默认 {@link android.content.SharedPreferences} 文件中,如需检查用户的设置,可以使用您的其他应用组件读取该文件。 + +不过,许多应用需要的代码要稍微多一点,以侦听首选项发生的变化。有关侦听 +{@link android.content.SharedPreferences} +文件变化的信息,请参阅读取首选项部分。 +

    + + + + +

    使用首选项片段

    + +

    如果您是开发针对 Android 3.0(API 级别 11)及更高版本系统的应用,则应使用 {@link +android.preference.PreferenceFragment} 显示 {@link android.preference.Preference} +对象的列表。您可以将 {@link android.preference.PreferenceFragment} 添加到任何 Activity,而不必使用 +{@link android.preference.PreferenceActivity}。

    + +

    与仅使用上述 Activity 相比,无论您在构建何种 Activity,片段都可为应用提供一个更加灵活的体系结构。 + +因此,我们建议您尽可能使用 {@link +android.preference.PreferenceFragment} 控制设置的显示,而不是使用 {@link +android.preference.PreferenceActivity}。

    + +

    {@link android.preference.PreferenceFragment} 的实现就像定义 +{@link android.preference.PreferenceFragment#onCreate onCreate()} 方法以使用 +{@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()} 加载首选项文件一样简单。例如:

    + +
    +public static class SettingsFragment extends PreferenceFragment {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        // Load the preferences from an XML resource
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +    ...
    +}
    +
    + +

    然后,正如您对其他任何 +{@link android.app.Fragment} 的处理一样,您可以将此片段添加到 {@link android.app.Activity}。例如:

    + +
    +public class SettingsActivity extends Activity {
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        // Display the fragment as the main content.
    +        getFragmentManager().beginTransaction()
    +                .replace(android.R.id.content, new SettingsFragment())
    +                .commit();
    +    }
    +}
    +
    + +

    注:{@link android.preference.PreferenceFragment} 没有自己的 +{@link android.content.Context} 对象。如需 {@link android.content.Context} +对象,您可以调用 {@link android.app.Fragment#getActivity()}。但请注意,只应在该片段附加到 Activity 时才调用 +{@link android.app.Fragment#getActivity()}。如果该片段尚未附加或在其生命周期结束期间已分离,则 +{@link +android.app.Fragment#getActivity()} 将返回空 null。

    + + +

    设置默认值

    + +

    您创建的首选项可能会为应用定义一些重要行为,因此在用户首次打开应用时,您有必要使用每个 {@link android.preference.Preference} +的默认值初始化相关的 +{@link android.content.SharedPreferences} +文件。

    + +

    首先,您必须使用 {@code android:defaultValue} 属性为 XML 文件中的每个 {@link +android.preference.Preference} +对象指定默认值。该值可以是适合相应 +{@link android.preference.Preference} 对象的任意数据类型。例如: +

    + +
    +<!-- default value is a boolean -->
    +<CheckBoxPreference
    +    android:defaultValue="true"
    +    ... />
    +
    +<!-- default value is a string -->
    +<ListPreference
    +    android:defaultValue="@string/pref_syncConnectionTypes_default"
    +    ... />
    +
    + +

    然后,通过应用的主 Activity(以及用户首次进入应用所藉由的任何其他 Activity)中的 {@link android.app.Activity#onCreate onCreate()} +方法调用 {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}: +

    + +
    +PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
    +
    + +

    在 +{@link android.app.Activity#onCreate onCreate()} +期间调用此方法可确保使用默认设置正确初始化应用,而应用可能需要读取这些设置以确定某些行为(例如,是否在蜂窝网络中下载数据)。 +

    + +

    此方法采用三个参数:

    +
      +
    • 应用 {@link android.content.Context}。
    • +
    • 要为其设置默认值的首选项 XML 文件的资源 ID。
    • +
    • 一个布尔值,用于指示是否应该多次设置默认值。 +

      如果该值为 false,则仅当过去从未调用此方法时(或者默认值共享首选项文件中的 +{@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES}为 +false 时),系统才会设置默认值。

    • +
    + +

    只要将第三个参数设置为 +false,您便可在每次启动 Activity 时安全地调用此方法,而不必通过重置为默认值来替代用户已保存的首选项。 +但是,如果将它设置为 +true,则需要使用默认值替代之前的所有值。

    + + + +

    使用首选项标头

    + +

    在极少数情况下,您可能需要设计设置,使第一个屏幕仅显示子屏幕的列表(例如在系统“设置”应用中,如图 4 和图 5 所示)。 + +在开发针对 Android 3.0 及更高版本系统的此类设计时,您应该使用 +Android 3.0 中的新“标头”功能,而非使用嵌套的 +{@link android.preference.PreferenceScreen} 元素构建子屏幕。

    + +

    要使用标头构建设置,您需要:

    +
      +
    1. 将每组设置分成单独的 {@link +android.preference.PreferenceFragment} 实例。即,每组设置均需要一个单独的 XML +文件。
    2. +
    3. 创建 XML +标头文件,其中列出每个设置组并声明哪个片段包含对应的设置列表。
    4. +
    5. 扩展 {@link android.preference.PreferenceActivity} 类以托管设置。
    6. +
    7. 实现 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} +回调以指定标头文件。
    8. +
    + +

    使用此设计的一大好处是,在大屏幕上运行时,{@link android.preference.PreferenceActivity} +会自动提供双窗格布局(如图 4 所示)。

    + +

    即使您的应用支持早于 3.0 的 Android 版本,您仍可将应用设计为使用 +{@link android.preference.PreferenceFragment} +在较新版本的设备上呈现双窗格布局,同时仍支持较旧版本设备上传统的多屏幕层次结构(请参阅使用首选项标头支持旧版本部分)。 + +

    + + +

    图 4. 带标头的双窗格布局。
    1.标头用 +XML 标头文件定义。
    2.每组设置均由 +{@link android.preference.PreferenceFragment}(通过标头文件中的 {@code <header>} +元素指定)定义。

    + + +

    图 5. 带设置标头的手机设备。选择项目后,相关的 +{@link android.preference.PreferenceFragment} +将替换标头。

    + + +

    创建标头文件

    + +

    标头列表中的每组设置均由根 {@code <preference-headers>} 元素内的单个 {@code <header>} +元素指定。例如:

    + +
    +<?xml version="1.0" encoding="utf-8"?>
    +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    +    <header 
    +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one" />
    +    <header 
    +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" >
    +        <!-- key/value pairs can be included as arguments for the fragment. -->
    +        <extra android:name="someKey" android:value="someHeaderValue" />
    +    </header>
    +</preference-headers>
    +
    + +

    每个标头均可使用 {@code android:fragment} 属性声明在用户选择该标头时应打开的 {@link +android.preference.PreferenceFragment} 实例。

    + +

    {@code <extras>} 元素允许您使用 {@link +android.os.Bundle} 将键值对传递给片段。该片段可以通过调用 {@link +android.app.Fragment#getArguments()} 检索参数。您向该片段传递参数的原因可能有很多,不过一个重要原因是,要对每个组重复使用 +{@link +android.preference.PreferenceFragment} +的相同子类,而且要使用参数来指定该片段应加载哪些首选项 XML 文件。

    + +

    例如,当每个标头均使用 +{@code "settings"} 键定义 {@code <extra>} 参数时,则可以对多个设置组重复使用以下片段:

    + +
    +public static class SettingsFragment extends PreferenceFragment {
    +    @Override
    +    public void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +
    +        String settings = getArguments().getString("settings");
    +        if ("notifications".equals(settings)) {
    +            addPreferencesFromResource(R.xml.settings_wifi);
    +        } else if ("sync".equals(settings)) {
    +            addPreferencesFromResource(R.xml.settings_sync);
    +        }
    +    }
    +}
    +
    + + + +

    显示标头

    + +

    要显示首选项标头,您必须实现 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回调方法并调用 +{@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}。例如:

    + +
    +public class SettingsActivity extends PreferenceActivity {
    +    @Override
    +    public void onBuildHeaders(List<Header> target) {
    +        loadHeadersFromResource(R.xml.preference_headers, target);
    +    }
    +}
    +
    + +

    当用户从标头列表中选择一个项目时,系统会打开相关的 {@link +android.preference.PreferenceFragment}。

    + +

    注:使用首选项标头时,{@link +android.preference.PreferenceActivity} 的子类无需实现 {@link +android.preference.PreferenceActivity#onCreate onCreate()} +方法,因为 Activity 唯一所需执行的任务就是加载标头。

    + + +

    使用首选项标头支持旧版本

    + +

    如果您的应用支持早于 3.0 的 Android +版本,则在 Android 3.0 及更高版本系统上运行时,您仍可使用标头提供双窗格数据。为此,您只需另外创建 +一个使用基本 {@link android.preference.Preference +<Preference>} 元素的首选项 XML 文件即可,这些基本元素的行为方式与标头项目类似(供较旧版本的 Android + 系统使用)。

    + +

    但是,每个 {@link +android.preference.Preference <Preference>} 元素均会向 {@link android.preference.PreferenceActivity} 发送一个 {@link android.content.Intent},指定要加载哪个首选项 XML 文件,而不是打开新的 +{@link android.preference.PreferenceScreen}。 +

    + +

    例如,下面就是一个用于 Android 3.0 +及更高版本系统的首选项标头 XML 文件 ({@code res/xml/preference_headers.xml}):

    + +
    +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    +    <header 
    +        android:fragment="com.example.prefs.SettingsFragmentOne"
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one" />
    +    <header 
    +        android:fragment="com.example.prefs.SettingsFragmentTwo"
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" />
    +</preference-headers>
    +
    + +

    下面是为早于 +Android 3.0 版本的系统提供相同标头的首选项文件 ({@code res/xml/preference_headers_legacy.xml}):

    + +
    +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    +    <Preference 
    +        android:title="@string/prefs_category_one"
    +        android:summary="@string/prefs_summ_category_one"  >
    +        <intent 
    +            android:targetPackage="com.example.prefs"
    +            android:targetClass="com.example.prefs.SettingsActivity"
    +            android:action="com.example.prefs.PREFS_ONE" />
    +    </Preference>
    +    <Preference 
    +        android:title="@string/prefs_category_two"
    +        android:summary="@string/prefs_summ_category_two" >
    +        <intent 
    +            android:targetPackage="com.example.prefs"
    +            android:targetClass="com.example.prefs.SettingsActivity"
    +            android:action="com.example.prefs.PREFS_TWO" />
    +    </Preference>
    +</PreferenceScreen>
    +
    + +

    由于是从 Android 3.0 开始方添加对 {@code <preference-headers>} 的支持,因此只有在 Androd 3.0 或更高版本中运行时,系统才会在您的 {@link +android.preference.PreferenceActivity} +中调用 {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}。要加载“旧版”标头文件 +({@code preference_headers_legacy.xml}),您必须检查 Android +版本,如果版本低于 Android 3.0 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}),请调用 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} +来加载旧版标头文件。例如:

    + +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +    ...
    +
    +    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    +        // Load the legacy preferences headers
    +        addPreferencesFromResource(R.xml.preference_headers_legacy);
    +    }
    +}
    +
    +// Called only on Honeycomb and later
    +@Override
    +public void onBuildHeaders(List<Header> target) {
    +   loadHeadersFromResource(R.xml.preference_headers, target);
    +}
    +
    + +

    最后要做的就是处理传入 Activity 的 +{@link android.content.Intent},以确定要加载的首选项文件。因此,请检索 Intent 的操作,并将其与在首选项 XML 的 {@code <intent>} +标记中使用的已知操作字符串进行比较。

    + +
    +final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
    +...
    +
    +@Override
    +public void onCreate(Bundle savedInstanceState) {
    +    super.onCreate(savedInstanceState);
    +
    +    String action = getIntent().getAction();
    +    if (action != null && action.equals(ACTION_PREFS_ONE)) {
    +        addPreferencesFromResource(R.xml.preferences);
    +    }
    +    ...
    +
    +    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    +        // Load the legacy preferences headers
    +        addPreferencesFromResource(R.xml.preference_headers_legacy);
    +    }
    +}
    +
    + +

    值得注意的是,连续调用 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} +会将所有首选项堆叠在一个列表中,因此请将条件与 +else-if 语句链接在一起,确保它只调用一次。

    + + + + + +

    读取首选项

    + +

    默认情况下,应用的所有首选项均保存到一个可通过调用静态方法 +{@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()} 从应用内的任何位置访问的文件中。这将返回 {@link +android.content.SharedPreferences} 对象,其中包含与 +{@link +android.preference.PreferenceActivity} 中所用 {@link android.preference.Preference} 对象相关的所有键值对。

    + +

    例如,从应用中的任何其他 Activity 读取某个首选项值的方法如下: +

    + +
    +SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
    +String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
    +
    + + + +

    侦听首选项变更

    + +

    出于某些原因,您可能希望在用户更改任一首选项时立即收到通知。 +要在任一首选项发生更改时收到回调,请实现 +{@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} 接口,并通过调用 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} 为 +{@link android.content.SharedPreferences} 对象注册侦听器。

    + +

    该接口只有 {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()} +一种回调方法,而且您可能会发现在 Activity 过程中实现该接口最为简单。例如:

    + +
    +public class SettingsActivity extends PreferenceActivity
    +                              implements OnSharedPreferenceChangeListener {
    +    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
    +    ...
    +
    +    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
    +        String key) {
    +        if (key.equals(KEY_PREF_SYNC_CONN)) {
    +            Preference connectionPref = findPreference(key);
    +            // Set summary to be the user-description for the selected value
    +            connectionPref.setSummary(sharedPreferences.getString(key, ""));
    +        }
    +    }
    +}
    +
    + +

    在此示例中,该方法检查更改的设置是否是针对已知的首选项键。它调用 +{@link android.preference.PreferenceActivity#findPreference findPreference()} 来获取已更改的 +{@link android.preference.Preference} +对象,以便能够将项目摘要修改为对用户选择的说明。即,如果设置为 {@link +android.preference.ListPreference} 或其他多选设置时,则当设置更改为显示当前状态(例如,图 +5 所示的“Sleep”设置)时,您应调用 {@link +android.preference.Preference#setSummary setSummary()}。

    + +

    注:正如 Android 设计有关设置的文档中所述,我们建议您在用户每次更改首选项时更新 +{@link android.preference.ListPreference} +的摘要,以描述当前设置。

    + +

    若要妥善管理 Activity 生命周期,我们建议您在 +{@link +android.app.Activity#onResume} 和 {@link android.app.Activity#onPause} 回调期间分别注册和注销 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener}。

    + +
    +@Override
    +protected void onResume() {
    +    super.onResume();
    +    getPreferenceScreen().getSharedPreferences()
    +            .registerOnSharedPreferenceChangeListener(this);
    +}
    +
    +@Override
    +protected void onPause() {
    +    super.onPause();
    +    getPreferenceScreen().getSharedPreferences()
    +            .unregisterOnSharedPreferenceChangeListener(this);
    +}
    +
    + +

    注意:目前,首选项管理器不会在您调用 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} +时存储对侦听器的强引用。但是,您必须存储对侦听器的强引用,否则它将很容易被当作垃圾回收。 +我们建议您将对侦听器的引用保存在只要您需要侦听器就会存在的对象的实例数据中。 + +

    + +

    例如,在以下代码中,调用方未保留对侦听器的引用。 +因此,侦听器将容易被当作垃圾回收,并在将来某个不确定的时间失败: +

    + +
    +prefs.registerOnSharedPreferenceChangeListener(
    +  // Bad! The listener is subject to garbage collection!
    +  new SharedPreferences.OnSharedPreferenceChangeListener() {
    +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    +    // listener implementation
    +  }
    +});
    +
    + +

    有鉴于此,请将对侦听器的引用存储在只要需要侦听器就会存在的对象的实例数据字段中: +

    + +
    +SharedPreferences.OnSharedPreferenceChangeListener listener =
    +    new SharedPreferences.OnSharedPreferenceChangeListener() {
    +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    +    // listener implementation
    +  }
    +};
    +prefs.registerOnSharedPreferenceChangeListener(listener);
    +
    + +

    管理网络使用情况

    + + +

    从 Android 4.0 +开始,通过系统的“设置”应用,用户可以了解自己的应用在前台和后台使用的网络数据量。然后,用户可以据此禁止具体的应用使用后台数据。 +为了避免用户禁止您的应用从后台访问数据,您应该有效地使用数据连接,并允许用户通过应用设置优化应用的数据使用。 + +

    + +

    例如,您可以允许用户控制应用同步数据的频率,控制应用是否仅在有 +Wi-Fi 时才执行上传/下载操作,以及控制应用能否在漫游时使用数据,等等。为用户提供这些控件后,即使数据使用量接近他们在系统“设置”中设置的限制,他们也不大可能禁止您的应用访问数据,因为他们可以精确地控制应用使用的数据量。 + + +

    + +

    在 {@link android.preference.PreferenceActivity} +中添加必要的首选项来控制应用的数据使用习惯后,您应立即在清单文件中为 {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} 添加 Intent 过滤器。例如:

    + +
    +<activity android:name="SettingsActivity" ... >
    +    <intent-filter>
    +       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
    +       <category android:name="android.intent.category.DEFAULT" />
    +    </intent-filter>
    +</activity>
    +
    + +

    此 Intent +过滤器指示系统此 Activity 控制应用的数据使用情况。因此,当用户从系统的“设置”应用检查应用所使用的数据量时,可以使用“查看应用设置”按钮启动 +{@link android.preference.PreferenceActivity},这样,用户就能够优化应用使用的数据量。 + +

    + + + + + + + +

    构建自定义首选项

    + +

    Android 框架包括各种 +{@link android.preference.Preference} +子类,您可以使用它们为各种不同类型的设置构建 UI。不过,您可能会发现自己需要的设置没有内置解决方案,例如,数字选取器或日期选取器。 +在这种情况下,您将需要通过扩展 +{@link android.preference.Preference} 类或其他子类之一来创建自定义首选项。

    + +

    扩展 {@link android.preference.Preference} +类时,您需要执行以下几项重要操作:

    + +
      +
    • 指定在用户选择设置时显示的用户界面。
    • +
    • 适时保存设置的值。
    • +
    • 使用显示的当前(默认)值初始化 +{@link android.preference.Preference}。
    • +
    • 在系统请求时提供默认值。
    • +
    • 如果 {@link android.preference.Preference} +提供自己的 UI(例如对话框),请保存并恢复状态以处理生命周期变更(例如,用户旋转屏幕)。
    • +
    + +

    下文介绍如何完成所有这些任务。

    + + + +

    指定用户界面

    + +

    如果您要直接扩展 {@link android.preference.Preference} 类,则需要实现 +{@link android.preference.Preference#onClick()} +来定义在用户选择该项时发生的操作。不过,大多数自定义设置都会扩展 {@link android.preference.DialogPreference} +以显示对话框,从而简化这一过程。扩展 {@link +android.preference.DialogPreference} 时,必须在类构造函数中调用 {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()} +来指定对话框的布局。

    + +

    例如,自定义 {@link +android.preference.DialogPreference} +可以使用下面的构造函数来声明布局并为默认的肯定和否定对话框按钮指定文本:

    + +
    +public class NumberPickerPreference extends DialogPreference {
    +    public NumberPickerPreference(Context context, AttributeSet attrs) {
    +        super(context, attrs);
    +        
    +        setDialogLayoutResource(R.layout.numberpicker_dialog);
    +        setPositiveButtonText(android.R.string.ok);
    +        setNegativeButtonText(android.R.string.cancel);
    +        
    +        setDialogIcon(null);
    +    }
    +    ...
    +}
    +
    + + + +

    保存设置的值

    + +

    如果设置的值为整型数或是用于保存布尔值的 +{@link android.preference.Preference#persistBoolean persistBoolean()},则可通过调用 {@link +android.preference.Preference} 类的一个 {@code persist*()} 方法(如 {@link +android.preference.Preference#persistInt persistInt()})随时保存该值。

    + +

    注:每个 {@link android.preference.Preference} +均只能保存一种数据类型,因此您必须使用适合自定义 +{@link android.preference.Preference} 所用数据类型的 {@code persist*()} 方法。

    + +

    至于何时选择保留设置,则可能取决于要扩展的 {@link +android.preference.Preference} 类。如果扩展 +{@link +android.preference.DialogPreference},则只能在对话框因肯定结果(用户选择“确定”按钮)而关闭时保留该值。

    + +

    当 {@link android.preference.DialogPreference} 关闭时,系统会调用 {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()} 方法。该方法包括一个布尔参数,用于指定用户结果是否为“肯定”;如果值为 +true,则表示用户选择的是肯定按钮且您应该保存新值。 +例如: +

    + +
    +@Override
    +protected void onDialogClosed(boolean positiveResult) {
    +    // When the user selects "OK", persist the new value
    +    if (positiveResult) {
    +        persistInt(mNewValue);
    +    }
    +}
    +
    + +

    在此示例中,mNewValue +是一个类成员,可存放设置的当前值。调用 {@link android.preference.Preference#persistInt persistInt()} 会将该值保存到 +{@link android.content.SharedPreferences} 文件(自动使用在此 +{@link android.preference.Preference} 的 XML 文件中指定的键)。

    + + +

    初始化当前值

    + +

    系统将 {@link android.preference.Preference} 添加到屏幕时,会调用 +{@link android.preference.Preference#onSetInitialValue onSetInitialValue()} +来通知您设置是否具有保留值。如果没有保留值,则此调用将为您提供默认值。 +

    + +

    {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} +方法传递一个布尔值 (restorePersistedValue),以指示是否已为该设置保留值。 +如果值为 true,则应通过调用 +{@link +android.preference.Preference} 类的一个 {@code getPersisted*()} 方法(如整型值对应的 {@link +android.preference.Preference#getPersistedInt getPersistedInt()})来检索保留值。通常,您会需要检索保留值,以便能够正确更新 UI 来反映之前保存的值。 + +

    + +

    如果 restorePersistedValue 为 +false,则应使用在第二个参数中传递的默认值。

    + +
    +@Override
    +protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    +    if (restorePersistedValue) {
    +        // Restore existing state
    +        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    +    } else {
    +        // Set default state from the XML attribute
    +        mCurrentValue = (Integer) defaultValue;
    +        persistInt(mCurrentValue);
    +    }
    +}
    +
    + +

    每种 {@code getPersisted*()} +方法均采用一个参数,用于指定在实际上没有保留值或该键不存在时所要使用的默认值。在上述示例中,当 +{@link +android.preference.Preference#getPersistedInt getPersistedInt()} 不能返回保留值时,局部常量用于指定默认值。

    + +

    注意:不能使用 +defaultValue 作为 {@code getPersisted*()} 方法中的默认值,因为当 +restorePersistedValuetrue 时,其值始终为 null。

    + + +

    提供默认值

    + +

    如果 {@link android.preference.Preference} +类的实例指定一个默认值(使用 {@code android:defaultValue} +属性),则在实例化对象以检索该值时,系统会调用 {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()}。您必须实现此方法,系统才能将默认值保存在 {@link +android.content.SharedPreferences} 中。 +例如:

    + +
    +@Override
    +protected Object onGetDefaultValue(TypedArray a, int index) {
    +    return a.getInteger(index, DEFAULT_VALUE);
    +}
    +
    + +

    方法参数可提供您所需的一切:属性的数组和 +{@code android:defaultValue}(必须检索的值)的索引位置。之所以必须实现此方法以从该属性中提取默认值,是因为您必须为此属性指定在未定义属性值时所要使用的局部默认值。 + +

    + + + +

    保存和恢复首选项的状态

    + +

    正如布局中的 {@link android.view.View} +一样,在重启 Activity 或片段时(例如,用户旋转屏幕),{@link android.preference.Preference} +子类也负责保存并恢复其状态。要正确保存并恢复 +{@link android.preference.Preference} 类的状态,您必须实现生命周期回调方法 +{@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}。

    + +

    {@link android.preference.Preference} 的状态由实现 +{@link android.os.Parcelable} 接口的对象定义。Android 框架为您提供此类对象,作为定义状态对象({@link +android.preference.Preference.BaseSavedState} +类)的起点。

    + +

    要定义 {@link android.preference.Preference} 类保存其状态的方式,您应该扩展 +{@link android.preference.Preference.BaseSavedState} 类。您只需重写几种方法并定义 +{@link android.preference.Preference.BaseSavedState#CREATOR} +对象。

    + +

    对于大多数应用,如果 {@link android.preference.Preference} +子类保存除整型数以外的其他数据类型,则可复制下列实现并直接更改处理 +{@code value} 的行。

    + +
    +private static class SavedState extends BaseSavedState {
    +    // Member that holds the setting's value
    +    // Change this data type to match the type saved by your Preference
    +    int value;
    +
    +    public SavedState(Parcelable superState) {
    +        super(superState);
    +    }
    +
    +    public SavedState(Parcel source) {
    +        super(source);
    +        // Get the current preference's value
    +        value = source.readInt();  // Change this to read the appropriate data type
    +    }
    +
    +    @Override
    +    public void writeToParcel(Parcel dest, int flags) {
    +        super.writeToParcel(dest, flags);
    +        // Write the preference's value
    +        dest.writeInt(value);  // Change this to write the appropriate data type
    +    }
    +
    +    // Standard creator object using an instance of this class
    +    public static final Parcelable.Creator<SavedState> CREATOR =
    +            new Parcelable.Creator<SavedState>() {
    +
    +        public SavedState createFromParcel(Parcel in) {
    +            return new SavedState(in);
    +        }
    +
    +        public SavedState[] newArray(int size) {
    +            return new SavedState[size];
    +        }
    +    };
    +}
    +
    + +

    如果将上述 {@link android.preference.Preference.BaseSavedState} +实现添加到您的应用(通常,作为 {@link android.preference.Preference} +子类的子类),则需要为 +{@link android.preference.Preference} 子类实现 {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} 方法。

    + +

    例如:

    + +
    +@Override
    +protected Parcelable onSaveInstanceState() {
    +    final Parcelable superState = super.onSaveInstanceState();
    +    // Check whether this Preference is persistent (continually saved)
    +    if (isPersistent()) {
    +        // No need to save instance state since it's persistent,
    +        // use superclass state
    +        return superState;
    +    }
    +
    +    // Create instance of custom BaseSavedState
    +    final SavedState myState = new SavedState(superState);
    +    // Set the state's value with the class member that holds current
    +    // setting value
    +    myState.value = mNewValue;
    +    return myState;
    +}
    +
    +@Override
    +protected void onRestoreInstanceState(Parcelable state) {
    +    // Check whether we saved the state in onSaveInstanceState
    +    if (state == null || !state.getClass().equals(SavedState.class)) {
    +        // Didn't save the state, so call superclass
    +        super.onRestoreInstanceState(state);
    +        return;
    +    }
    +
    +    // Cast state to custom BaseSavedState and pass to superclass
    +    SavedState myState = (SavedState) state;
    +    super.onRestoreInstanceState(myState.getSuperState());
    +    
    +    // Set this Preference's widget to reflect the restored state
    +    mNumberPicker.setValue(myState.value);
    +}
    +
    + diff --git a/docs/html-intl/intl/zh-cn/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/zh-cn/guide/topics/ui/ui-events.jd new file mode 100644 index 0000000000000000000000000000000000000000..f9e976302d347003707e8d9f59a19aeb19330510 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=输入事件 +parent.title=用户界面 +parent.link=index.html +@jd:body + +
    + +
    + +

    在 Android 系统中,从用户与应用的交互中截获事件的方法不止一种。如考虑截获用户界面内的事件,则可从用户与之交互的特定视图对象中捕获事件。 + +为此,View 类提供了多种方法。

    + +

    在您将用于构建布局的各种 View 类中,您可能会注意到几种看起来适用于 +UI 事件的公共回调方法。当该对象上发生相应的操作时,Android +框架会调用这些方法。例如,在触摸一个视图对象(例如“按钮”)时,对该对象调用 +onTouchEvent() 方法。不过,为了截获此事件,您必须扩展 View 类并重写该方法。 +然而,为了处理此类事件而扩展每个视图对象并不现实。 +正因如此,View 类还包含一系列嵌套接口以及您可以更加轻松定义的回调。 +这些接口称为事件侦听器,是您捕获用户与 +UI 之间交互的票证。

    + +

    尽管您通常会使用事件侦听器来侦听用户交互,但有时您确实需要扩展 View 类以构建自定义组件。 +也许,您想扩展 +{@link android.widget.Button} +类来丰富某些内容的样式。在这种情况下,您将能够使用该类的事件处理程序为类定义默认事件行为。 +

    + + +

    事件侦听器

    + +

    事件侦听器是 {@link android.view.View} +类中包含一个回调方法的接口。当用户与 UI 项目之间的交互触发已注册此视图的侦听器时,Android +框架将调用这些方法。

    + +

    各事件侦听器接口包含的回调方法如下:

    + +
    +
    onClick()
    +
    在 {@link android.view.View.OnClickListener} 中。 +当用户触摸项目(处于触摸模式下)时,或者使用导航键或轨迹球聚焦于项目,然后按适用的“Enter”键或按下轨迹球时,将调用此方法。 + +
    +
    onLongClick()
    +
    在 {@link android.view.View.OnLongClickListener} 中。 +当用户触摸并按住项目(处于触摸模式下)者,或者使用导航键或轨迹球聚焦于项目,然后按住适用的“Enter”键或按住轨迹球(持续一秒钟)时,将调用此方法。 + +
    +
    onFocusChange()
    +
    在 {@link android.view.View.OnFocusChangeListener} 中。 +当用户使用导航键或轨迹球导航到或远离项目时,将调用此方法。
    +
    onKey()
    +
    在 {@link android.view.View.OnKeyListener} 中。 +当用户聚焦于项目并按下或释放设备上的硬按键时,将调用此方法。
    +
    onTouch()
    +
    在 {@link android.view.View.OnTouchListener} 中。 +当用户执行可视为触摸事件的操作时,其中包括按下、释放或屏幕上的任何移动手势(在项目边界内),将调用此方法。 +
    +
    onCreateContextMenu()
    +
    在 {@link android.view.View.OnCreateContextMenuListener} 中。 +当(因持续“长按”而)生成上下文菜单时,将调用此方法。请参阅菜单开发者指南中有关上下文菜单的阐述。 + +
    +
    + +

    这些方法是其相应接口的唯一成员。要定义其中一个方法并处理事件,请在 Activity 中实现嵌套接口或将其定义为匿名类。然后,将实现的实例传递给相应的 +View.set...Listener() +方法。 +(例如,调用 +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()} +并向其传递 {@link android.view.View.OnClickListener OnClickListener} 实现。)

    + +

    以下示例显示了如何为按钮注册点击侦听器。

    + +
    +// Create an anonymous implementation of OnClickListener
    +private OnClickListener mCorkyListener = new OnClickListener() {
    +    public void onClick(View v) {
    +      // do something when the button is clicked
    +    }
    +};
    +
    +protected void onCreate(Bundle savedValues) {
    +    ...
    +    // Capture our button from layout
    +    Button button = (Button)findViewById(R.id.corky);
    +    // Register the onClick listener with the implementation above
    +    button.setOnClickListener(mCorkyListener);
    +    ...
    +}
    +
    + +

    您可能还会发现,将 +OnClickListener 作为 Activity 的一部分来实现更为方便。这样可以避免加载额外的类和分配对象。例如:

    +
    +public class ExampleActivity extends Activity implements OnClickListener {
    +    protected void onCreate(Bundle savedValues) {
    +        ...
    +        Button button = (Button)findViewById(R.id.corky);
    +        button.setOnClickListener(this);
    +    }
    +
    +    // Implement the OnClickListener callback
    +    public void onClick(View v) {
    +      // do something when the button is clicked
    +    }
    +    ...
    +}
    +
    + +

    请注意,上述示例中的 +onClick() 回调没有返回值,但是其他某些事件侦听器方法必须返回布尔值。具体原因取决于事件。 +对于这几个事件侦听器,必须返回布尔值的原因如下:

    +
      +
    • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()}: +此方法返回一个布尔值,表示您是否已处理完事件,以及是否应该将它继续传下去。 +也就是说,返回“true”表示您已经处理事件且事件应就此停止;如果您尚未处理事件和/或事件应该继续传递给其他任何点击侦听器,则返回“false”。 + +
    • +
    • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()}: +此方法返回一个布尔值,表示您是否已处理完事件,以及是否应该将它继续传下去。 + 也就是说,返回“true”表示您已经处理事件且事件应就此停止;如果您尚未处理事件和/或事件应该继续传递给其他任何按键侦听器,则返回“false”。 + +
    • +
    • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()}: +此方法返回一个布尔值,表示侦听器是否处理完此事件。重要的是,此事件可以拥有多个分先后顺序的操作。 +因此,如果在收到关闭操作事件时返回“false”,则表示您并未处理完此事件,而且对其后续操作也不感兴趣。 + +因此,您无需执行事件内的任何其他操作,如手势或最终操作事件。 +
    • +
    + +

    请记住,硬按键事件总是传递给目前处于焦点的视图对象。它们从视图 +层次结构的顶部开始分派,然后向下,直至到达合适的目的地。如果您的视图对象(或视图对象的子项)目前具有焦点,那么您可以看到事件经由 +{@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()} 方法的分派过程。除了通过视图捕获按键事件,您还可以使用 +{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} +和 {@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()} 接收 Activity 内部的所有事件。

    + +

    此外,考虑应用的文本输入时,请记住:许多设备只有软件输入法。 +此类方法无需基于按键;某些可能使用语音输入、手写等。尽管输入法提供了类似键盘的界面,但它通常不会触发 +{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} +系列的事件。除非您要将应用限制为带有硬键盘的设备,否则,在设计 UI 时切勿要求必须通过特定按键进行控制。 + +特别是,当用户按下返回键时,不要依赖这些方法验证输入;请改用 +{@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} +等操作让输入法知晓您的应用预计会作何反应,这样,可以通过一种有意义的方式更改其 UI。不要推断软件输入法应如何工作,只要相信它能够为应用提供已设置格式的文本即可。 +

    + +

    注:Android 会先调用事件处理程序,然后从类定义调用合适的默认处理程序。 +因此,从这些事件侦听器返回“true”会停止将事件传播到其他事件侦听器,还会阻止回调视图对象中的默认事件处理程序。 + +因此,在返回“true”时请确保您要终止事件。

    + + +

    事件处理程序

    + +

    如果您从视图构建自定义组件,则将能够定义几种用作默认事件处理程序的回调方法。在有关自定义组件的文档中,您将了解某些用于事件处理的常见回调,其中包括: + + + +

    +
      +
    • {@link android.view.View#onKeyDown}:在发生新的按键事件时调用
    • +
    • {@link android.view.View#onKeyUp}:在发生按键弹起事件时调用
    • +
    • {@link android.view.View#onTrackballEvent}:在发生轨迹球运动事件时调用
    • +
    • {@link android.view.View#onTouchEvent}:在发生触摸屏运动事件时调用
    • +
    • {@link android.view.View#onFocusChanged}:在视图获得或失去焦点时调用
    • +
    +

    还有一些其他方法值得您注意,尽管它们并非 View 类的一部分,但可能会直接影响所能采取的事件处理方式。 +因此,在管理布局内更复杂的事件时,请考虑使用以下其他方法: +

    +
      +
    • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)}:此方法允许 {@link + android.app.Activity} 在分派给窗口之前截获所有触摸事件。
    • +
    • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)}:此方法允许 {@link + android.view.ViewGroup} 监视分派给子视图的事件。
    • +
    • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)}: +对父视图调用此方法表明不应使用 {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)} 截获触摸事件。
    • +
    + +

    触摸模式

    +

    +当用户使用方向键或轨迹球导航用户界面时,必须聚焦到可操作项目上(如按钮),以便用户看到将接受输入的对象。 + +但是,如果设备具有触摸功能且用户开始通过触摸界面与之交互,则不再需要突出显示项目或聚焦到特定视图对象上。 + +因此,有一种交互模式称为“触摸模式”。 + +

    +

    +对于支持触摸功能的设备,当用户触摸屏幕时,设备会立即进入触摸模式。 +自此以后,只有 +{@link android.view.View#isFocusableInTouchMode} +为“true”的视图才可聚焦,如文本编辑小工具。其他可触摸的视图(如按钮)在用户触摸时不会获得焦点;按下时它们只是触发点击侦听器。 + +

    +

    +无论何时,只要用户点击方向键或滚动轨迹球,设备就会退出触摸模式并找到一个视图使其获得焦点。 +现在,用户可在不触摸屏幕的情况下继续与用户界面交互。 + +

    +

    +整个系统(所有窗口和 Activity)都将保持触摸模式状态。要查询当前状态,您可以调用 +{@link android.view.View#isInTouchMode} +来检查设备目前是否处于触摸模式。 +

    + + +

    处理焦点

    + +

    该框架将处理例行焦点移动来响应用户输入。其中包括在视图被删除或隐藏时或在新视图变得可用时更改焦点。 + +视图对象表示愿意通过 +{@link android.view.View#isFocusable()} 方法获得焦点。要设置视图能否获得焦点,请调用 +{@link android.view.View#setFocusable(boolean) setFocusable()}。在触摸模式中,您可以使用 +{@link android.view.View#isFocusableInTouchMode()} 查询视图是否允许聚焦,而且可以使用 +{@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()} 对此进行更改。 +

    + +

    焦点移动所使用的算法会查找指定方向上距离最近的元素。 +在极少数情况下,默认算法可能与开发者的期望行为不一致。 +在这些情况下,您可以在布局文件中显式重写以下 +XML 属性: +nextFocusDownnextFocusLeftnextFocusRight和 +nextFocusUp。将其中一个属性添加到失去焦点的视图。 +将属性的值定义为应该聚焦的视图的 +ID。例如:

    +
    +<LinearLayout
    +    android:orientation="vertical"
    +    ... >
    +  <Button android:id="@+id/top"
    +          android:nextFocusUp="@+id/bottom"
    +          ... />
    +  <Button android:id="@+id/bottom"
    +          android:nextFocusDown="@+id/top"
    +          ... />
    +</LinearLayout>
    +
    + +

    一般来说,在此垂直布局中,从第一个按钮向上导航或从第二个按钮向下导航,焦点都不会移到任何其他位置。 +现在,顶部按钮已将底部按钮定义为 + nextFocusUp (反之亦然),因而导航焦点将自上而下和自下而上循环往复。 +

    + +

    若要将一个视图声明为在 UI 中可聚焦(传统上并非如此),请在布局声明中将 android:focusable XML 属性添加到该视图。将值设置为 + + true。此外,您还可以使用 +android:focusableInTouchMode 将 Vew 声明为在触摸模式下可聚焦。

    +

    要请求要获得焦点的特定视图,请调用 {@link android.view.View#requestFocus()}

    +

    要侦听焦点事件(视图获得或失去焦点时会收到通知),请使用 +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()}, +如上文的事件侦听器部分中所述。

    + + + + diff --git a/docs/html-intl/intl/zh-cn/index.jd b/docs/html-intl/intl/zh-cn/index.jd index cdd81cde059650a140408307877addbe301c34c4..ca3a84b80eb2b74ba0d825aad3ce42ced79f1284 100644 --- a/docs/html-intl/intl/zh-cn/index.jd +++ b/docs/html-intl/intl/zh-cn/index.jd @@ -5,42 +5,81 @@ page.customHeadTag= -
    --> - +
  • -
    + + + -
    +

    Build Beautiful Apps

    Resources to get you started with designing and developing for Android. diff --git a/docs/html-intl/intl/zh-cn/preview/api-overview.jd b/docs/html-intl/intl/zh-cn/preview/api-overview.jd deleted file mode 100644 index f7764cdc5b5f7dc0a9b81e32d7625643768fb08c..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/api-overview.jd +++ /dev/null @@ -1,521 +0,0 @@ -page.title=API 概览 -page.keywords=预览版,sdk,兼容性 -page.tags=previewresources, androidm -sdk.platform.apiLevel=22-mnc -page.image=images/cards/card-api-overview_16-9_2x.png -@jd:body - - - - -

    M 开发者预览版可让您预览即将推出的 Android 平台版本为用户和应用开发者提供的新功能。 - -本文旨在介绍其中最值得关注的 API。

    - -

    M 开发者预览版的适用对象是喜欢尝鲜的开发者测试人员。 -如果您有兴趣影响 Android 框架的发展方向,欢迎试用 M 开发者预览版并向我们提供反馈! - - -

    - -

    注意:请不要在 Google Play 商店中发布基于 M 开发者预览版的应用。 -

    - -

    注:本文经常提及的一些类和方法在 developer.android.com 上尚未提供相关参考资料。 -这些 API 元素在本文中设置为 {@code code style} 格式(不带超链接)。 -如需查看这些元素的初步 API 文档,请下载预览版参考资料。 -

    - -

    重要的行为变更

    - -

    如果您之前发布过 Android 应用,请注意您的应用可能受到平台变化的影响。 -

    - -

    如需了解完整信息,请参阅行为变更

    - -

    应用链接

    -

    此预览版通过提供功能更强大的应用链接,增强了 Android 的意向系统。您可以利用此功能将应用与您拥有的某个 Web 域关联。 -平台可以根据此关联确定在处理特定 Web 链接时默认使用的应用,跳过提示用户选择应用的步骤。如需了解如何实现此功能,请参阅应用链接。 - - - - -

    自动备份应用

    -

    现在,系统可以自动为应用执行完整数据备份和恢复。对于以 M 预览版为目标平台的应用,系统会默认启用此行为,您无需额外添加任何代码。 -如果用户删除其 Google 帐户,其备份数据也会被删除。 -如需了解该功能的工作方式以及配置文件系统备份内容的方法,请参阅自动备份应用。 - -

    - -

    身份验证

    -

    本预览版提供一些新的 API,在受支持的设备上,用户只需扫描其指纹即可完成身份验证,还可检查系统最后一次使用设备解锁机制(如锁屏密码)对用户进行身份验证是发生在多久之前。 - -这些 API 可与 Android 密钥库系统结合使用。 -

    - -

    指纹身份验证

    - -

    如需通过指纹扫描验证用户身份,请获取新增 -{@code android.hardware.fingerprint.FingerprintManager} 类的实例,并调用 -{@code FingerprintManager.authenticate()} 方法。您的应用必须运行在带有指纹传感器的兼容设备上。 -您必须在应用中实现指纹身份验证流的用户界面,并在 UI 中使用标准 Android 指纹图标。示例应用中包含有 Android 指纹图标 ({@code c_fp_40px.png})。请注意,如果您要开发多个使用指纹身份验证的应用,每个应用必须独立验证用户的指纹。 - - - - -

    - -

    如需在您的应用中使用此功能,请先在清单文件中添加 {@code USE_FINGERPRINT} 权限。 -

    - -
    -<uses-permission
    -        android:name="android.permission.USE_FINGERPRINT" />
    -
    - - - -

    如需查看指纹身份验证的应用实现,请参阅指纹对话框示例。 - -

    - -

    如果您要测试此功能,请执行以下步骤:

    -
      -
    1. 如果您尚未安装 Android SDK 工具 24.3 修订版,请执行此操作。
    2. -
    3. 转到设置 > 安全 > 指纹,然后按照登记说明在模拟器中登记新指纹。 -
    4. -
    5. 使用模拟器通过以下命令模拟指纹触摸事件。 -使用同一命令模拟锁屏上或应用中的指纹触摸事件。 - -
      -adb -e emu finger touch <finger_id>
      -
      -

      在 Windows 上,您可能需要运行带有 -{@code finger touch } 参数的 {@code telnet 127.0.0.1 } 命令。 -

      -
    6. -
    - -

    确认凭据

    -

    您的应用可以根据用户在多久之前最后一次解锁设备来验证其身份。此功能让用户不必费心记忆应用特定密码,您也无需实现自己的身份验证用户界面。 - -您的应用应当利用此功能并结合实现公钥或私钥,以进行用户身份验证。 -

    - -

    如需设置成功验证用户身份后可再次使用同一密钥的超时持续时间,请在设置 {@link javax.crypto.KeyGenerator} 或 -{@link java.security.KeyPairGenerator} 时调用新增的 -{@code android.security.keystore.KeyGenParameterSpec.setUserAuthenticationValidityDurationSeconds()} - 方法。 -此功能目前适用于对称加密操作。 -

    - -

    避免过多显示重新验证对话框 -- 您的应用应尝试先使用加密对象,如果超时到期,请使用 -{@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence) createConfirmDeviceCredentialIntent()} 方法在您的应用内重新验证用户身份。 - - -

    - -

    如需查看此功能的应用实现,请参阅确认凭据示例。 - -

    - -

    直接共享

    - - - -

    本预览版为您提供的 API 可让用户直观、快捷地进行共享。现在,您可以定义可在您的应用内启动特定活动的直接共享目标。这些直接共享目标通过“共享”菜单公开给用户。 - -此功能让用户可以向其他应用内的目标(如联系人)共享内容。 -例如,直接共享目标可以启动另一社交网络应用中的某个活动,让用户可以直接向该应用中的某位朋友或某个社区共享内容。 - -

    - -

    如需启用直接共享目标,您必须定义一个类,用于扩展 -{@code android.service.}
    -{@code chooser.ChooserTargetService} 类。在清单文件中声明您的 -{@code ChooserTargetService}。在该声明内,指定 -{@code BIND_CHOOSER_TARGET_SERVICE} 权限和一个带有 -{@code SERVICE_INTERFACE} 操作的意向过滤器。

    -

    以下示例展示了如何在清单文件中声明 {@code ChooserTargetService}。 -

    -
    -<service android:name=".ChooserTargetService"
    -        android:label="@string/service_name"
    -        android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
    -    <intent-filter>
    -        <action android:name="android.service.chooser.ChooserTargetService" />
    -    </intent-filter>
    -</service>
    -
    - -

    对于您想要向 {@code ChooserTargetService} 公开的每个活动,请在您的应用清单文件中为其添加一个名为 -{@code "android.service.chooser.chooser_target_service"} 的 -{@code } 元素。 -

    - -
    -<activity android:name=".MyShareActivity”
    -        android:label="@string/share_activity_label">
    -    <intent-filter>
    -        <action android:name="android.intent.action.SEND" />
    -    </intent-filter>
    -<meta-data
    -        android:name="android.service.chooser.chooser_target_service"
    -        android:value=".ChooserTargetService" />
    -</activity>
    -
    - -

    语音交互

    -

    -本预览版提供了一个新的语音交互 API,与 -语音操作一起使用时,可让您为应用内建对话式语音体验。 -调用 -{@code android.app.Activity.isVoiceInteraction()} 方法可确定您的活动是否为响应语音操作而启动。 -如果是这样,则您的应用可以使用 -{@code android.app.VoiceInteractor} 类请求用户进行语音确认、从选项列表中进行选择以及执行其他操作。 -如需了解有关实现语音操作的更多信息,请参阅 -语音操作开发者网站。 -

    - -

    助手 API

    -

    -本预览版提供了一种让用户通过助手程序与应用进行互动的新方式。如需使用此功能,用户必须启用助手以使用当前上下文。 -启用后,用户可通过长按主页按钮在任何应用内召唤助手。 -

    -

    您的应用可通过设置 -{@link android.view.WindowManager.LayoutParams#FLAG_SECURE} 标志选择不与助手共享当前上下文。除了平台传递给助手的一组标准信息外,您的应用还可利用新增的 {@code android.app.Activity.AssistContent} 类共享其他信息。 - -

    - -

    如需为助手提供您的应用内的其他上下文,请执行以下步骤:

    - -
      -
    1. 实现 {@link android.app.Application.OnProvideAssistDataListener} 接口。
    2. -
    3. 利用 -{@link android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()} 注册此侦听器。
    4. -
    5. 如需提供特定于活动的上下文信息,请替代 -{@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} - 回调和新增的 {@code Activity.onProvideAssistContent()} 回调(可选操作)。 -
    - -

    通知

    -

    本预览版针对通知功能引入了下列 API 变更:

    -
      -
    • 新增了 {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} 过滤级别,它对应于新增的“仅闹铃”免打扰模式。 -
    • -
    • 新增了 {@code Notification.CATEGORY_REMINDER} 类别值,用于区分用户安排的提醒与其他事件 ({@link android.app.Notification#CATEGORY_EVENT}) 和闹铃 ({@link android.app.Notification#CATEGORY_ALARM})。 - - -
    • -
    • 新增了 {@code android.graphics.drawable.Icon} 类,可通过 {@code Notification.Builder.setSmallIcon(Icon)} 方法和 -{@code Notification.Builder.setLargeIcon(Icon)} 方法附加到通知上。 -
    • -
    • 新增了 {@code NotificationManager.getActiveNotifications()} 方法,让您的应用能够了解哪些通知目前处于活动状态。 -如需查看使用此功能的应用实现,请参阅活动通知示例。 -
    • -
    - -

    蓝牙触控笔支持

    -

    本预览版改善了对用户使用蓝牙触控笔进行输入的支持。用户可将兼容的蓝牙触控笔与其手机或平板电脑配对并建立连接。 -连接后,来自触摸屏的位置信息将与来自触控笔的压力和按键信息融合,从而实现比单纯使用触摸屏更丰富的表达。 - -您的应用可以通过在活动中注册新增的 -{@code View.onStylusButtonPressListener} 回调和 {@code GestureDetector.OnStylusButtonPressListener} - 回调侦听触控笔按键动作并执行辅助操作。 -

    - -

    可使用 {@link android.view.MotionEvent} 方法和常量来检测触控笔按键交互: -

    -
      -
    • 如果用户使用带按键的触控笔触按应用屏幕, -{@link android.view.MotionEvent#getToolType(int) getTooltype()} 方法会返回 -{@link android.view.MotionEvent#TOOL_TYPE_STYLUS}。
    • -
    • 对于以 M 预览版为目标平台的应用,当用户按触控笔的主按键时, -{@link android.view.MotionEvent#getButtonState() getButtonState()} - 方法会返回 {@code MotionEvent.STYLUS_BUTTON_PRIMARY}。 -如果触控笔有辅助按键,当用户按下它时,同一方法会返回 -{@code MotionEvent.STYLUS_BUTTON_SECONDARY}。如果用户同时按下两个按键,该方法会同时返回通过 OR 运算符连接起来的两个值 ({@code STYLUS_BUTTON_PRIMARY|STYLUS_BUTTON_SECONDARY})。 - -
    • -
    • -对于以较低平台版本为目标的应用, -{@link android.view.MotionEvent#getButtonState() getButtonState()} 方法返回 -{@link android.view.MotionEvent#BUTTON_SECONDARY}(按下触控笔主按键时)、 -{@link android.view.MotionEvent#BUTTON_TERTIARY}(按下触控笔辅助按键时)之一或同时返回这两者。 -
    • -
    - -

    改进的蓝牙低功耗扫描

    -

    -如果您的应用执行蓝牙低功耗扫描,可以使用新增的 -{@code android.bluetooth.le.ScanSettings.Builder.setCallbackType()} 方法指定您只希望在下列条件下通知回调:首次找到与设置的 -{@link android.bluetooth.le.ScanFilter} 匹配的播发数据包并且该数据包已有一段时间没有出现。 - -这种扫描方法与旧平台版本中提供的方法相比更加节能。 - -

    - -

    Hotspot 2.0 第 1 版支持

    -

    -本预览版在 Nexus 6 和 Nexus 9 设备上添加了对 Hotspot 2.0 第 1 版规范的支持。如需在您的应用中设置 Hotspot 2.0 凭据,请使用新增的 -{@link android.net.wifi.WifiEnterpriseConfig} 类方法,如 {@code setPlmn()} 方法和 -{@code setRealm()} 方法。 -在 {@link android.net.wifi.WifiConfiguration} 对象中,您可以设置 -{@link android.net.wifi.WifiConfiguration#FQDN} 字段和 {@code providerFriendlyName} 字段。 -新增的 {@code ScanResult.PasspointNetwork} 属性可指示检测到的网络是否为 Hotspot 2.0 接入点。 - -

    - -

    4K 显示模式

    -

    现在,平台允许应用在兼容硬件上请求将显示分辨率升级到 4K 渲染。 -如需查询当前物理分辨率,请使用新增的 -{@code android.view.Display.Mode} API。请注意,如果 UI 是以较低逻辑分辨率绘制并通过放大达到更高的物理分辨率,则 -{@code Display.Mode.getPhysicalWidth()} 方法返回的物理分辨率可能不同于 {@link android.view.Display#getSize(android.graphics.Point) getSize()} 所报告的逻辑分辨率。 - -

    - -

    您可以通过设置应用窗口的 {@code WindowManager.LayoutParams.preferredDisplayModeId} 属性请求系统更改应用运行时的物理分辨率。 -如果您想切换到 4K 显示分辨率,此功能会很有帮助。 -在 4K 显示模式下,UI 仍然以原始分辨率(如 1080p)渲染,通过放大达到 4K,但 -{@link android.view.SurfaceView} 对象可能会以原生分辨率显示内容。 -

    - -

    主题化 ColorStateList

    -

    对于运行 M 预览版的设备,现在支持在 -{@link android.content.res.ColorStateList} 中使用主题属性。 -{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} 方法和 -{@link android.content.res.Resources#getColor(int) getColor()} 方法已弃用。如果您要调用这些 API,请改为调用新增的 {@code Context.getColorStateList()} 方法或 -{@code Context.getColor()} 方法。 -还可在 v4 appcompat 库中通过 {@link android.support.v4.content.ContextCompat} 使用这些方法。 -

    - -

    音频功能

    - -

    本预览版增强了 Android 上的音频处理功能,包括:

    -
      -
    • 通过新增的 {@code android.media.midi} API 提供了对 MIDI - 协议的支持。使用这些 API 可发送和接收 MIDI 事件。 -
    • -
    • 新增了 {@code android.media.AudioRecord.Builder} 类和 {@code android.media.AudioTrack.Builder} - 类,分别用于创建数字音频采集和回放对象,还可用于配置音频源和接收器属性来替代系统默认值。 -
    • -
    • 用于关联音频和输入设备的 API 钩子。如果您的应用允许用户通过与 Android TV 相连的游戏控制器或遥控器启动语音搜索,此功能尤为有用。系统会在用户启动搜索时调用新增的 {@code android.app.Activity.onSearchRequested()} 回调。 - - -如需确定用户的输入设备是否内置麦克风,请从该回调检索 {@link android.view.InputDevice} 对象,然后调用新增的 -{@code InputDevice.hasMic()} 方法。 -
    • -
    • 新增了 {@code android.media.AudioDevicesManager} 类,让您可以检索连接的所有源设备和接收器音频设备列表。 -如果您想让应用在音频设备连接或断开时收到通知,还可以指定一个 -{@code android.media.OnAudioDeviceConnectionListener} 对象。 -
    • -
    - -

    视频功能

    -

    本预览版为视频处理 API 添加了新功能,包括:

    -
      -
    • 新增了 {@code android.media.MediaSync} 类,可帮助应用同步渲染音频流和视频流。 -音频缓冲区以非锁定方式提交,并通过回调返回。 -此外,它还支持动态回放速率。 -
    • -
    • 新增了 {@code MediaDrm.EVENT_SESSION_RECLAIMED} 事件,它表示应用打开的会话已被资源管理器收回。 -如果您的应用使用 DRM 会话,则应处理此事件,并确保不使用收回的会话。 - -
    • -
    • 新增了 {@code MediaCodec.CodecException.ERROR_RECLAIMED} 错误代码,它表示资源管理器收回了编解码器使用的媒体资源。 -出现此异常时,必须释放编解码器,因为它已转入终止状态。 - -
    • -
    • 新增了 {@code MediaCodecInfo.CodecCapabilities.getMaxSupportedInstances()} 接口,用于获取有关支持的编解码器实例最大并发数量的提示。 - -
    • -
    • 新增了 {@code MediaPlayer.setPlaybackParams()} 方法,用于设置快动作回放或慢动作回放的媒体回放速率。 -此外,它还会随视频一起自动拉长或加速音频回放。 -
    • -
    - -

    相机功能

    -

    本预览版提供了下列用于访问相机闪光灯和相机图像再处理的新 API: -

    - -

    闪光灯 API

    -

    如果相机设备带有闪光灯,您可以通过调用 {@code CameraManager.setTorchMode()} - 方法,在不打开相机设备的情况下打开或关闭闪光灯的火炬模式。应用对闪光灯或相机设备不享有独占所有权。 -每当相机设备不可用,或者开启火炬的其他相机资源不可用时,火炬模式即会被关闭并变为不可用状态。 - -其他应用也可调用 {@code setTorchMode()} 来关闭火炬模式。 -当最后一个开启火炬模式的应用关闭时,火炬模式就会被关闭。 -

    - -

    您可以注册一个回调,通过调用 -{@code CameraManager.registerTorchCallback()} 方法接收有关火炬模式状态的通知。第一次注册回调时,系统会立即调用它,并返回所有当前已知配备闪光灯的相机设备的火炬模式状态。 - -如果成功开启或关闭火炬模式,系统会调用 -{@code CameraManager.TorchCallback.onTorchModeChanged()} 方法。

    - -

    再处理 API

    -

    {@link android.hardware.camera2 Camera2} API 进行了扩展,以支持 YUV 和专用不透明格式图像再处理。 -您的应用通过 {@code CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES} 确定再处理功能是否可用。 -如果设备支持再处理,您可以通过调用 -{@code CameraDevice.createReprocessableCaptureSession()} 创建一个可再处理的相机捕获会话并创建输入缓冲区再处理请求。 - -

    - -

    使用 {@code ImageWriter} 类可将输入缓冲区流与相机再处理输入相连。 -如需获得空白缓冲区,请遵循以下编程模型:

    - -
      -
    1. 调用 {@code ImageWriter.dequeueInputImage()} 方法。
    2. -
    3. 在输入缓冲区中填充数据。
    4. -
    5. 通过调用 {@code ImageWriter.queueInputImage()} 方法将缓冲区发送至相机。
    6. -
    - -

    如果您将 {@code ImageWriter} 对象与 -{@code android.graphics.ImageFormat.PRIVATE} 图像一起使用,您的应用并不能直接访问图像数据。 -请改为调用 {@code ImageWriter.queueInputImage()} 方法,将 {@code ImageFormat.PRIVATE} 图像直接传递给 -{@code ImageWriter},而无需进行任何缓冲区复制。 -

    - -

    {@code ImageReader} 类现在支持 {@code android.graphics.ImageFormat.PRIVATE} 格式图像流。 -凭借此支持特性,您的应用可使 -{@code ImageReader} 输出图像保持为循环图像队列,还可选择一个或多个图像并将其发送给 -{@code ImageWriter} 进行相机再处理。

    - -

    Android for Work 功能

    -

    本预览版提供了下列用于 Android for Work 的新 API:

    -
      -
    • 用于企业所有、单一用途设备的增强型控件:现在,设备所有者可以通过控制以下设置来改善企业所有、单一用途 (COSU) 设备的管理: - - -
        -
      • 通过 -{@code DevicePolicyManager.setKeyguardEnabledState()} 方法禁用或重新启用键盘锁。
      • -
      • 通过 -{@code DevicePolicyManager.setStatusBarEnabledState()} 方法禁用或重新启用状态栏(包括快速设置、通知以及启动 Google Now 的向上划动手势)。 -
      • -
      • 通过 {@link android.os.UserManager} 常量 -{@code DISALLOW_SAFE_BOOT} 禁用或重新启用安全启动
      • -
      • 通过 -{@link android.provider.Settings.Global} 常量 {@code STAY_ON_WHILE_PLUGGED_IN} 防止屏幕在插入电源的情况下关闭。
      • -
      -
    • -
    • 设备所有者静默式安装和卸载应用:现在,设备所有者可使用 {@link android.content.pm.PackageInstaller} - API 在不依赖 Google Play for Work 的情况下静默式安装和卸载应用。 -现在,您可以通过设备所有者配置设备,从而无需用户干预即可获取并安装应用。 -此功能可用于在不激活 Google 帐户的情况下实现信息亭或其他此类设备的一键式配置。 -
    • -
    • 静默式企业证书访问:现在,当应用调用 -{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()} 时,配置文件所有者或设备所有者可以在系统提示用户选择证书前调用 {@code DeviceAdminReceiver.onChoosePrivateKeyAlias()} 方法,静默式向发出请求的应用提供别名。 - - -此功能让您可以在无需用户干预的情况下授予托管应用访问证书的权限。 -
    • -
    • 自动接受系统更新。现在,设备所有者可以通过 -{@code DevicePolicyManager.setSystemUpdatePolicy()} 设置一个系统更新政策来自动接受系统更新(例如对于信息亭设备),或者推迟更新并在至多 30 天的时间内防止用户获取更新。 - -此外,管理员还可设置每日必须获取更新的时间窗口,例如在信息亭设备无人使用的时段。 -有可用的系统更新时,系统会检查工作政策控制器应用是否设置了系统更新政策,并相应地执行操作。 - - -
    • -
    • -授权证书安装:配置文件所有者或设备所有者现在可以授权第三方应用调用以下 {@link android.app.admin.DevicePolicyManager} 证书管理 API: - - -
        -
      • {@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName) -getInstalledCaCerts()}
      • -
      • {@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[]) -hasCaCertInstalled()}
      • -
      • {@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[]) -installCaCert()}
      • -
      • {@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[]) -uninstallCaCert()}
      • -
      • {@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName) -uninstallAllUserCaCerts()}
      • -
      • {@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String) -installKeyPair()}
      • -
      -
    • -
    • 企业恢复出厂设置保护:现在,配置设备所有者时,您可以通过设置 -{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS} 捆绑包配置参数来解锁恢复出厂设置保护 (FRP)。 -在重置设备以解锁 FRP 和配置设备之后,NFC 编程器应用可提供这些参数,而无需事先配置 Google 帐户。 - -如果您不修改这些参数,FRP 仍然发挥作用,必须使用之前激活的 Google 凭据方可激活设备。 - - -

      此外,通过对 Google Play 服务设置应用限制,设备所有者可以指定用于 FRP 解锁的备用 Google 帐户,以替代在设备上激活的 Google 帐户。 -

      -
    • - -
    • 数据使用情况跟踪。现在,配置文件所有者或设备所有者可以利用新增的 -{@code android.app.usage.NetworkStatsManager} 方法查询设置 > 数据使用情况中显示的数据使用情况统计信息。 -配置文件所有者会被自动授予查询其管理的配置文件相关数据的权限,而设备所有者则被授予对其管理的主要用户使用情况数据的访问权。 - -
    • -
    • 运行时权限管理: -

      配置文件所有者或设备所有者可以利用 -{@code DevicePolicyManager.setPermissionPolicy()} 设置适用于所有应用全部运行时请求的权限政策,以提示用户照常授予权限,或自动以静默方式授予或拒绝权限。 - -如果设置后一种政策,则用户将无法修改配置文件所有者或设备所有者在应用权限屏幕的设置内所做的选择。 - -

    • -
    • “设置”中的 VPN:现在,设置 > 更多 > VPN 中会显示 VPN 应用。此外,现在,关于 VPN 使用情况的通知取决于该 VPN 的配置方式。 - - -对于配置文件所有者,通知取决于该 VPN 是针对托管配置文件、个人配置文件还是同时针对这两者进行配置。 -对于设备所有者,通知取决于 VPN 是否针对整个设备进行配置。 -
    • -
    • 工作状态通知:现在,每当来自托管配置文件的应用具有前台活动时,状态栏就会出现一个公文包图标。 -此外,如果设备直接解锁到托管配置文件中某个应用的活动,则会显示一个 Toast,通知用户他们位于托管配置文件内。 - - -
    • -
    - -

    - 如需详细了解 M 开发者预览版中的所有 API 变更,请参阅 API 差异报告。 -

    diff --git a/docs/html-intl/intl/zh-cn/preview/backup/index.jd b/docs/html-intl/intl/zh-cn/preview/backup/index.jd deleted file mode 100644 index 39786a3a270b180da50001302c4c16057d2935a6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/backup/index.jd +++ /dev/null @@ -1,327 +0,0 @@ -page.title=自动备份应用 -page.tags=备份, 预览版资源, androidm -page.keywords=备份, 自动备份, 预览版 -page.image=images/cards/card-auto-backup_2x.png -@jd:body - -
    - -
    - -

    - 通常,用户需要投入大量的时间和精力在应用内创建数据和设置首选项。 -如果用户更换破损设备或升级至新设备,则保留用户数据对于确保提供卓越的用户体验至关重要。 -在这些情况下,运行 Android M 预览版系统的设备可以通过将应用数据自动备份到 Google Drive,帮助确保提供卓越的用户体验。 - -这样,即使用户更换或升级设备,应用数据也可自动恢复。 - -

    - -

    - 对于运行 Android M 预览版的设备中所有已安装的应用,均可启用自动备份。无需额外提供应用代码。 -该系统允许用户选择禁止自动数据备份。 -此外,您还可以选择限制要备份应用中的哪些数据。 -

    - -

    - 本文介绍了新系统行为,阐述了如何指定要为应用备份哪些数据。 - -

    - -

    概览

    - -

    - 自动备份功能可以通过将应用在用户设备中创建的数据上传到用户的 Google Drive 帐户并进行加密,来保留这些数据。 -您或用户无需为数据存储付费,且保存的数据不计入用户个人的 Drive 配额。 -在 M 预览版运行期间,用户可针对每个 Android 应用存储多达 25MB 的数据。 - -

    - -

    - 当设备处于空闲、充电以及连接至 Wi-Fi 网络时,会每 24 小时自动备份一次数据。 -满足上述这些条件后,备份管理器服务会将所有可用的备份数据上传至云端。 -当用户改用新设备或卸载并重新安装备份的应用时,恢复操作会将备份的数据复制到新安装的应用的数据目录中。 - - -

    - -

    - 注:如果应用使用旧版 Android 备份服务,则此新行为不适用,不过现有的备份行为会照常运行。 - - -

    - - -

    自动排除的数据文件

    - -

    - 并非所有应用数据均应备份,例如临时文件和缓存文件就无需备份,因此自动备份服务会默认排除如下一些特定的数据文件: - -

    - -
      -
    • 通过 {@link android.content.Context#getCacheDir - getCacheDir()} 和 {@link android.content.ContextWrapper#getCodeCacheDir getCodeCacheDir()} - 方法所引用的目录中的文件 -
    • - -
    • 位于外部存储中的文件,除非这些文件是驻留在通过 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} - 方法所引用的目录中 -
    • - -
    • 位于通过 -{@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()} 方法所引用的目录中的文件 -
    • -
    - -

    配置数据备份

    - -

    - 除了上一部分中列出的自动排除的文件外,凡 M 预览版设备中安装的任何应用所创建的数据均会备份。 -您可以使用应用清单文件中的设置,进一步限制和配置应用中的哪些数据需要备份。 - -

    - -

    包括或排除数据

    - -

    - 根据应用所需的数据及其保存方式,您可能需要针对包括或排除哪些特定的文件或目录设置具体的规则。 -自动备份服务支持使用 XML 配置文件和应用清单文件来设置这些备份规则。 - -您可以在应用清单文件中指定备份架构配置文件,如下例所示: - -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    -        xmlns:tools="http://schemas.android.com/tools"
    -        package="com.my.appexample">
    -    <uses-sdk android:minSdkVersion="MNC"/>
    -    <uses-sdk android:targetSdkVersion="MNC"/>
    -    <app ...
    -        android:fullBackupContent="@xml/mybackupscheme">
    -    </app>
    -    ...
    -</manifest>
    -
    - -

    - 在此示例代码中,android:fullBackupContent 属性指定了一个 XML 文件。该文件名为 -mybackupscheme.xml,位于应用开发项目的 res/xml/ 目录中。 -此配置文件包括关于要备份哪些文件的规则。 -下列示例代码显示了将某一特定文件排除在备份之外的配置文件: - -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<full-backup-content>
    -    <exclude domain="database" path="device_info.db"/>
    -</full-backup-content>
    -
    - -

    - 此示例备份配置仅将一个特定数据库文件排除在备份之外。 - 所有其他文件均予以备份。 -

    - -

    备份配置语法

    - -

    - 您可以通过备份服务配置指定备份中要包括或排除哪些文件。 -数据备份配置 xml 文件的语法如下: -

    - -
    -<full-backup-content>
    -    <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
    -    <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
    -</full-backup-content>
    -
    - -

    - 您可以使用下列元素和属性指定备份中要包括或排除哪些文件: - -

    - -
      -
    • - <include>。如果您想指定一组要备份的资源,而不是默认由系统备份应用中的所有数据,请使用此元素。 -如果您指定了 <include> 标记,系统将仅备份使用此元素指定的资源。 - - -
    • - -
    • - <exclude>。使用此元素指定一组要排除在备份之外的资源。 -除了使用此元素指定的资源外,系统将备份应用中的所有数据。 - -
    • - -
    • - domain. 您想要在备份中包括或排除的资源类型。您可为此属性指定的有效值包括: - -
    • - -
    • -
        -
      • - root。指定资源处于应用的根目录中 -
      • - -
      • - file。与通过 -{@link android.content.Context#getFilesDir getFilesDir()} 方法返回的目录中的某个资源相对应 -
      • - -
      • - database。与通过 -{@link android.content.Context#getDatabasePath getDatabasePath()} 方法或使用 -{@link android.database.sqlite.SQLiteOpenHelper} 类返回的某个数据库相对应 -
      • - -
      • - sharedpref。与通过 {@link android.content.Context#getSharedPreferences getSharedPreferences()} - 方法返回的某个 {@link android.content.SharedPreferences} 对象相对应 - -
      • - -
      • - external。指定资源位于外部存储中,且与通过 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} 方法返回的目录中的某个文件相对应 - -
      • - -
      • - path。您想要在备份中包括或排除的资源的文件路径 - -
      • -
      -
    • -
    - - -

    禁止数据备份

    - -

    - 通过在清单文件的应用元素中将 -android:allowBackup 属性设置为 false,您可选择阻止自动备份任何应用数据。 -此设置如下列示例代码所示: -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    -        xmlns:tools="http://schemas.android.com/tools"
    -        package="com.my.appexample">
    -    <uses-sdk android:minSdkVersion="MNC"/>
    -    <uses-sdk android:targetSdkVersion="MNC"/>
    -    <app ...
    -        android:allowBackup="false">
    -    </app>
    -    ...
    -</manifest>
    -
    - - -

    测试备份配置

    - -

    - 创建备份配置后,您应立即进行测试,确保应用能够保存数据,且数据能够正确恢复。 - -

    - - -

    启用备份日志记录

    - -

    - 要帮助确定备份功能如何解析 XML 文件,请在执行测试备份之前启用日志记录功能: - -

    - -
    -$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE
    -
    - -

    测试备份

    - -

    要手动运行备份,您首先必须通过调用下列命令初始化备份管理器: - -

    - -
    -$ adb shell bmgr run
    -
    - -

    - 然后,使用下列命令并以 <PACKAGE> 参数指定应用的软件包名称来手动备份应用: - -

    - -
    -$ adb shell bmgr fullbackup <PACKAGE>
    - - -

    测试恢复

    - -

    - 要在备份应用数据后手动启动数据恢复,请调用下列命令,并以 <PACKAGE> 参数指定应用的软件包名称: - -

    - -
    -$ adb shell bmgr restore <PACKAGE>
    -
    - -

    - 警告:执行恢复操作之前,此操作将阻止您的应用运行并擦除其数据。 - -

    - -

    - 您可以通过卸载并重新安装应用来启动数据恢复进程。应用安装完成后,系统会立即自动从云中恢复应用数据。 - -

    - - -

    诊断备份问题

    - -

    - 如果遇到问题,可通过在设置 > 备份中先关闭然后重新打开备份、将设备恢复出厂设置或者调用以下命令,来清除备份数据及关联的元数据: - - -

    - -
    $ adb shell bmgr wipe <TRANSPORT> <PACKAGE>
    - -

    - <TRANSPORT> 值必须以 com.google.android.gms 为前缀。 - 要获取传输列表,请调用下列命令: -

    - -
    $ adb shell bmgr list transports
    - -

    已知问题

    - -

    以下是自动备份服务的已知问题:

    - -
      -
    • Google 云消息传递:对于使用 Google 云消息传递推送通知的应用,存在一个已知问题,即:备份通过 Google 云消息传递注册所返回的注册 ID 时,可能会中断已恢复应用的推送通知。为此,应用安装到新设备后,务必查询 API,看是否具有新注册 ID,这一点至关重要。如果已备份旧注册 ID,则不会出现这种情况。 - - - - -为避免这种现象,请从已备份文件集中排除注册 ID。 - -
    • -
    diff --git a/docs/html-intl/intl/zh-cn/preview/behavior-changes.jd b/docs/html-intl/intl/zh-cn/preview/behavior-changes.jd deleted file mode 100644 index 39494b9041f988691bf1ede7c0590e6e08eac78c..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/behavior-changes.jd +++ /dev/null @@ -1,402 +0,0 @@ -page.title=行为变更 -page.keywords=预览版,sdk,兼容性 -sdk.platform.apiLevel=MNC -@jd:body - - - -

    M 开发者预览版除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更。 -本文重点介绍您应该了解并在开发应用时加以考虑的一些重要变更。 -

    - -

    如果您之前发布过 Android 应用,请注意您的应用可能受到这些平台变更的影响。 -

    - -

    运行时权限

    -

    本预览版引入了一种新的权限模型,如今,用户可直接在运行时管理应用权限。 -这种模型让用户能够更好地了解和控制权限,同时为应用开发者精简了安装和自动更新过程。用户可为所安装的各个应用分别授予或撤销权限。 - -

    - -

    对于面向 M 预览版开发的应用,请务必在运行时检查和请求权限。 -如需确定您的应用是否已被授予权限,请调用新增的 {@code Context.checkSelfPermission()} 方法。 -如需请求权限,请调用新增的 -{@code Activity.requestPermission()} 方法。即使您的应用不是针对 M 开发的,您也应该在新权限模型下测试您的应用。 -

    - -

    如需了解有关在您的应用中支持新权限模型的详细信息,请参阅开发者预览版的权限页面。 - -如需了解如何评估新模型对应用的影响,请参阅测试指南。 -

    - -

    节能优化

    -

    本预览版引入了针对空闲设备和应用的最新节能优化技术。

    - -

    打盹

    -

    如果设备未插入电源,并在屏幕关闭后的一段时间内保持不活动状态,则会进入打盹模式,在该模式下设备会尝试让系统保持休眠状态。 -在该模式下,设备会定期短时间恢复正常工作,以便进行应用同步,还可让系统执行任何挂起的操作。 - -

    - -

    在打盹模式下,您的应用会受到以下限制:

    -
      -
    • 网络访问被禁用,除非您的应用获得高优先级 Google Cloud Messaging 操作消息 (tickle)。 -
    • -
    • 唤醒锁会被忽略。
    • -
    • 通过 {@link android.app.AlarmManager} 类设置的闹铃会被禁用,但通过 {@link android.app.AlarmManager#setAlarmClock setAlarmClock()} 方法和 {@code AlarmManager.setAndAllowWhileIdle()} 方法设置的闹铃除外。 - -
    • -
    • 不执行 WiFi 扫描
    • -
    • 不允许运行同步适配器和 {@link android.app.job.JobScheduler} 的同步和作业。 -
    • -
    -

    -

    当设备退出打盹模式时,它会执行任何挂起的作业和同步。

    -

    您可以通过将运行 M 预览版的设备与您的开发计算机相连,并调用以下命令来测试此功能: - -

    -
    -$ adb shell dumpsys battery unplug
    -$ adb shell dumpsys deviceidle step
    -$ adb shell dumpsys deviceidle -h
    -
    -

    :即将推出的 - - Google Cloud Messaging 版本允许您指定高优先级消息。 -如果您的应用收到高优先级 GCM 消息,即使设备处于打盹模式,系统也会向其授予短时间的网络访问权限。 - -

    - -

    如需了解在您的应用中测试打盹模式的技巧,请参阅 -测试指南。 -

    - -

    应用待机

    -

    使用本预览版时,系统会在应用未被主动使用时确定其处于空闲状态。 -除非系统检测到以下信号之一,否则将在一段时间后将您的应用视为处于空闲状态: -

    - -
      -
    • 用户显式启动应用。
    • -
    • 应用当前有一个进程位于前台(表现为活动或前台服务形式,或被另一活动或前台服务占用)。 -
    • -
    • 应用生成用户可在锁屏或通知托盘中看到的通知。 -
    • -
    • 用户通过设置显式请求不对应用进行优化。 -
    • -
    - -

    如果设备未插入电源,系统会将被视为处于空闲状态的应用的网络访问禁用,并暂停其同步和作业。 -当设备插入电源时,系统将允许这些应用访问网络并执行任何挂起的作业和同步。 -如果设备长时间处于空闲状态,系统将按每天大约一次的频率允许空闲状态的应用访问网络。 -

    - -

    您可以通过将运行 M 预览版的设备与您的开发计算机相连,并调用以下命令来测试此功能: - -

    -
    -$ adb shell dumpsys battery unplug
    -$ adb shell am set-idle <packageName> true
    -$ adb shell am set-idle <packageName> false
    -$ adb shell am get-idle <packageName>
    -
    - -

    :即将推出的 - - Google Cloud Messaging (GCM) 版本允许您指定高优先级消息。 -如果您的应用收到高优先级 GCM 消息,即使其处于空闲状态,也会被授予短时间的网络访问权限。 - -

    - -

    如需了解在您的应用中测试应用待机的技巧,请参阅 -测试指南。 -

    - -

    可采用的存储设备

    -

    -使用本预览版时,用户可以采用 SD 卡等外部存储设备。采用外部存储设备可加密和格式化设备,使其具有类似内部存储设备的行为。 -用户可以利用此特性在存储设备之间移动应用及其私有数据。 -移动应用时,系统会遵守清单文件中的 -{@code android:installLocation} - 首选项。 -

    - -

    请注意,在内部存储设备与外部存储设备之间移动应用时,如果您的应用访问以下 API 或字段,它们返回的文件路径将会动态变化。郑重建议:在生成文件路径时,请始终动态调用这些 API。请勿使用硬编码文件路径或之前生成的永久性完全限定文件路径。 - - -

    - -
      -
    • {@link android.content.Context} 方法: -
        -
      • {@link android.content.Context#getFilesDir() getFilesDir()}
      • -
      • {@link android.content.Context#getCacheDir() getCacheDir()}
      • -
      • {@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}
      • -
      • {@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}
      • -
      • {@link android.content.Context#getDir(java.lang.String,int) getDir()}
      • -
      • {@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}
      • -
      • {@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}
      • -
      • {@link android.content.Context#getPackageCodePath() getPackageCodePath()}
      • -
      • {@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}
      • -
      -
    • -
    • {@link android.content.pm.ApplicationInfo} 字段: -
        -
      • {@link android.content.pm.ApplicationInfo#dataDir dataDir}
      • -
      • {@link android.content.pm.ApplicationInfo#sourceDir sourceDir}
      • -
      • {@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}
      • -
      • {@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}
      • -
      • {@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}
      • -
      • {@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}
      • -
      -
    • -
    - -

    如需在开发者预览版中调试此功能,您可以将一个 USB 驱动器通过一根 USB On-The-Go (OTG) 电缆连接到 Android 设备并运行以下命令启用对该 USB 驱动器的采用: -

    - -
    -$ adb shell sm set-force-adoptable true
    -
    - -

    取消支持 Apache HTTP 客户端

    -

    本预览版取消了对 Apache HTTP 客户端的支持。如果您的应用使用该客户端,并以 Android 2.3(API 级别 9)或更高版本为目标平台,请改用 {@link java.net.HttpURLConnection} 类。 - -此 API 效率更高,因为它可以通过透明压缩和响应缓存减少网络使用,并可最大限度降低耗电量。 -如需继续使用 Apache HTTP API,您必须先在 {@code build.gradle} 文件中声明以下编译时依赖项: - -

    -
    -android {
    -    useLibrary 'org.apache.http.legacy'
    -}
    -
    -

    Android 正在从使用 OpenSSL 库转向使用 -BoringSSL - 库。如果您要在应用中使用 Android NDK,请勿链接到并非 NDK API 组成部分的加密库,如 {@code libcrypto.so} 和 {@code libssl.so}。 -这些库并非公共 API,可能会在不同版本和设备上毫无征兆地发生变化或出现故障。此外,您还可能让自己暴露在安全漏洞的风险之下。 - -请改为修改原生代码,以通过 JNI 调用 Java 加密 API,或静态链接到您选择的加密库。 - -

    - -

    音频管理器变更

    -

    不再支持通过 {@link android.media.AudioManager} - 类直接设置音量或将特定音频流静音。{@link android.media.AudioManager#setStreamSolo(int,boolean) -setStreamSolo()} 方法已弃用,您应该改为调用 -{@code AudioManager.requestAudioFocus()} 方法。类似地, -{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()} 方法也已弃用,请改为调用 {@code AudioManager.adjustStreamVolume()} 方法并传入方向值 {@code ADJUST_MUTE} 或 {@code ADJUST_UNMUTE}。 - -

    - -

    文本选择

    - - - -

    现在,当用户在您的应用中选择文本时,您可以在一个浮动工具栏中显示“剪切”、“复制”和“粘贴”等文本选择操作。 - -其在用户交互实现上与为单个视图启用上下文操作模式中所述的上下文操作栏类似。 - - -

    - -

    如需实现可用于文本选择的浮动工具栏,请在您的现有应用中做出以下更改: -

    -
      -
    1. 在 {@link android.view.View} 对象或 {@link android.app.Activity} 对象中,将 -{@link android.view.ActionMode} 调用从 -{@code startActionMode(Callback)} 更改为 {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}。
    2. -
    3. 改为使用 {@code ActionMode.Callback} 的现有实现扩展 -{@code ActionMode.Callback2}。
    4. -
    5. 替代 {@code Callback2.onGetContentRect()} 方法,用于提供 {@link android.graphics.Rect} 内容对象(如文本选择矩形)在视图中的坐标。 -
    6. -
    7. 如果矩形的定位不再有效,并且这是唯一需要声明为无效的元素,请调用 {@code ActionMode.invalidateContentRect()} 方法。 -
    8. -
    - -

    请注意,如果您使用 - Android Support Library 22.2 修订版,浮动工具栏不向后兼容,默认情况下 appcompat 会获得对 {@link android.view.ActionMode} 对象的控制权。 - -这会禁止显示浮动工具栏。如需在 -{@link android.support.v7.app.AppCompatActivity} 中启用 -{@link android.view.ActionMode} 支持,请调用 -{@code android.support.v7.app.AppCompatActivity.getDelegate()},然后对返回的 -{@link android.support.v7.app.AppCompatDelegate} 调用 -{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()},并将输入参数设置为 {@code false}。 -此调用会将 {@link android.view.ActionMode} 对象的控制权交还给框架。 -在运行 M 预览版的设备上,框架可以支持 -{@link android.support.v7.app.ActionBar} 模式或浮动工具栏模式;而在运行 M 预览版之前版本的设备上,框架仅支持 {@link android.support.v7.app.ActionBar} 模式。 -

    - -

    Android 密钥库变更

    -

    使用本预览版时, -Android 密钥库提供程序不再支持 DSA, -但仍支持 ECDSA。

    - -

    禁用或重置安全锁屏时(例如,由用户或设备管理员执行此类操作时),系统将不再删除需要闲时加密的密钥, -但在上述事件期间会删除需要闲时加密的密钥。 -

    - -

    Wi-Fi 和网络连接变更

    - -

    本预览版对 Wi-Fi 和网络连接 API 引入了以下行为变更。

    -
      -
    • 现在,您的应用只能更改由您创建的 {@link android.net.wifi.WifiConfiguration} 对象的状态。 -系统不允许您修改或删除由用户或其他应用创建的 -{@link android.net.wifi.WifiConfiguration} 对象; -
    • -
    • -在之前的版本中,如果应用利用带有 -{@code disableAllOthers=true} 设置的 -{@link android.net.wifi.WifiManager#enableNetwork(int,boolean) enableNetwork()} 强制设备连接特定 Wi-Fi 网络,设备将会断开与蜂窝数据网络等其他网络的连接。 -在本预览版中,设备不再断开与上述其他网络的连接。如果您的应用的 {@code targetSdkVersion} 为 {@code “20”} 或更低,则会固定连接所选 Wi-Fi 网络。 - -如果您的应用的 {@code targetSdkVersion} 为 {@code “21”} 或更高,请使用多网络 API(如 -{@link android.net.Network#openConnection(java.net.URL) openConnection()} 、 -{@link android.net.Network#bindSocket(java.net.Socket) bindSocket()} 和新增的 -{@code ConnectivityManager.bindProcessToNetwork()} 方法)来确保通过所选网络传送网络流量。 - -
    • -
    - -

    相机服务变更

    -

    在本预览版中,相机服务中共享资源的访问模式已从之前的“先到先得”访问模式更改为高优先级进程优先的访问模式。 - -对服务行为的变更包括:

    -
      -
    • 根据客户端应用进程的“优先级”授予对相机子系统资源的访问权,包括打开和配置相机设备。 -带有对用户可见活动或前台活动的应用进程一般会被授予较高的优先级,从而使相机资源的获取和使用更加可靠; - -
    • -
    • 当高优先级的应用尝试使用相机时,系统可能会“驱逐”正在使用相机客户端的低优先级应用。 -在已弃用的 {@link android.hardware.Camera} API 中,这会导致系统为被驱逐的客户端调用 -{@link android.hardware.Camera.ErrorCallback#onError(int,android.hardware.Camera) onError()}。 - -在 {@link android.hardware.camera2 Camera2} API 中,这会导致系统为被驱逐的客户端调用 -{@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected(android.hardware.camera2.CameraDevice) onDisconnected()}; -
    • -
    • 在配备相应相机硬件的设备上,不同的应用进程可同时独立打开和使用不同的相机设备。 -但现在,如果在多进程用例中同时访问相机会造成任何打开的相机设备的性能或能力严重下降,相机服务会检测到这种情况并禁止同时访问。 - -即使并没有其他应用直接尝试访问同一相机设备,此变更也可能导致低优先级客户端被“驱逐”。 - - -
    • -
    • -更改当前用户会导致之前用户帐户拥有的应用内活动相机客户端被驱逐。 -对相机的访问仅限于访问当前设备用户拥有的用户个人资料。举例来说,这意味着,当用户切换到其他帐户后,“来宾”帐户实际上无法让使用相机子系统的进程保持运行状态。 - - -
    • -
    - -

    ART 运行时

    -

    ART 运行时现在可正确实现 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法的访问规则。此变更修正了之前版本中 Dalvik 无法正确检查访问规则的问题。如果您的应用使用 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法,并且您想替代访问检查,请调用 -{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()} 方法(将输入参数设置为 {@code true})。 - - - -如果您的应用使用 -v7 appcompat 库或 -v7 recyclerview 库,则您必须更新应用以使用这些库的最新版本。 -否则,请务必更新从 XML 引用的任何自定义类,以便能够访问它们的类构造函数。 -

    - -

    本预览版更新了动态链接程序的行为。动态链接程序现在可以识别库的 {@code soname} 与其路径之间的差异( -公开 bug 6670),并且现在已实现了按 {@code soname} 搜索。 - - -之前包含错误的 {@code DT_NEEDED} 条目(通常是开发计算机文件系统上的绝对路径)却仍工作正常的应用,如今可能会出现加载失败。 -

    - -

    现已正确实现 {@code dlopen(3) RTLD_LOCAL} 标志。请注意, -{@code RTLD_LOCAL} 是默认值,因此不显式使用 -{@code RTLD_LOCAL} 的 {@code dlopen(3)} 调用将受到影响(除非您的应用显式使用 {@code RTLD_GLOBAL})。使用 -{@code RTLD_LOCAL} 时,在随后通过调用 -{@code dlopen(3)} 加载的库中并不能使用这些符号(这与由 {@code DT_NEEDED} 条目引用的情况截然不同)。

    -

    - -

    APK 验证

    -

    该平台现在执行的 APK 验证更为严格。如果在清单文件中声明的文件在 APK 中并不存在,该 APK 将被视为已损坏。 -移除任何内容后必须重新签署 APK。 -

    - -

    Android for Work 变更

    -

    本预览版包含下列针对 Android for Work 的行为变更:

    -
      -
    • 个人上下文中的工作联系人。Google Dialer Call Log 现在会在用户查看通话记录时显示工作联系人。将 {@code DevicePolicyManager.setCrossProfileCallerIdDisabled()} 设置为 {@code true} 可在 Google Dialer Call Log 中隐藏托管配置文件联系人。 - - -仅当您将 {@code DevicePolicyManager.setBluetoothContactSharingDisabled()} 设置为 {@code false} 时,才可以通过蓝牙将工作联系人随个人联系人一起显示给设备。 - -默认情况下,它设置为 {@code true}。 - -
    • -
    • WiFi 配置移除:现在,当删除某个托管配置文件时,将会移除由配置文件所有者添加的 WiFi 配置(例如,通过调用 -{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration) -addNetwork()} 方法添加的配置); -
    • -
    • WiFi 配置锁定:用户无法再修改或删除任何由活动设备所有者创建的 WiFi 配置。 -用户仍可创建和修改其自己的 WiFi 配置,前提是尚未针对该用户设置 {@link android.os.UserManager} 常量 -{@link android.os.UserManager#DISALLOW_CONFIG_WIFI}。 -
    • -
    • 通过添加 Google 帐户下载工作策略控制器向托管环境以外的设备添加需要通过工作策略控制器 (WPC) 管理的 Google 帐户时,帐户添加流程现在会提示用户安装相应的 WPC。在设备初始设置向导中通过设置 > 帐户添加帐户时,也会出现此行为。 - - - -
    • -
    • 对特定 DevicePolicyManager API 行为的变更:调用 {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName,boolean) setCameraDisabled()} 方法只会影响调用该方法的用户的相机;从托管配置文件调用它不会影响主用户运行的相机应用。 - - -此外, -{@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,int) setKeyguardDisabledFeatures()} 方法现在除了可供设备所有者使用外,还可供配置文件所有者使用。 -配置文件所有者可设置以下键盘锁限制: - -
        -
      • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} 和 -{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_FINGERPRINT},它们影响配置文件上级用户的键盘锁设置。 -
      • -
      • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS},它只影响应用在托管配置文件中生成的通知。 -
      • -
      -
    • -
    diff --git a/docs/html-intl/intl/zh-cn/preview/download.jd b/docs/html-intl/intl/zh-cn/preview/download.jd deleted file mode 100644 index c7ba9df279e79423bffa798f46b79ec4a26035e1..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/download.jd +++ /dev/null @@ -1,360 +0,0 @@ -page.title=下载 -page.image=images/cards/card-download_16-9_2x.png - -@jd:body - -
    - - - - -
    - -
    -
    -

    本文内容

    -
      -
    1. Android 6.0 SDK
    2. -
    3. 开发者文档
    4. -
    5. 硬件系统映像
    6. -
    -

    Legacy downloads

    -
      -
    1. Developer Preview 1
    2. -
    3. Developer Preview 2
    4. -
    -
    -
    - - -

    - Android M 预览版 SDK 包括开发工具、Android 系统文件和库文件,旨在帮助您在下一版本的平台中测试您的应用以及该平台提供的新 API。 -本文旨在介绍如何获得“预览版”的可下载组件,以便测试您的应用。 - -

    - - -

    Android 6.0 SDK

    - -

    - 预览版 SDK 可通过 Android SDK 管理器下载。如需了解有关下载和配置预览版 SDK 的详细信息,请参阅设置预览版 SDK。 - -

    - - -

    开发者文档

    - -

    - 开发者文档下载软件包提供详细的 API 参考信息和“预览版”的 API 差异报告。 -

    - - - - - - - - - - -
    DescriptionDownload / Checksums
    Android M Preview 3
    Developer Docs
    m-preview-3-developer-docs.zip
    - MD5: d99b14b0c06d31c8dfecb25072654ca3
    - SHA-1: 9cefeeda07676130da606a1796e1c00fffc667c1 -
    - - -

    硬件系统映像

    - -

    - 这些系统映像允许您在实际设备上安装预览版平台,以便进行测试。 -通过使用这些映像之一配置设备,您可以安装并测试您的应用,以了解其在下一版本平台上的性能表现。 -在设备上安装系统映像的过程会删除设备中的所有数据,因此您应该在安装系统映像之前备份数据。 - - -

    - -

    - 警告:以下 Android 系统映像是预览版,可能会随时发生变化。您对这些系统映像的使用受 Android SDK 预览版许可协议的制约。 -Android 预览版系统映像并非稳定版本,可能包含会对您的计算机系统、设备和数据造成损害的错误和缺陷。 - -Android 预览版系统映像未经过与出厂操作系统相同的测试,可能会导致您的手机和安装的服务与应用停止工作。 - - -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    DeviceDownload / Checksums
    Nexus 5 (GSM/LTE)
    "hammerhead"
    hammerhead-MPA44I-preview-2ebbc049.tgz
    - MD5: 91a924fb0c9f8e716e3b4c9954fd0dbb
    - SHA-1: 2ebbc049b68c4da8baeee3e42bb94d7a965ba4a3 -
    Nexus 6
    "shamu"
    shamu-MPA44I-preview-62b9c486.tgz
    - MD5: ac6e58da86125073d9c395257fd42664
    - SHA-1: 62b9c486fd7a5020e228d53ca5acd5c1857e48ff -
    Nexus 9
    "volantis"
    volantis-MPA44I-preview-5c30a6e2.tgz
    - MD5: 7f83768757913d3fea945a661020d185
    - SHA-1: 5c30a6e2acd11a81f4105b12d23ff654f534f699 -
    Nexus Player
    "fugu"
    fugu-MPA44I-preview-2860040a.tgz
    - MD5: 438da8d37da9e341a69cfb16a4001ac5
    - SHA-1: 2860040a326582f1ff5f702bf9a1ef002717fc98 -
    - -

    将映像安装到设备

    - -

    - 要使用设备映像进行测试,您必须将其安装到兼容设备上。请按照下面的说明安装系统映像: - -

    - -
      -
    1. 下载并解压此处列出的系统映像包之一。
    2. -
    3. 备份设备中您希望予以保留的任何数据。
    4. -
    5. 按照 -developers.google.com/android - 中的说明将映像刷入设备中。
    6. -
    - -

    - 注:为开发设备刷入预览版系统映像之后,它将通过无线 (OTA) 更新自动升级到下一个预览版本。 - -

    - -

    将设备还原至出厂规格

    - -

    - 如果您想要卸载预览版并将设备还原至出厂规格,请转到 -developers.google.com/android 并下载要为设备刷入的映像。 -按照该页面上的说明将映像刷入设备中。 - -

    - -
    - -
    - - - - diff --git a/docs/html-intl/intl/zh-cn/preview/features/app-linking.jd b/docs/html-intl/intl/zh-cn/preview/features/app-linking.jd deleted file mode 100644 index 77a532f7cde19f00695c81250efd3e0c66d21e3d..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/features/app-linking.jd +++ /dev/null @@ -1,123 +0,0 @@ -page.title=应用链接 -page.image=images/cards/card-app-linking_2x.png -page.keywords=应用链接, 深层链接, 意向 -@jd:body - - - -

    - Android 意向系统是一种让应用能够处理内容和请求的灵活机制。 - 可能会有多个应用在其意向过滤器中声明一致的 URI 模式。如果用户点击的 Web 链接没有默认的启动处理程序,平台可能会显示一个对话框,让用户从一系列已声明一致意向过滤器的应用中进行选择。 - - -

    - -

    - Android M 开发者预览版引入了对应用链接的支持,后者在现有链接处理方式基础上进行了改进,允许应用开发者将应用与他们拥有的 Web 域进行关联。 -当开发者创建此关联时,平台可以自动确定在处理特定 Web 链接时默认使用的应用,略过询问用户的步骤。 - - -

    - - -

    声明网站关联

    - -

    - 网站所有者必须声明与应用的关联才能建立应用链接。网站所有者可以通过在域上众所周知的位置承载一个名为 {@code statements.json} 的 JSON 文件声明与应用的关系: - - -

    - -
    http://<domain>:<optional port>/.well-known/statements.json
    - -

    - 注: - 在 M 开发者预览版运行期间,系统会通过 HTTP 协议对该 JSON 文件进行验证。当平台正式发布后,系统将通过 HTTPS 加密协议对该文件进行验证。 - -

    - -

    - 该 JSON 文件指定应作为该域下 URL 默认处理程序使用的 Android 应用。 -它根据以下字段标识该应用: -

    - -
      -
    • {@code package_name}:该应用的清单文件中声明的软件包名称;
    • - -
    • {@code sha256_cert_fingerprints}:应用签名证书的 SHA256 指纹。 - 您可以利用 Java 密钥工具,通过以下命令生成该指纹: -
      keytool -list -v -keystore my-release-key.keystore
      -
    • -
    - -

    - 以下文件清单显示的是一个 -{@code statements.json} 文件的内容和格式示例: -

    - -
    -[{
    -  "relation": ["delegate_permission/common.handle_all_urls"],
    -  "target": {
    -    "namespace": "android_app",
    -    "package_name": "<package name>",
    -    "sha256_cert_fingerprints": ["6C:EC:C5:0E:34:AE....EB:0C:9B"]
    -  }
    -}]
    -
    - - - - -

    - 应用可以请求平台自动根据相应 Web 域上承载的 {@code statements.json} 文件验证由其意向过滤器数据元素内的主机名称定义的任何应用链接。 - -要请求应用链接验证,请按以下清单文件代码段中所示向清单文件中所需的每个意向过滤器添加一个 {@code android:autoVerify} - 属性: - -

    - -
    -<activity ...>
    -    <intent-filter android:autoVerify="true">
    -        <action android:name="android.intent.action.VIEW" />
    -        <category android:name="android.intent.category.DEFAULT" />
    -        <category android:name="android.intent.category.BROWSABLE" />
    -        <data android:scheme="http" android:host="www.android.com" />
    -        <data android:scheme="https" android:host="www.android.com" />
    -    </intent-filter>
    -</activity>
    -
    - -

    - 如果应用清单文件中存在 {@code android:autoVerify} 属性,平台会在安装应用时尝试验证应用链接。 -如果平台无法成功验证应用链接,则不会将应用设置为处理 Web 链接的首选应用。 -用户下一次打开其中一个链接时,平台将退回原有模式,向用户显示一个对话框。 - - -

    - -

    - 注:在测试时,如果验证失败,但用户已经使用系统的“设置”应用显式允许应用在不询问用户的情况下打开受支持的链接,则可能会出现误报。在这种情况下,不会显示对话框,链接会直接指向您的应用,不过这完全是由于用户设置的缘故,并不是因为验证成功。 - - - -

    - - -

    管理应用链接设置

    - -

    - 用户可以更改应用链接设置,让系统按首选方式处理 URL。您可以在系统“设置”应用的设置 > 应用 > 应用信息 > 默认打开下查看和管理应用链接。 - - -

    diff --git a/docs/html-intl/intl/zh-cn/preview/features/runtime-permissions.jd b/docs/html-intl/intl/zh-cn/preview/features/runtime-permissions.jd deleted file mode 100644 index 8e361fb6c9b189c6198ed5dd9a0252b59c2a2f8b..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/features/runtime-permissions.jd +++ /dev/null @@ -1,794 +0,0 @@ -page.title=权限 -page.tags=previewresources, androidm -page.keywords=permissions, runtime, preview -page.image={@docRoot}preview/features/images/permissions_check.png -@jd:body - - -
    -
    -

    内容快览

    -
      -
    • 如果您的应用主要面向 M 预览版 SDK,则会在运行时(而非安装时)提示用户授予权限。 -
    • -
    • 用户可以随时从应用“设置”屏幕撤销权限。 -
    • -
    • 每次运行时,应用均需检查自身是否具备所需的权限。 -
    • -
    - -

    本文内容

    -
      -
    1. 概览
    2. -
    3. 为运行时权限编码
    4. -
    5. 测试运行时权限
    6. -
    7. 最佳做法
    8. -
    - - - - -
    -
    - - -

    - M 开发者预览版引入了一种新的应用权限模型,旨在简化用户安装和升级应用的过程。 -如果在 M 预览版上运行的应用支持新权限模型,则用户无需在安装或升级应用时授予任何权限。相反,应用会根据需要请求权限,且系统将向用户显示一个请求权限的对话框。 - - - - -

    - -

    - 如果应用支持新权限模型,则仍可在运行旧版 Android 的设备上使用旧权限模型安装并运行此应用。 - - -

    - -

    - 概览 -

    - -

    - 通过 M 开发者预览版,该平台引入了新的应用权限模型。 -以下概述了此新模型的主要组件: -

    - -
      -
    • - 声明权限:应用使用清单文件声明其所需的所有权限,就像在早期的 Android 平台中一样。 - -
    • - -
    • - 权限组:权限根据相应的功能分为若干 -权限组。例如, -CONTACTS 权限组包含读取和写入用户联系人和个人资料信息的权限。 - -
    • - -
    • -

      安装时授予的有限权限:当用户安装或更新应用时,系统将授予应用所请求的属于 {@link - android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL} 的所有权限。 - - - 例如,闹铃和 Internet 权限属于 {@link - android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL},因此系统将在安装应用时自动授予这些权限。 - -

      - -

      此外,系统还可以授予应用签名和系统权限,如系统应用和签名权限中所述。 - -系统不会在安装应用时提示用户授予任何权限。 -

      -
    • - -
    • - 用户在运行时授予权限:当应用请求权限时,系统将向用户显示一个对话框,然后调用应用的回调函数来通知它是否已授予权限。 - -如果用户授予某项权限,则应用将获得应用清单文件中声明的、该权限功能区域中的所有权限。 - - -
    • - -
    - -

    - 对于需要权限的功能,此权限模型将改变应用的行为方式。 -以下概述了您调整此模型时所应遵循的开发实践: - -

    - -
      - -
    • - 始终检查权限:当应用必须执行任何需要权限的操作时,应先检查它是否已具备该权限。 - -如果没有,则请求授予该权限。 - -
    • - -
    • - 妥善处理权限不足的情况:如果应用未被授予适当的权限,则应正常处理失败情况。 - - 例如,如果只有新增的功能需要该权限,则应用可以禁用该功能。 -如果该权限对于应用正常运行至关重要,则应用可能会禁用其所有功能,并通知用户需要授予该权限。 - - -
    • - -
      - -

      - 图 1.应用“设置”中的“权限”屏幕。 -

      -
      - -
    • - 权限可撤销:用户可以随时撤销应用的权限。 -即使用户禁用应用的权限,应用也不会收到通知。 -再次强调:您的应用应在执行任何受限操作之前验证是否具备所需的权限。 - -
    • -
    - -

    - 注:如果应用主要面向 M 开发者预览版,则必须使用新权限模型。 - -

    - -

    - 截至 M 开发者预览版发布,并非所有 Google 应用均已完全实现新权限模型。 -Google 会在 M 开发者预览版运行期间更新这些应用,以便严格遵守权限切换设置。 - - -

    - -

    - 注:如果您的应用拥有自己的 API 接口,请先确保调用方具备访问该数据所需的必要权限,然后再代理权限。 - - -

    - -

    - 系统应用和签名权限 -

    - -

    - 通常,当用户安装应用时,系统仅授予应用 - {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL - PROTECTION_NORMAL}。但在某些情况下,系统将授予应用更多权限: - -

    - -
      -
    • 如果应用是系统映像的一部分,则系统会自动授予该应用清单文件中列出的所有权限。 - -
    • - -
    • 如果应用请求提供清单文件中属于 {@link - android.content.pm.PermissionInfo#PROTECTION_SIGNATURE PROTECTION_SIGNATURE} 的权限,且该应用已使用与声明这些权限的应用相同的证书进行签名,则系统将在安装请求权限的应用时向其授予这些权限。 - - - -
    • -
    - -

    - 在这两种情况下,用户仍可随时撤销权限,具体方法是:转到系统的设置屏幕,然后选择应用 > - - 应用名称 > 权限。应用应在运行时继续检查权限,并根据需要请求权限。 - - -

    - -

    - 前后兼容性 -

    - -

    - 如果应用并非面向 M 开发者预览版,则即使是在 M 预览版设备上,该应用也会继续使用旧权限模型。 -当用户安装应用时,系统将要求用户授予应用清单文件中列出的所有权限。 - - -

    - -

    - 注:在运行 M 开发者预览版的设备上,用户可以从应用的“设置”屏幕禁用任何应用(包括旧版应用)的权限。 - -如果用户禁用某旧版应用的权限,则系统将以静默方式禁用相应的功能。 -当应用尝试执行需要该权限的操作时,该操作不一定会导致出现异常。 - -相反,它可能会返回空数据集、报告错误或以其他方式表现出异常行为。 -例如,如果您未经许可查询日历,则该方法会返回空数据集。 - -

    - -

    - 如果您在未运行 M 预览版的设备上使用新权限模型安装应用,则系统将采用与其他任何应用相同的方式处理:系统会在安装应用时要求用户授予声明的所有权限。 - - - -

    - -

    - 注:对于预览版本,您必须将最低 SDK 版本设置为 M 预览版 SDK,才可使用预览版 SDK 进行编译。 -这意味着在开发者预览版运行期间,您无法在旧版平台上测试此类应用。 - - -

    - -

    权限与意向的比较

    - -

    - 许多情况下,您可以使用以下两种方式之一来让您的应用执行某项任务。 -您可以将应用设置为请求执行操作本身所需的权限。 -或者,您可以将应用设置为通过传送意向,让其他应用来执行任务。 - -

    - -

    - 例如,假设应用需要能够使用设备相机拍摄照片。 -应用可以请求 -android.permission.CAMERA 权限,以便允许其直接访问相机。 -然后,应用将使用 Camera API 控制相机并拍摄照片。 -利用此方法,您的应用能够完全控制摄影过程,并支持您将相机 UI 合并至应用中。 - - -

    - -

    - 但是,如果您无需此类控制,则可仅使用 {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} 意向来请求图像。 -启动该意向时,系统会提示用户选择相机应用(如果没有默认相机应用),然后该应用将拍摄照片。 - -该相机应用会将照片返回给应用的 {@link - android.app.Activity#onActivityResult onActivityResult()} 方法。 -

    - -

    - 同样,如果您需要打电话、访问用户的联系人或要执行其他操作,则可通过创建适当的意向来完成,或者您可以请求相应的权限并直接访问相应的对象。 - -每种方法各有优缺点。 - -

    - -

    - 如果使用权限: -

    - -
      -
    • 应用可在您执行操作时完全控制用户体验。 -但是,如此广泛的控制会增加任务的复杂性,因为您需要设计适当的 UI。 - -
    • - -
    • 当您首次执行操作时,系统会显示一次让用户授予权限的提示。 -之后,应用即可执行操作,不再需要用户进行其他交互。 -但是,如果用户不授予权限(或稍后撤销权限),则应用根本无法执行操作。 - - -
    • -
    - -

    - 如果使用意向: -

    - -
      -
    • 您无需为操作设计 UI。处理意向的应用将提供 UI。不过这意味着您无法控制用户体验。 - -用户可以与您从未见过的应用进行交互。 - -
    • - -
    • 如果用户没有适用于操作的默认应用,则系统会提示用户选择应用。如果用户未指定默认处理程序,则必须在每次执行此操作时额外处理一个对话框。 - - - -
    • -
    - -

    为运行时权限编码

    - -

    - 如果应用主要面向新的 M 开发者预览版,则您必须使用新权限模型。 -这意味着除了通过清单文件声明所需的权限以外,您还必须检查应用运行时是否已有相应的权限,如果没有,则需要请求权限。 - - - -

    - -

    - 启用新权限模型 -

    - -

    - 要启用新的 M 开发者预览版权限模型,请将应用的 -targetSdkVersion 属性设置为 "MNC",并将 -compileSdkVersion 设置为 "android-MNC"。这样可启用所有新的权限功能。 - -

    - -

    - 对于预览版本,您必须将 minSdkVersion 设置为 -"MNC",才能使用预览版 SDK 进行编译。 -

    - -

    - 指定仅用于 M 预览版的权限 -

    - -

    - 您可以使用应用清单文件中的新 <uses-permission-sdk-m> 元素指明仅在 M 开发者预览版中需要某权限。 -如果您以这种方式声明权限,则每当在旧版设备上安装应用时,系统都不会提示用户或向应用授予权限。通过使用 <uses-permission-sdk-m> - 元素,您可以将新权限添加到更新后的应用版本,而不必强制用户在安装更新时授予权限。 - - - - - -

    - -

    - 如果应用在已安装 M 开发者预览版的设备上运行,则 -<uses-permission-sdk-m> 的行为与 -<uses-permission> 相同。 - 系统不会在安装应用时提示用户授予任何权限,且应用将根据需要请求权限。 - -

    - -

    - 提示授予权限 -

    - -

    - 如果应用使用新的 M 开发者预览版权限模型,则在运行 M 预览版的设备上首次启动应用时,系统不会要求用户授予所有权限。 - -相反,应用将根据需要请求权限。 -当应用请求某权限时,系统会向用户显示一个对话框。 - -

    - -

    - 如果应用在已安装 SDK 22 或更低版本的设备上运行,则应用将使用旧权限模型。 -当用户安装应用时,系统将提示他们授予应用在清单文件中请求的所有权限,但那些带有 <uses-permission-sdk-m> 标记的权限除外。 - - -

    - -

    检查运行应用的平台

    - -

    - 只有运行 M 开发者预览版的设备支持此权限模型。 -在调用其中任何方法之前,应用均应通过检查 {@link android.os.Build.VERSION#CODENAME - Build.VERSION.CODENAME} 的值来验证运行应用的平台。 - -如果设备正在运行 M 开发者预览版,则 -{@link android.os.Build.VERSION#CODENAME CODENAME} 为 "MNC"。 -

    - -

    检查应用是否具备所需的权限

    - -

    当用户尝试执行需要权限的操作时,应用将检查目前是否具备执行此操作所需的权限。 -为此,应用将调用 -Context.checkSelfPermission( -permission_name)。即便知道用户已授予该权限,应用也应执行此检查,因为用户可以随时撤销应用的权限。 - - -例如,如果用户需要使用应用拍摄照片,则应用将调用 -Context.checkSelfPermission(Manifest.permission.CAMERA)。 -

    - -

    - 表 1.权限和权限组。

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    权限组权限
    android.permission-group.CALENDAR -
      -
    • - android.permission.READ_CALENDAR -
    • -
    -
      -
    • - android.permission.WRITE_CALENDAR -
    • -
    -
    android.permission-group.CAMERA -
      -
    • - android.permission.CAMERA -
    • -
    -
    android.permission-group.CONTACTS -
      -
    • - android.permission.READ_CONTACTS -
    • -
    • - android.permission.WRITE_CONTACTS -
    • -
    • - android.permission.READ_PROFILE -
    • -
    • - android.permission.WRITE_PROFILE -
    • -
    -
    android.permission-group.LOCATION -
      -
    • - android.permission.ACCESS_FINE_LOCATION -
    • -
    • - android.permission.ACCESS_COARSE_LOCATION -
    • -
    -
    android.permission-group.MICROPHONE -
      -
    • - android.permission.RECORD_AUDIO -
    • -
    -
    android.permission-group.PHONE -
      -
    • - android.permission.READ_PHONE_STATE -
    • -
    • - android.permission.CALL_PHONE -
    • -
    • - android.permission.READ_CALL_LOG -
    • -
    • - android.permission.WRITE_CALL_LOG -
    • -
    • - com.android.voicemail.permission.ADD_VOICEMAIL -
    • -
    • - android.permission.USE_SIP -
    • -
    • - android.permission.PROCESS_OUTGOING_CALLS -
    • -
    -
    android.permission-group.SENSORS -
      -
    • - android.permission.BODY_SENSORS -
    • -
    -
      -
    • - android.permission.USE_FINGERPRINT -
    • -
    -
    android.permission-group.SMS -
      -
    • - android.permission.SEND_SMS -
    • -
    • - android.permission.RECEIVE_SMS -
    • -
    • - android.permission.READ_SMS -
    • -
    • - android.permission.RECEIVE_WAP_PUSH -
    • -
    • - android.permission.RECEIVE_MMS -
    • -
    • - android.permission.READ_CELL_BROADCASTS -
    • -
    -
    - -

    根据需要请求权限

    - -

    如果应用尚无所需的权限,则应用将调用 -Activity.requestPermissions(String[], int) 方法,请求提供一项或多项适当的权限。 -应用将传递所需的一项或多项权限,以及整数“请求代码”。 - - 此方法异步运行:它会立即返回,并且在用户响应对话框之后,系统会使用结果调用应用的回调方法,将应用传递的相同“请求代码”传递到 requestPermissions()。 - - -

    - -

    以下代码检查应用是否具备读取用户联系人所需的权限,并根据需要请求该权限: -

    - -
    -if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
    -        != PackageManager.PERMISSION_GRANTED) {
    -    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
    -            MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    -
    -    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
    -    // app-defined int constant
    -
    -    return;
    -}
    -
    - -

    处理权限请求响应

    - -

    - 当应用请求权限时,系统将向用户显示一个对话框。 -当用户响应时,系统将调用应用的 -Activity.onRequestPermissionsResult(int, String[], int[]) -,向其传递用户响应。应用需要替代该方法。回调会将您传递的相同请求代码传递给 -requestPermissions()。 -例如,如果应用请求 -READ_CONTACTS 访问权限,则可能采用以下回调方法: - -

    - -
    -@Override
    -public void onRequestPermissionsResult(int requestCode,
    -        String permissions[], int[] grantResults) {
    -    switch (requestCode) {
    -        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
    -            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    -
    -                // permission was granted, yay! do the
    -                // calendar task you need to do.
    -
    -            } else {
    -
    -                // permission denied, boo! Disable the
    -                // functionality that depends on this permission.
    -            }
    -            return;
    -        }
    -
    -        // other 'switch' lines to check for other
    -        // permissions this app might request
    -    }
    -}
    -
    - -

    如果用户授予权限,则系统会为应用授予应用清单文件为该功能区域列出的所有权限。 -如果用户拒绝请求,则您应采取适当的操作。 -例如,您可以禁用任何取决于此权限的菜单操作。 - - -

    - -

    - 当系统要求用户授予权限时,用户可以选择指示系统不再要求提供该权限。 -在这种情况下,当应用使用 requestPermissions() 请求该权限时,系统会立即拒绝此请求。 - -在这种情况下,如果用户已再次明确拒绝您的请求,则系统会以同样的方式调用您的 onRequestPermissionsResult()。 - -因此,您的应用不能假设用户采取了任何直接交互行为。 - -

    - -

    测试运行时权限

    - - -

    - 如果应用主要面向 M 开发者预览版,则您必须测试它是否正确处理权限。 -您不能假设应用在运行时具备任何特定的权限。 -应用首次启动时,它可能没有任何权限,且用户可以随时撤销或恢复权限。 - - -

    - -

    - 您应测试应用,确保它在所有权限情况下均可正常运行。 -通过 M 预览版 SDK,我们提供了新的 -Android - Debug Bridge (adb) 命令,支持您使用需要尝试的任何权限设置测试应用。 - -

    - -

    - 新 adb 命令和选项 -

    - -

    - M 预览版 SDK 平台工具提供了多个新命令,支持您测试应用处理权限的方式。 - -

    - -

    - 使用权限安装 -

    - -

    - 您可以使用 adb - install 命令的新 -g 选项,该选项将安装应用并授予其清单文件中列出的所有权限: - -

    - -
    -$ adb install -g <path_to_apk>
    -
    - -

    - 授予和撤销权限 -

    - -

    - 您可以使用新的 ADB 软件包管理器 (pm) 命令向已安装的应用授予权限和撤销其权限。此功能对于自动化测试非常有用。 - - -

    - -

    - 要授予权限,请使用软件包管理器的 grant 命令: -

    - -
    -$ adb pm grant <package_name> <permission_name>
    -
    - -

    - 例如,要向录音​​授予 com.example.myapp 软件包权限,请使用以下命令: - -

    - -
    -$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO
    -
    - -

    - 要撤销权限,请使用软件包管理器的 revoke 命令: -

    - -
    -$ adb pm revoke <package_name> <permission_name>
    -
    - -

    最佳做法

    - -

    - 新权限模型为用户带来更流畅的体验,让他们能够更轻松地安装应用,并得心应手地使用应用的各项功能。 - -为了充分利用该新模型,我们建议采用下列最佳做法。 - -

    - - -

    仅请求自己所需的权限

    - -

    - 每次您请求权限时,实际上是在强迫用户作出决定。 - 如果用户拒绝请求,则会减少应用的功能。 - 您应尽量减少提出这些请求的次数。 -

    - -

    - 例如,应用往往可以通过使用 -意向(而不是请求权限)获得所需的功能。 -如果应用需要使用手机的相机拍摄照片,则可使用 - {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE - MediaStore.ACTION_IMAGE_CAPTURE} 意向。当应用执行该意向时,系统会提示用户选择已安装的相机应用拍摄照片。 - - -

    - -

    - 不要让用户感到无所适从 -

    - -

    - 如果您让用户一次面对大量权限请求,用户可能会感到无所适从并因此退出应用。替代做法是,您应根据需要请求权限。 - - -

    - -

    - 某些情况下,您的应用可能绝对需要一项或多项权限。在这种情况下,合理的做法是,在应用启动之后立即请求所有权限。 - -例如,如果您运行摄影应用,则该应用需要访问设备的相机。 -当用户首次启动该应用时,不会对请求使用相机所需的权限感到惊讶。 - -但是,如果同一应用还具备与用户联系人共享照片的功能,则您不应在首次启动时请求用户提供该权限, - -而是等到用户尝试使用“共享”功能之后,再请求该权限。 - -

    - -

    - 如果应用提供了教程,则合理的做法是,在教程结束时请求提供应用的必要权限。 - -

    - -

    - 解释需要权限的原因 -

    - -

    - 系统在您调用 -requestPermissions() 时显示的权限对话框将说明应用所需的权限,但不会解释为何需要这些权限。 -在某些情况下,用户可能会感到困惑。 - 最好在调用 requestPermissions() 之前向用户解释应用需要权限的原因。 - -

    - -

    - 例如,摄影应用可能需要使用位置服务,以便能够为照片添加地理标签。 -通常,用户可能不了解照片能够包含位置信息,并且对摄影应用想要了解具体位置感到不解。 - -因此在这种情况下,应用最好在调用 -requestPermissions() 之前告知此功能的相关信息。 - -

    - -

    - 其中一种办法是将这些请求纳入应用教程。这样,教程可以依次显示应用的每项功能,并在显示每项功能时解释需要哪些相应的权限。 - -例如,摄影应用的教程可以演示其“与您的联系人共享照片”功能,然后告知用户需要为应用授予权限以便其查看用户的联系人。 - - -然后,应用可以调用 requestPermissions(),要求用户提供该访问权限。 -当然,并非所有用户都会按照教程操作,因此您仍需在应用的正常操作期间检查和请求权限。 - - -

    diff --git a/docs/html-intl/intl/zh-cn/preview/index.jd b/docs/html-intl/intl/zh-cn/preview/index.jd deleted file mode 100644 index caed4cb117a97ed1b1a5726294a4280c94c8f92f..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/index.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Android M 开发者预览版 -page.tags="preview", -meta.tags="preview, M preview", androidm -fullpage=true -section.landing=true -header.hide=1 -footer.hide=1 -@jd:body - -
    -
    -
    -
    - -
    -
    -

    Android M 开发者预览版

    -

    - 准备迎接 Android 的下一版本。在 Nexus 5、6、9 和 Player 中测试应用。 -了解新功能:运行权限瞌睡应用待机省电功能、新的协助技术等。 - - -

    - - - - 开始! -
    - - - Developer Preview 3 (final SDK) -
    -
    -
    -
    -
    -
    -
    - -
    -

    资源

    -
    - 帮助您的应用准备使用 Android M 的必备信息。 -
    - -
    - - -
    -
    - diff --git a/docs/html-intl/intl/zh-cn/preview/license.jd b/docs/html-intl/intl/zh-cn/preview/license.jd deleted file mode 100644 index 31363bec4e3bcdfa387800e2ed70e372b0454ae2..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/license.jd +++ /dev/null @@ -1,143 +0,0 @@ -page.title=许可协议 - -@jd:body - -

    -在开始使用 Android SDK 预览版之前,您必须同意下列条款和条件。正如下文所述,请注意:这是 Android SDK 的预览版本,可能随时更改,恕不另行通知,您使用此版本的风险由您自行承担。 -Android SDK 预览版并非稳定版本,可能包含会对您的计算机系统、设备和数据造成损害的错误和缺陷。 -

    - -

    -本协议是 Android SDK 预览版许可协议(以下称为“许可协议”)。 -

    -
    -1. 简介 - -1.1 Android SDK 预览版(在本许可协议中简称为“预览版”,具体包括 Android 系统文件、封装 API 以及预览版库文件(若可用))依据本许可协议的条款授权您使用。本许可协议在您与 Google 之间就您对“预览版”的使用构成具有法律约束力的合约。 - -1.2 “Android”是指以 Android 开源项目(项目网址为 http://source.android.com/,其内容会不时更新)名义提供、面向设备的 Android 软件栈。 - -1.3 “Google”是指 Google Inc.,是一家特拉华州公司,主要营业地位于:1600 Amphitheatre Parkway, Mountain View, CA 94043, United States。 - -2. 接受许可协议 - -2.1 要使用“预览版”,您必须先同意本许可协议。如果您不接受本许可协议,则不得使用“预览版”。 - -2.2 点击接受并/或使用“预览版”,即表示您特此同意本许可协议的条款。 - -2.3 如果依照美国或其他国家/地区(包括您居住或您使用“预览版”所在的国家/地区)的法律,您被禁止获取“预览版”,则您不得使用“预览版”,也不得接受本许可协议。 - -2.4 如果您将在贵公司或组织内部使用“预览版”,则您同意代表您的雇主或其他实体接受本许可协议的约束,并且您表示并保证您拥有完全的合法授权令您的雇主或上述实体受本许可协议的约束。如果您不具备必要的授权,则不得代表您的雇主或其他实体接受本许可协议或使用“预览版”。 - -3. Google 预览版许可 - -3.1 Google 依据本许可协议的条款授予您个人或在贵公司或组织内部有限使用“预览版”的免版税、不可转让、非独占性、不可再授权且可撤销的许可,其用途仅限开发在 Android 平台上运行的应用。 - -3.2 您同意 Google 或第三方拥有“预览版”中存在或相关的全部合法权利、所有权和利益,包括“预览版”中存在的任何知识产权。“知识产权”是指根据专利法、版权法、商业机密法、商标法享有的任何及全部权利,以及其他任何及全部专有权利。Google 保留所有未明确授予您的权利。 - -3.3 您不得将“预览版”用于本许可协议未明确允许的任何用途。您不得:(a) 对“预览版”或“预览版”的任何部分进行复制(备份用途除外)、修改、改编、再分发、反编译、逆向工程、反汇编或创建其衍生品;或 (b) 将“预览版”的任何部分加载到移动手持终端或除个人计算机之外的任何其他硬件设备上,将“预览版”的任何部分与其他软件合并,或者发行任何融入“预览版”某一部分的软件或设备。 - -3.4 您同意您将不会进行任何可能引起或导致 Android 碎片化的行动,包括但不限于分发、参与创建或以任何方式推广从“预览版”衍生的软件开发工具包。 - -3.5 对于依据开源软件许可授权的“预览版”组件,其使用、复制和分发仅受该开源软件许可条款的制约,不受本许可协议的约束。您同意在依照被授予的所有权利作为被许可方期间,在遵守此类开源软件许可协议方面始终保持良好的信誉,并避免进行任何可能导致终止、暂停或违反此类权利的行动。 - -3.6 您同意 Google 所提供“预览版”的形式和性质可随时发生变更,而无需事先通知您,并且未来的“预览版”版本可能会与在之前的“预览版”版本上开发的应用不兼容。您同意 Google 可单方面决定在未事先通知您的情况下全面停止(永久性或暂时性)向您或用户提供“预览版”(或“预览版”内的任何功能)。 - -3.7 本许可协议内没有任何条款授予您使用 Google 的任何商品名、商标、服务标志、徽标、域名或其他独特品牌特征的权利。 - -3.8 您同意您不会移除、遮盖或篡改“预览版”上可能贴有或“预览版”内可能包含的任何专有权利声明(包括版权声明和商标声明)。 - -4. 您对“预览版”的使用 - -4.1 Google 同意本许可协议中的任何条款均未授予 Google 从您(或您的许可方)处获取您依照本许可协议使用“预览版”开发的任何软件应用中存在或与其相关的权利、所有权或利益,包括这些应用中存在的任何知识产权。 - -4.2 您同意只出于 (a) 本许可协议和 (b) 相关管辖区域内任何适用法律、法规或公认惯例或准则(包括有关向美国或其他相关国家/地区出口数据或软件或从美国或其他相关国家/地区进口数据或软件的任何法律)所允许的目的而使用“预览版”和编写应用。 - -4.3 您同意,如果您使用“预览版”开发应用,您将会保护用户的隐私权和合法权利。如果用户向您提供用户名、密码或其他登录信息或个人信息,您必须确保用户知晓这些信息将供您的应用使用,并且您必须为这些用户提供足以满足法律要求的隐私声明和保护。如果您的应用存储由用户提供的个人信息或敏感信息,其存储方式必须安全。如果用户向您提供 Google 帐户信息,您的应用只能在用户允许时出于用户所许可的有限目的使用该信息访问用户的 Google 帐户。 - -4.4 您同意您不会利用“预览版”从事任何干扰、中断、损坏或以未经授权方式访问 Google 或任何第三方的服务器、网络或其他财产或服务的Activity(包括应用的开发或分发)。 - -4.5 您同意您对通过 Android 和/或 Android 应用创建、传输或显示的任何数据、内容或资源以及您的行为所导致的后果(包括 Google 可能遭受的任何损失或损害)负全责(Google 在上述方面对您或任何第三方不承担任何责任)。 - -4.6 您同意您为违反本许可协议、任何适用的第三方合约或服务条款或任何适用法律或法规下的义务以及任何上述违规行为所导致的后果(包括 Google 或任何第三方可能遭受的任何损失或损害)负全责(Google 在上述方面对您或任何第三方不承担任何责任)。 - -4.7 “预览版”正在开发中,您的测试和反馈是开发过程的重要环节。使用“预览版”,即表示您承认某些功能仍在开发实现之中,您不应期望“预览版”具备稳定版本的全部功能。您同意不使用此“预览版”公开发布或发运任何应用,因为此“预览版”在 Android SDK 正式发行之后将不再受支持。 - -5. 您的开发者凭据 - -5.1 您同意,对于 Google 可能向您发放或可能由您自行选择的任何开发者凭据,您有责任保持其机密性,并且您对以您的开发者凭据名义开发的所有应用负全责。 - -6. 隐私权和信息 - -6.1 为持续创新和改进“预览版”,Google 可能会从软件收集某些使用统计数据,包括但不限于唯一标识符、关联的 IP 地址、软件的版本号以及有关软件使用了“预览版”中哪些工具和/或服务及其使用方式的信息。在收集任何上述信息之前,“预览版”都会通知您并征求您的同意。如果您拒绝同意,我们将不会收集这些信息。 - -6.2 我们会对收集的数据进行汇总调查,以便改进“预览版”,并会按照 Google 的隐私政策(网址为 http://www.google.com/policies/privacy/)维护数据。 - -7. 第三方应用 - -7.1 如果您使用“预览版”运行由第三方开发或访问由第三方提供的数据、内容或资源的应用,您同意 Google 对这些应用、数据、内容或资源不承担任何责任。您理解,您通过上述第三方应用可能访问到的所有数据、内容或资源由其提供者负全责,Google 对您因使用或访问其中任何第三方应用、数据、内容或资源而遭受的任何损失或损害不承担任何责任。 - -7.2 您应知晓,通过此类第三方应用提供给您的数据、内容和资源可能受提供商(或代表他们的其他人员或公司)所拥有的知识产权的保护。除非相关所有者明确给予许可,否则您不得修改、出租、租赁、借出、出售、分发这些数据、内容或资源(的全部或部分),或以其为基础创建衍生品。 - -7.3 您承认您对上述第三方应用、数据、内容或资源的使用可能受到您与相关第三方之间单独订立的条款的制约。 - -8. 使用 Google API - -8.1 Google API - -8.1.1 如果您使用任何 API 从 Google 检索数据,即表示您承认这些数据可能受到 Google 或这些数据提供方(或代表他们的其他人员或公司)拥有的知识产权的保护。您对任何上述 API 的使用可能受到附加服务条款的制约。除非相关服务条款允许,否则您不得修改、出租、租赁、借出、出售、分发这些数据(的全部或部分),或以其为基础创建衍生品。 - -8.1.2 如果您使用任何 API 从 Google 检索用户数据,即表示您承认并同意您只有在征得用户明确同意时才会检索数据,并且只能在用户允许时出于用户许可的有限目的检索数据。 - -9. 终止许可协议 - -9.1 本许可协议将持续有效,直至您或 Google 按以下规定终止本协议。 - -9.2 如果您想终止本许可协议,可通过停止使用“预览版”以及任何相关开发者凭据予以终止。 - -9.3 Google 有权在向您作出通知后,有理由或无理由地随时终止与您订立的这份许可协议。 - -9.4 本许可协议将在下列情况下自动终止,而无需另行通知或采取其他行动,以先符合条件者为准: -(A) Google 在您居住或使用服务所在国家/地区停止向用户提供“预览版”或“预览版”的某些部分; -(B) Google 发行 Android SDK 的最终版本。 - -9.5 在本许可协议终止时,本许可协议中向您授予的许可将终止,您应立即完全停止使用“预览版”,并且第 10、11、12 和 14 节的条款将无限期继续存在。 - -10. 免责声明 - -10.1 您明确理解并同意,您使用“预览版”的风险将由您自行承担,并且“预览版”是按“原样”和“现状”提供,Google 不提供任何类型的担保。 - -10.2 您对“预览版”的使用以及通过使用“预览版”下载或以其他方式获得的任何材料由您自行决定,风险自负,并且对于因此类使用而对您的计算机系统或其他设备造成的任何损害或数据损失由您单方面负责。在不对上文所述予以限制的条件下,您了解“预览版”并非稳定版本,可能存在将导致重大损害的错误、缺陷和安全漏洞,包括无法挽回地完全无法使用您的计算机系统或其他设备。 - -10.3 Google 进一步明确拒绝任何类型的所有担保和条件,无论明示或暗示,包括但不限于有关适销性、特定用途适用性以及非侵权的暗示担保和条件。 - -11. 有限责任 - -11.1 您明确理解并同意,对于您可能遭遇的任何直接、间接、附带、特殊、继发或惩罚性损害(包括任何数据损失),Google 及其子公司和附属公司以及其许可方在任何责任理论下对您概不承担任何责任,无论 Google 或其代表是否已被告知或是否本应知晓发生任何上述损失的可能性。 - -12. 赔偿 - -12.1 您同意,在法律所允许的最大限度内,为 Google、其附属公司及其各自的董事、高管、员工和代理商提供辩护,使其免于因下列情况引起或产生的任何及所有索赔、诉讼、起诉或诉讼程序以及任何及所有损失、债务、损害、成本和费用(包括合理的律师费用)而承担责任或遭受损害:(a) 您对“预览版”的使用;(b) 您在“预览版”上开发的任何应用侵犯任何人的任何知识产权或诽谤任何人或侵犯其公开权或隐私权;以及 (c) 您的任何行为有悖于本许可协议 - -13. 许可协议的更改 - -13.1 Google 可能会在分发新版本“预览版”时对许可协议做出更改。做出这些更改后,Google 将在提供“预览版”的网站上公布新版本的许可协议。 - -14. 一般法律条款 - -14.1 本许可协议构成您与 Google 之间的完整法律协议,管辖您对“预览版”(不包括 Google 可能依据另外的书面协议向您提供的任何服务)的使用,并完全取代您之前与 Google 之间签订的、与“预览版”有关的任何协议。 - -14.2 您同意,如果 Google 未行使或未强制执行本许可协议包含的任何法定权利或救济(或 Google 在任何适用法律下享有的相关利益),不得视为 Google 正式放弃这些权利,Google 仍可获得这些权利或救济。 - -14.3 如果任何拥有管辖权的法院将本许可协议的任何条款裁定为无效,则该条款将从本许可协议中删除,而不会影响本许可协议的其余部分。本许可协议的其余条款将继续有效且可强制执行。 - -14.4 您承认并同意,Google 集团旗下的每一家公司都将成为本许可协议的第三方受益人,并且此类其他公司将有权直接强制执行和依赖本许可协议中任何授予其利益(或支持其权利)的条款。除此之外,任何其他人员或公司均不得成为本许可协议的第三方受益人。 - -14.5 出口限制。“预览版”受美国出口法律和法规的制约。您必须遵守适用于“预览版”的所有国内和国际出口法律和法规。这些法律包括目的地、最终用户和最终用途方面的限制。 - -14.6 未经 Google 事先书面批准,您不得擅自转让或转移本许可协议,未经此类批准而试图进行的任何转让均为无效。未经 Google 事先书面批准,您不得委托您依据本许可协议所应承担的责任或义务。 - -14.7 本许可协议以及您与 Google 依据本许可协议而建立的关系受加利福尼亚州法律管辖,而无论其是否与其他法律条款冲突。您与 Google 同意服从位于加利福尼亚州圣克拉拉县内法院的专属司法管辖权,以解决本许可协议引起的任何法律事务。尽管有上述规定,您同意仍允许 Google 在任何管辖区域申请禁令救济(或同等类型的紧急法律救济)。 - - -
    \ No newline at end of file diff --git a/docs/html-intl/intl/zh-cn/preview/overview.jd b/docs/html-intl/intl/zh-cn/preview/overview.jd deleted file mode 100644 index d931db0a678aa8c1a8924fca5866f96bd5f91609..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/overview.jd +++ /dev/null @@ -1,389 +0,0 @@ -page.title=计划概览 -page.metaDescription=欢迎参加 Android M 开发者预览版计划。此计划将为您提供针对 Android 的下一版本测试和优化应用所需的所有功能。 -page.image=images/cards/card-preview_16-9_2x.png -page.tags="preview", "developer", "android" - -@jd:body - -
    -

    - Developer Preview 2 is now available -

    - - -
    - -

    - 欢迎使用 Android M 开发者预览版计划。此计划将为您提供针对 Android 的下一版本测试和优化应用所需的所有功能。 - -它是一款免费软件。您仅需下载 M 开发者预览版工具即可立即使用。 - -

    - -
    -
    -
    -
    -
    - 硬件和模拟器系统映像 -
    - -

    - 在 Nexus 5、6、9 和 Player(适用于电视)以及模拟器中运行并测试应用。 - -

    -
    - -
    -
    - 最新的平台代码 -
    - -

    - 我们将在预览版期间提供多次更新,因此您将可以针对最新的平台变更测试您的应用。 - -

    -
    - -
    -
    - 通过 OTA(空中下载技术)更新 -
    - -

    - 在向设备刷入初始预览版后,即可通过 OTA 技术获取更新。 - -

    -
    -
    - -
    - - -
    -
    - 新行为和新功能 -
    - -

    - 尽早做好支持新平台行为(例如新运行时权限模型和省电功能)的准备工作。 - -

    -
    - -
    -
    - 为处理开发者反馈问题而设的优先窗口 -
    - -

    - 在最初的几周里,我们将优先处理开发者报告的问题,以便尽快测试并提供反馈。 - -

    -
    - -
    -
    - 反馈和支持 -
    - -

    - 使用问题跟踪器向我们报告问题并提供反馈。 - 与 M 开发者社区中的其他开发者建立联系。 - -

    -
    -
    -
    -
    - - - - -

    - 时间表和更新 -

    -Preview program timeline -

    - M 开发者预览版从 5 月 28 日开始一直运行到最终 Android M SDK 发布为止。最终的 Android M SDK 将于 2015 年第三季度公众版发布之前不久发布。 - - -

    - -

    - 在开发阶段的各个里程碑,我们将为您的测试设备提供预览版更新。 - 暂定的里程碑包括 -

    - -
      -
    • - 预览版 1(5 月底发布初始预览版); -
    • - -
    • - 预览版 2(6 月底/7 月初); -
    • - -
    • - 预览版 3(7 月底发布近乎最终版本) -
    • -
    - -

    - 这些更新将促进形成最终版本的 SDK(第三季度末),为 Android 新版本以及最终的系统行为和功能交付官方 API。 - - -

    - -

    - 如果您在 Android M 中进行测试和开发,我们郑重建议您随着预览版更新的发布,将开发环境保持为相应的最新版本。 - - 为了简化这一过程,我们将为已刷入预览版内部版本的设备提供空中下载 (OTA) 更新功能,并提供可供您手动下载并刷入的系统映像。 - - -

    -

    - 注:最终的 SDK 和系统映像无法通过 OTA 交付,而是需要手动刷入到测试设备中。 - - -

    - -

    - 当有预览版更新可用时,我们将通过 Android 开发者博客、此站点以及 Android M 开发者社区通知您。 - - -

    - -

    - 预览版具有哪些功能? -

    - -

    - M 开发者预览版包括您在各种使用不同屏幕尺寸、网络技术、CPU/GPU 芯片和硬件架构的设备中测试现有应用所需的所有功能。 - - -

    - -

    - SDK 工具 -

    - -

    - 您可通过 Android Studio 中的 SDK 管理器下载这些组件: -

    - -
      -
    • M 开发者预览版 SDK 工具 -
    • - -
    • M 开发者预览版模拟器系统映像(32 位和 64 位) - -
    • - -
    • 适用于 Android TV 的 M 开发者预览版模拟器系统映像(32 位) - -
    • -
    - -

    - 硬件系统映像 -

    - -

    - 您可从“下载”页面中下载这些供 Nexus 设备使用的硬件系统映像: - -

    - -
      -
    • - Nexus 5 (GSM/LTE) “hammerhead” 设备系统映像 -
    • - -
    • - Nexus 6 “shamu” 设备系统映像 -
    • - -
    • - Nexus 9 (Wi-Fi) “volantis” 设备系统映像 -
    • - -
    • - Nexus Player (Android TV) “fugu” 设备系统映像 -
    • -
    - -

    - 文档和示例代码 -

    - -

    - 以下文档资源有助于您了解预览版: -

    - - - -

    - 支持资源 -

    - -

    - 在 M 开发者预览版中测试和开发时,您可使用以下支持资源: - -

    - -
      -
    • M 开发者预览版问题跟踪器是您的主要反馈渠道。 - -您可通过问题跟踪器报告错误、性能问题和一般反馈。 -您还可检查已知问题并找出解决方法步骤。 - -
    • - -
    • Android M 开发者社区是一家 Google+ 社区。在此社区中,您可与其他使用 Android M 的开发者建立联系。您可以共享观察结果或想法,或查找 Android M 问题的解决方法。 - - - -
    • -
    - - -

    - 锁定目标、预览版 API 和发布 -

    - -

    - Android M 开发者预览版是仅面向开发的版本, -并不具有标准的 API 级别。如果您想选择拒绝通过兼容性行为测试您的应用(强烈推荐),则可将应用的 targetSdkVersion - 设置为 “MNC”,从而锁定 M 开发者预览版。 - - -

    - -

    - Android M 开发者预览版提供预览 API 功能 -— 在最终的 SDK 发布之前,这些 API 都不是正式的 API。目前,最终的 SDK 计划于 2015 年第三季度发布。 -这意味着一段时期内,特别是该计划的最初几周内, -API 可能会出现细微变化。 -我们会通过 Android M 开发者预览版的每次更新,为您提供一份变更摘要。 - -

    - -

    - 请注意,尽管预览版 API 可能会更改,但运行时权限和省电功能等基本系统行为仍保持稳定,可以立即用于测试。 - - -

    - -

    - 关于发布,Google Play 会禁止发布面向 M 开发者预览版的应用。 -当 Android M 最终版本 SDK 可用时,您可以锁定官方 Android M API 级别,并将应用发布至 Google Play。 - -与此同时,如果您需要将针对 Android M 的应用分发给测试者,则可通过电子邮件或从您的站点直接下载实现这一点。 - - -

    - -

    - 如何开始 -

    - -

    - 要开始测试应用,请执行以下操作: -

    - -
      -
    1. 查看 API 概览行为变更,大致了解新功能及其如何影响您的应用。 - -特别地,您需要了解新的运行时权限模型、省电功能和自动备份。 - - -
    2. - -
    3. 遵循有关设置预览版 SDK 和配置测试设备的说明,来设置您的环境。 - - -
    4. - -
    5. 遵循刷入说明,刷入 Nexus 5、6、9 和 Player 的最新 M 开发者预览版系统映像。 - -为开发设备刷入系统映像后,预览版更新将通过无线 (OTA) 更新进行交付。 - -
    6. - -
    7. 下载 M 预览版 API 参考资料M 预览版示例,更深入地了解新 API 功能以及如何在应用中使用这些功能。 - - - -
    8. - -
    9. 加入 Android M 开发者社区,获取最新资讯并与使用新平台的其他开发者建立联系。 - - -
    10. -
    - -

    - 感谢您参加 Android M 开发者预览版计划! -

    diff --git a/docs/html-intl/intl/zh-cn/preview/samples.jd b/docs/html-intl/intl/zh-cn/preview/samples.jd deleted file mode 100644 index 9f1631900b9457fa0b06b7bfc83e8a44aa52ef77..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/samples.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=示例 -page.image=images/cards/samples-new_2x.png -@jd:body - -

    - 以下是 M 开发者预览版的代码示例。要在 Android Studio 中下载示例,请选择文件 > 导入示例菜单选项。 - -

    - -

    - 注:这些可下载的项目设计用于与 Gradle 和 Android Studio 结合使用。 - -

    - - -

    运行时权限

    - -

    - Android M 改变了系统权限运行的方式。系统会在运行时(而不是安装期间)要求用户批准权限请求。 -此示例显示了如何请求这些权限。 - -

    - -

    在 GitHub 中获取

    - -

    确认凭据

    - -

    - 此示例演示了在您的应用中如何使用设备凭据作为身份验证方法。 -

    - -

    在 GitHub 中获取 -

    - -

    指纹对话框

    - -

    - 此示例演示了在您的应用中如何识别已注册的指纹以验证用户的身份。 - -

    - -

    在 GitHub 中获取

    - -

    自动备份应用

    - -

    - Android M 引入了自动备份应用设置功能。此示例演示如何向应用添加筛选规则,以管理设置备份。 - -

    - -

    在 GitHub 中获取

    - -

    相机 2 RAW

    - -

    - 演示如何使用 Camera2 API 捕获 RAW 相机缓冲区并将其另存为 DNG 文件。 - -

    - -

    在 GitHub 中获取

    - -

    活动通知

    - -

    - 此示例演示 NotificationManager - 如何让您了解应用当前显示的通知数量。 - -

    - -

    在 GitHub 中获取

    diff --git a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd b/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd deleted file mode 100644 index e9910b4b44dc4ded1817c873bcd75a165c8f4d56..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/setup-sdk.jd +++ /dev/null @@ -1,207 +0,0 @@ -page.title=设置预览版 SDK -page.image=images/cards/card-set-up_16-9_2x.png - -@jd:body - - - - -

    M 开发者预览版 SDK 可通过 Android SDK 管理器获取。本文假定您熟悉 Android 应用开发的相关操作,例如:使用 Android SDK 管理器和创建项目。 - -如果您是首次使用 Android,请先参阅开发您的第一款应用培训课程。 - -

    - -

    获取 Android Studio 1.3

    - -

    开发者预览版与处于预览版状态的 Android Studio 1.3 结合使用时效果最佳。 -强烈建议您安装 Android Studio 1.3 的预览版本,以便与预览版 SDK 结合使用。 -

    - -

    注意:Android Studio 1.3 的 Canary 预览版仍处于活动的开发状态。 -如果您正使用主要开发机器测试开发者预览版,则可创建另一套 Android Studio 安装系统供测试使用。 - -

    - -

    要安装 Android Studio 1.3 预览版,请执行以下操作:

    - -
      -
    1. 下载并启动 Android Studio。 - -
    2. - -
    3. 打开设置窗口(在 Windows 中,您可通过选择文件 > 设置执行此操作)。 -选择外观和行为 > 系统设置 > 更新面板。 - - - -

      在 OSX 中,您可在 Android Studio 的首选项窗口中找到外观和行为面板。 - -

      -
    4. - -
    5. 更新面板中,选择选项自动检查更新: -Canary 渠道。 -
    6. - -
    7. 更新面板中,选择立即检查,以检查最新的 Canary 内部版本。 -当系统出现提示时,请下载并安装该内部版本。 - -
    8. -
    - -

    获取预览版 SDK

    - -

    要将预览版 SDK 组件添加到开发环境,请执行以下操作:

    - -
      -
    1. 启动 Android Studio 1.3 预览版。 -
    2. - -
    3. 打开设置窗口(在 Windows 中,您可通过选择文件 > 设置执行此操作)。 -选择外观和行为 > 系统设置 > 更新面板。 - - - -

      在 OSX 中,您可在 Android Studio 的首选项窗口中找到外观和行为面板。 - -

      -
    4. - -
    5. 更新面板中,选择选项自动检查更新: -Canary 渠道自动检查 Android SDK 的更新: -预览版渠道。 -
    6. - -
    7. 启动 Android SDK 管理器。(对于 Android Studio 1.3,SDK 管理器已集成至 Android Studio,而不是独立应用。) - - -
    8. - -
    9. 平台部分下,选择 Android MNC 预览版。 - -
    10. - -
    11. 工具部分中,选择最新的 Android SDK 工具平台工具构建工具。 - - -
    12. - -
    13. 点击安装软件包并接受所有软件包的许可协议。 - -
    14. - -
    15. 验证是否已安装 M 开发者预览版,具体方式如下:打开设置窗口并选择外观与行为 > 系统设置 > Android SDK 面板。 - -
    16. - -
    17. Android SDK 面板中,选择 SDK 平台选项卡。 -Android MNC 预览版应作为“已安装”列出。 -另外,打开 SDK 工具选项卡,确认是否已安装最新工具。 - - -
    18. -
    -

    完成上述步骤后,预览版组件即可用于您的开发环境。 -

    - - -

    创建或更新项目

    - -

    - 为使用预览版 API,您必须创建或更新一个使用预览版组件的开发项目。 - -

    - - -

    创建新项目

    - -

    - 建议使用 Android Studio 创建一个使用该预览版的项目。按照创建项目中所描述的步骤操作,直到转到项目向导中的“外形”屏幕为止。 - -然后执行下列步骤,创建为该预览版配置的项目。 - -

    - -
      -
    • 选中手机和平板电脑
    • -
    • 选择 MNC:Android M (预览版)(在最低 SDK 中)。 -
    • -
    - - -

    更新现有项目

    - -

    - 对于现有项目,您必须修改项目配置以启用预览版 API。在开发环境中,打开模块的 build.gradle 文件,并按如下所示设置这些值: - - -

    - -
      -
    • compileSdkVersion 设置为 'android-MNC'
    • -
    • minSdkVersion 设置为 'MNC'
    • -
    • targetSdkVersion 设置为 'MNC'
    • -
    - - -

    测试设置

    - -

    - 要使用预览版测试应用,您必须拥有已配置平台预览版本的物理设备或虚拟设备。 -如果您拥有兼容设备,则可安装预览版平台进行测试。 -另外,您可以配置用于测试的虚拟设备。 -

    - -

    设置物理设备

    - -

    - 如果您拥有 Nexus 5、Nexus 6、Nexus 9 或 Android TV,则可在这些设备中安装预览版系统映像,来测试您的应用。通过使用 Android 虚拟设备管理器工具,您可从 Android Studio 内设置带有平台预览版本的虚拟设备。 - - - -

    - -

    - 重要说明:在设备中安装预览版映像会删除此设备中的所有数据,因此您应在安装预览版映像之前备份数据。 - -

    - -

    设置虚拟设备

    - -

    - 通过使用 Android 虚拟设备管理器工具,您可从 Android Studio 内设置带有平台预览版本的虚拟设备。 - -

    - -

    要使用 AVD 管理器创建 AVD,请执行以下操作:

    - -
      -
    1. 在开发环境中安装预览版 SDK,如设置预览版 SDK 中所述。 - -
    2. -
    3. 按照使用 AVD 管理器管理 AVD 中的步骤操作。 - -使用以下设置: -
        -
      • 设备:Nexus 5、Nexus 6、Nexus 9 或 Android TV
      • -
      • 目标: - Android M(预览版)- API 级别 M
      • -
      • ABI:x86
      • -
      -
    4. -
    - -

    - 如需了解有关创建测试用虚拟设备的详细信息,请参阅管理虚拟设备。 -

    diff --git a/docs/html-intl/intl/zh-cn/preview/testing/guide.jd b/docs/html-intl/intl/zh-cn/preview/testing/guide.jd deleted file mode 100644 index 256d5bbef0ee58262cfe4cf5dff0ef43ab3040a6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/testing/guide.jd +++ /dev/null @@ -1,187 +0,0 @@ -page.title=测试指南 -page.image=images/cards/card-build_16x9_2x.png -page.keywords=预览资源,androidm,测试,权限 - -@jd:body - - - -

    - 利用 Android M 开发者预览版,您有机会确保应用可使用下一平台版本。 -如 API 概览行为变更中所述,该预览版包括大量 API 和可能影响应用的行为变更。 - -使用预览版测试应用时,您应重点关注一些特定的系统变更,确保用户拥有愉悦的体验。 - - -

    - -

    - 本指南介绍可使用您的应用测试预览版的哪些功能以及如何测试。您应确定优先测试以下特定预览版功能,因为它们可能会对应用行为产生较大影响。 - - -

    - - - -

    - 如需了解有关如何使用预览版系统映像设置设备或虚拟设备以进行测试的详细信息,请参阅设置预览版 SDK。 - -

    - - -

    测试权限

    - -

    - 新权限模型改变了用户向您的应用分配权限的方式。 -您的应用必须在运行时要求用户提供各项权限,而不是在安装过程中要求授予所有权限。 - -对于用户而言,此行为有助于他们更精细地控制每个应用的Activity,并更深入地了解应用为何请求提供特定权限的上下文信息。 -用户可以随时向应用授予某项权限或撤销其某项权限。 -预览版的这种功能最有可能会对应用行为产生影响,而且可能会阻止某些应用功能运行或只能在降级状态中运行。 - - -

    - -

    - 这一变更会影响在新平台上运行的所有应用,即便这些应用并非面向新平台版本开发亦是如此。 -该平台为旧版应用提供有限的兼容性行为,但您现在应当开始计划将应用迁移到新权限模型,以便在官方平台启动时发布更新的应用版本。 - - -

    - - -

    测试提示

    - -

    - 使用以下测试提示有助于您计划并通过新权限行为执行应用测试。 - -

    - -
      -
    • 识别应用的当前权限和相关的代码路径
    • -
    • 跨受权限保护的服务和数据测试用户流程
    • -
    • 使用授予/撤销权限的各种组合进行测试
    • -
    • 使用 {@code adb} 工具从命令行管理权限: -
        -
      • 按组列出权限和状态: -
        adb shell pm list permissions -d -g
        -
      • -
      • 使用以下语法授予或撤销一项或多项权限:
        -
        adb shell pm [grant|revoke] <permission.name> ...
        -
      • -
      -
    • -
    • 针对使用权限的服务对应用进行分析
    • -
    - -

    测试策略

    - -

    - 权限更改会影响应用的结构和设计,以及您为用户提供的用户体验和流程。 -您应评估应用的当前权限使用情况并开始计划要提供的新流程。 -平台的正式版本提供兼容性行为,但您应计划更新应用,而不是依赖于这些行为。 - - -

    - -

    - 确定应用实际需要和使用的权限,然后找出各种使用受权限保护的服务的代码路径。 -您可通过结合使用新平台测试和代码分析完成此操作。 -在测试中,您应通过将应用的 {@code targetSdkVersion} 更改为预览版,重点关注选择运行时权限。 -如需了解详细信息,请参阅设置预览版 SDK。 - -

    - -

    - 使用已撤销和已添加权限的各种组合进行测试,突出显示依赖于权限的用户流程。 -如果依赖关系不明显或不符合逻辑,则您应考虑重构或划分该流程,以消除依赖关系或阐明需要权限的原因。 - - -

    - -

    - 如需了解有关运行时权限行为、测试和最佳做法的详细信息,请参阅权限开发者预览版页面。 - - -

    - - -

    测试瞌睡模式和应用待机模式

    - -

    - 当设备处于空闲状态或应用未聚焦时,瞌睡模式和应用待机模式的节能功能将限制应用可执行的后台处理工作量。 -系统可对应用实施的限制包括:限制或禁止访问网络、暂停后台任务、暂停通知、忽略唤醒请求和闹铃。 - -要确保应用在完成这些节能优化后正常运行,您应通过模拟这些低功耗状态对应用进行测试。 - - -

    - -

    在瞌睡模式下测试您的应用

    - -

    要在瞌睡模式下测试您的应用,请执行以下操作:

    - -
      -
    1. 使用 M 预览版系统映像配置硬件设备或虚拟设备
    2. -
    3. 将设备连接到开发计算机并安装应用
    4. -
    5. 运行应用并使其保持活动状态
    6. -
    7. 通过运行以下命令,模拟进入瞌睡模式的设备: - -
      -$ adb shell dumpsys battery unplug
      -$ adb shell dumpsys deviceidle step
      -$ adb shell dumpsys deviceidle -h
      -
      - -
    8. -
    9. 观察重新激活设备时的应用行为。确保应用在设备退出瞌睡模式时正常恢复 -
    10. -
    - - -

    在应用待机模式下测试您的应用

    - -

    要在应用待机模式下测试您的应用,请执行以下操作:

    - -
      -
    1. 使用 M 预览版系统映像配置硬件设备或虚拟设备
    2. -
    3. 将设备连接到开发计算机并安装应用
    4. -
    5. 运行应用并使其保持活动状态
    6. -
    7. 通过运行以下命令,模拟进入待机模式的应用: - -
      -$ adb shell am broadcast -a android.os.action.DISCHARGING
      -$ adb shell am set-idle <packageName> true
      -
      - -
    8. -
    9. 使用以下命令模拟如何唤醒应用: -
      $ adb shell am set-idle <packageName> false
      -
    10. -
    11. 观察唤醒后的应用行为。确保应用从待机模式中正常恢复。 -特别地,您应检查应用的通知和后台作业是否按预期继续运行 -
    12. -
    - -

    自动备份应用和设备特定的标识符

    - -

    如果应用坚持在内部存储中使用任何设备特定的标识符,如 Google 云消息传递注册 ID,请确保遵循最佳做法将存储位置从自动备份中排除,如自动备份应用中所述。 - - - -

    diff --git a/docs/html-intl/intl/zh-cn/preview/testing/performance.jd b/docs/html-intl/intl/zh-cn/preview/testing/performance.jd deleted file mode 100644 index 1f88854b3e5cfed473cb46e32697e70879c0cffa..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-cn/preview/testing/performance.jd +++ /dev/null @@ -1,656 +0,0 @@ -page.title=测试显示性能 -page.image=images/cards/card-test-performance_2x.png -page.keywords=性能, fps, 工具 - -@jd:body - - - - - -

    - 用户界面 (UI) 性能测试可确保您的应用不仅满足其功能要求,同时确保用户与应用之间的交互顺畅无比,能够以每秒连续 60 帧(为什么选择 60fps?)的帧速运行,而不会出现任何帧丢失或延迟的现象,也就是我们通常所说的“卡顿”。 - - -本文档介绍可用于测量 UI 性能的工具并提出一种将 UI 性能测量集成到测试实践中的方法。 - - -

    - - -

    测量 UI 性能

    - -

    - 为了改善性能,您首先必须能够测量系统性能,然后诊断并识别可能来自管道各个部分的问题。 - - -

    - -

    - dumpsys 是一种在设备上运行并转储感兴趣的系统服务状态信息的 Android 工具。 - -通过向 dumpsys 传递“gfxinfo”命令,可以提供 logcat 格式的输出,其中包含有关与录制阶段期间发生的动画帧相关的性能信息。 - - -

    - -
    -> adb shell dumpsys gfxinfo <PACKAGE_NAME>
    -
    - -

    - 此命令可以生成多个不同表达形式的帧时间数据。 -

    - -

    聚合帧统计信息

    - -

    - 借助 M 预览版,该命令可将在整个进程生命周期中收集的帧数据的聚合分析打印输出到 logcat。 -例如: -

    - -
    -Stats since: 752958278148ns
    -Total frames rendered: 82189
    -Janky frames: 35335 (42.99%)
    -90th percentile: 34ms
    -95th percentile: 42ms
    -99th percentile: 69ms
    -Number Missed Vsync: 4706
    -Number High input latency: 142
    -Number Slow UI thread: 17270
    -Number Slow bitmap uploads: 1542
    -Number Slow draw: 23342
    -
    - -

    - 这些高级统计信息可以较高水平地传达应用的呈现性能及其在多个帧之间的稳定性。 - -

    - - -

    精确的帧时间信息

    - -

    - M 预览版附带提供了一个适用于 gfxinfo 的新命令,即:framestats,该命令根据最近的帧提供非常详细的帧时间信息,让您能够更准确地查出并调试问题。 - - -

    - -
    ->adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
    -
    - -

    - 此命令根据应用生成的最后 120 帧,打印输出带有纳秒时间戳的帧时间信息。以下是来自 adb dumpsys gfxinfo <软件包名称> framestats 的原始输出示例: - - -

    - -
    -0,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954,
    -0,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120,
    -0,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527,
    -0,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683,
    -
    - -

    - 每行输出均代表应用生成的一帧。每行都有固定的列数,用于描述帧生成管道的每个阶段所花的时间。 -下文将详细描述此格式,包括每列代表的具体内容。 - -

    - - -

    Framestats 数据格式

    - -

    - 由于数据块是 CSV 格式的输出,因此将其粘贴到所选的电子表格工具或使用脚本进行收集和解析非常简单。 -下表解释了输出数据列的格式。 -所有时间戳均以纳秒计。 -

    - -
      -
    • 标志 -
        -
      • “标志”列带有“0”的行可以通过从 FRAME_COMPLETED 列中减去 INTENDED_VSYNC 列计算得出总帧时间。 - -
      • - -
      • 该列为非零值的行将被忽略,因为其对应的帧已被确定为偏离正常性能,其布局和绘制时间预计超过 16 毫秒。 - -可能出现这种情况有如下几个原因: -
          -
        • 窗口布局发生变化(例如,应用的第一帧或在旋转后) - -
        • - -
        • 此外,如果帧的某些值包含无意义的时间戳,则也可能跳过该帧。 -例如,如果帧的运行速度超过 60fps,或者如果屏幕上的所有内容最终都准确无误,则可能跳过该帧,这不一定表示应用中存在问题。 - - -
        • -
        -
      • -
      -
    • - -
    • INTENDED_VSYNC -
        -
      • 帧的预期起点。如果此值不同于 VSYNC,则表示 UI 线程中发生的工作使其无法及时响应垂直同步信号。 - - -
      • -
      -
    • - -
    • VSYNC -
        -
      • 所有垂直同步侦听器中使用的时间值和帧绘图(Choreographer 帧回调、动画、View.getDrawingTime() 等等) - -
      • - -
      • 如需进一步了解 VSYNC 及其对应用产生的影响,请观看了解 VSYNC 视频。 - - -
      • -
      -
    • - -
    • OLDEST_INPUT_EVENT -
        -
      • 输入队列中最早输入事件的时间戳或 Long.MAX_VALUE(如果帧没有输入事件)。 - -
      • - -
      • 此值主要用于平台工作,对应用开发者的作用有限。 - -
      • -
      -
    • - -
    • NEWEST_INPUT_EVENT -
        -
      • 输入队列中最新输入事件的时间戳或 0(如果帧没有输入事件)。 - -
      • - -
      • 此值主要用于平台工作,对应用开发者的作用有限。 - -
      • - -
      • 但是,可以通过查看 (FRAME_COMPLETED - NEWEST_INPUT_EVENT) 大致了解应用增加的延迟时间。 - -
      • -
      -
    • - -
    • HANDLE_INPUT_START -
        -
      • 将输入事件分派给应用的时间戳。 -
      • - -
      • 通过观察此时间戳与 ANIMATION_START 之间的时差,可以测量应用处理输入事件所花的时间。 - -
      • - -
      • 如果这个数字较高(> 2 毫秒),则表明应用处理 View.onTouchEvent() 等输入事件所花的时间太长,这意味着此工作需要进行优化或转交给其他线程。 - -请注意,有些情况下(例如,启动新Activity或类似活动的点击事件),这个数字较大是预料之中并且可以接受的。 - - -
      • -
      -
    • - -
    • ANIMATION_START -
        -
      • 在 Choreographer 中注册的动画运行的时间戳。 -
      • - -
      • 通过观察此时间戳与 PERFORM_TRANVERSALS_START 之间的时差,可以确定评估正在运行的所有动画(ObjectAnimator、ViewPropertyAnimator 和通用转换)所需的时间。 - - -
      • - -
      • 如果这个数字较高(> 2 毫秒),请检查您的应用是否编写了任何自定义动画,或检查 ObjectAnimator 在对哪些字段设置动画并确保它们适用于动画。 - - -
      • - -
      • 如需了解有关 Choreographer 的更多信息,请观看利弊视频。 - -
      • -
      -
    • - -
    • PERFORM_TRAVERSALS_START -
        -
      • 如果您从此值中扣除 DRAW_START,则可推断出完成布局和测量阶段所需的时间(请注意,在滚动或动画期间,您会希望此时间接近于零)。 - - -
      • - -
      • 如需了解有关呈现管道的测量和布局阶段的更多信息,请观看失效、布局和性能视频。 - - -
      • -
      -
    • - -
    • DRAW_START -
        -
      • performTraversals 绘制阶段的开始时间。这是记录任何失效视图的显示列表的起点。 - -
      • - -
      • 此时间与 SYNC_START 之间的时差就是对树中的所有失效视图调用 View.draw() 所需的时间。 - -
      • - -
      • 如需了解有关绘图模型的详细信息,请参阅硬件加速失效、布局和性能视频。 - - -
      • -
      -
    • - -
    • SYNC_START -
        -
      • 绘制同步阶段的开始时间。 -
      • - -
      • 如果此时间与 ISSUE_DRAW_COMMANDS_START 之间的时差较大(约 > 0.4 毫秒),则通常表示绘制了大量必须上传到 GPU 的新位图。 - - -
      • - -
      • 如需进一步了解同步阶段,请观看 GPU 呈现模式分析视频。 - -
      • -
      -
    • - -
    • ISSUE_DRAW_COMMANDS_START -
        -
      • 硬件呈现器开始向 GPU 发出绘图命令的时间。 -
      • - -
      • 此时间与 FRAME_COMPLETED 之间的时差让您可以大致了解应用生成的 GPU 工作量。 -绘制过度或呈现效果不佳等问题都会在此显示出来。 - -
      • -
      -
    • - -
    • SWAP_BUFFERS -
        -
      • 调用 eglSwapBuffers 的时间,此调用不属于平台工作,相对乏味。 - -
      • -
      -
    • - -
    • FRAME_COMPLETED -
        -
      • 全部完成!处理此帧所花的总时间可以通过执行 FRAME_COMPLETED - INTENDED_VSYNC 计算得出。 - -
      • -
      -
    • - -
    - -

    - 您可以通过不同的方法使用此数据。一种简单却有用的可视化方式就是在不同的延迟时段中显示帧时间 (FRAME_COMPLETED - INTENDED_VSYNC) 分布的直方图(参见下图)。 - -此图直观地表明,大部分帧非常有效,截止时间远低于 16 毫秒(显示为红色),但是少数帧明显超出了截止时间。 - -我们可以观察此直方图中的变化趋势,了解所产生的整体变化或新异常值。 -此外,您还可以根据数据中的多个时间戳绘制表示输入延迟、布局所用时间或其他类似关注指标的图形。 - - -

    - - - - -

    简单的帧时间转储

    - -

    - 如果在“开发者选项”中将 GPU 呈现模式分析设置为在 adb shell dumpsys gfxinfo 中,则 adb shell dumpsys gfxinfo 命令会打印输出最近 120 帧的时间信息,这些信息分为几个不同的类别,其相应的值以制表符分隔。 - - -这些数据可以用于大致表明绘图管道的哪些部分可能速度较慢。 - -

    - -

    - 与上述 framestats 类似,将其粘贴到所选的电子表格工具或使用脚本进行收集和解析同样非常简单。 - -下图详细显示了应用生成的许多帧的具体时间分布。 - -

    - - - -

    - 运行 gfxinfo、复制输出、将其粘贴到电子表格应用并将数据绘制成堆积条形图的结果。 - -

    - -

    - 每个垂直条均代表一个动画帧;其高度代表计算该动画帧所需的毫秒数。 -垂直条的每个彩色分段均代表呈现管道的一个不同阶段,因此您可以看到应用的哪些部分可能会出现瓶颈。 - -如需了解有关呈现管道的详细信息,请参阅失效、布局和性能视频。 - - -

    - - -

    控制统计信息收集的时段

    - -

    - Framestats 和简单的帧计时均可在极短的时间内(相当于约呈现 2 秒)收集数据。 -要精确控制此时间范围(例如,将数据限制于特定动画),您可以重置所有计数器并汇总收集的统计信息。 - - -

    - -
    ->adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
    -
    - -

    - 这也可以与转储命令结合使用来定期进行收集和重置,从而持续捕获时间范围不到 2 秒的帧。 - - -

    - - -

    诊断性能回归

    - -

    - 要查出问题并保持应用运行状况良好,第一步最好是识别回归。 -但是,dumpsys 仅确定是否存在问题及其相对严重性。 -您仍需诊断性能问题的具体原因并找到适当的解决方法。 -为此,我们强烈建议您使用 systrace 工具。 - -

    - - -

    其他资源

    - -

    - 如需了解有关 Android 呈现管道的工作原理、可能存在的常见问题以及如何解决这些问题的详细信息,以下其他资源可能对您有所帮助: - - -

    - -
      -
    • 呈现性能 101 -
    • -
    • 为什么选择 60fps? -
    • -
    • Android UI 和 GPU -
    • -
    • 失效、布局和性能 -
    • -
    • 使用 Systrace 分析 UI 性能 -
    • -
    - - -

    自动化 UI 性能测试

    - -

    - UI 性能测试方法之一是让测试人员对目标应用执行一系列用户操作,并目视检查是否存在卡顿现象,或花费大量时间使用工具驱动型方法来查明是否存在卡顿现象。 - -但是,这种人工方法充满风险:人为感知帧率变化的能力参差不齐,并且此过程又费时、繁琐且易于出错。 - - -

    - -

    - 更为有效的方法是记录并分析自动化 UI 测试中的关键性能指标。 -Android M 开发者预览版包括新的日志记录功能。利用这些功能,您可以轻松确定应用动画中的卡顿数量和严重性,您还可以使用这些功能构建严格的流程,用于确定当前性能并跟踪未来的性能目标。 - - - -

    - -

    - 本文将向您介绍一种使用这些数据自动化性能测试的推荐方法。 - -

    - -

    - 此方法主要分为两个关键操作。首先,确定测试对象和测试方法;其次,设置和维护自动化测试环境。 - - -

    - - -

    设置 UI 测试

    - -

    - 在开始进行自动化测试之前,您必须做出一些较高层次的决策,以便准确了解测试空间和可能存在的需求。 - -

    - -

    - 识别要测试的关键动画/流程 -

    - -

    - 请记住,流畅的动画中断时,用户对低劣性能的感触最深。 -因此,在识别要测试的 UI 操作类型时,集中精力处理用户最常见或对用户体验最为重要的关键动画非常有用。 - -例如,以下是对识别有所帮助的一些常见场景: -

    - -
      -
    • 滚动主要的 ListView 或 RecyclerView -
    • - -
    • 异步等待周期内的动画 -
    • - -
    • 可能涉及位图加载/操纵的所有操作 -
    • - -
    • 包括 Alpha 值混合处理在内的动画 -
    • - -
    • 使用画布绘制的自定义视图 -
    • -
    - -

    - 与团队中的工程师、设计师和产品经理开展合作,优先处理测试覆盖范围内的这些关键产品动画。 - -

    - -

    - 定义未来的目标并予以跟踪 -

    - -

    - 从较高层面来看,确定具体的性能目标、专注于编写测试并收集相关数据至关重要。 -例如: -

    - -
      -
    • 您是否只是首次想要开始跟踪 UI 性能以了解详情? -
    • - -
    • 您是否想要防止未来可能引入的性能回归? -
    • - -
    • 您当前是否有 90% 的帧运行顺畅且希望在本季度达到 98%? -
    • - -
    • 您是否有 98% 的帧运行顺畅且不希望出现性能回归? -
    • - -
    • 提高低端设备上的性能是否为您的目标? -
    • -
    - -

    - 在所有这些情况下,您都将需要进行历史跟踪,以显示多个应用版本中的性能。 - -

    - -

    - 识别用于测试的设备 -

    - -

    - 应用性能因其所在设备而异。某些设备的内存可能更少,GPU 功能略弱或 CPU 芯片速度较慢。 -这意味着动画在一套硬件上表现良好,但在其他硬件上可能并非如此,而且可能因为其他管道部分中出现的瓶颈表现更为糟糕。 - -因此,考虑到用户可能会看到的这种变化,请选取各种设备(包括当前的高端设备、低端设备、平板电脑等)来执行测试。 - -找出 CPU 性能、RAM、屏幕密度、尺寸等方面的变化。 -在高端设备上顺利通过的测试在低端设备上可能会失败。 - -

    - -

    - 基本的 UI 测试框架 -

    - -

    - UI AutomatorEspresso 等测试套件是为了帮助自动化用户使用应用过程中的操作而构建。 - -这些套件是模拟用户与您的设备进行交互的简单框架。 -要使用这些框架,您需要有效创建通过一组用户操作运行的独特脚本,并在设备中演示这些脚本。 - - -

    - -

    - 通过结合这些自动化测试以及 dumpsys gfxinfo,您可以快速创建可再生成的系统,然后您可使用此系统执行测试并测量特定条件的性能信息。 - - -

    - - -

    设置自动化 UI 测试

    - -

    - 您能够执行 UI 测试并拥有从单一测试收集数据的管道后,下一个重要步骤就是采用可以在多种设备上多次执行该测试的框架,并汇总生成的性能数据,以供开发团队做进一步分析。 - - - -

    - -

    - 测试自动化框架 -

    - -

    - 有一点值得注意,UI 测试框架(例如,UI Automator)直接在目标设备/模拟器上运行, -而性能信息收集则是由主机通过 ADB 发送命令来驱动 dumpsys gfxinfo 完成的。 -为帮助桥接这些单独实体的自动化,我们开发了 MonkeyRunner 框架;这是一款在主机上运行的脚本编写系统,可以向一组连接设备发出命令并从中接收数据。 - - - -

    - -

    - 为正确自动化 UI 性能测试而构建一套脚本时,至少应当能够利用 monkeyRunner 完成以下任务: - -

    - -
      -
    • 向一个或多个目标设备或模拟器加载并启动所需的 APK。 -
    • - -
    • 启动 UI Automator 的 UI 测试并允许执行该测试 -
    • - -
    • 通过 dumpsys gfxinfo 收集性能信息。 -
    • - -
    • 汇总信息并以有效的方式重新向开发者显示。 -
    • -
    - - -

    分类并解决观察到的问题

    - -

    - 一旦确定问题模式或回归,下一步就是识别和应用修复。 -如果自动化测试框架保持帧的精确时间分解,则可帮助您审查最近的可疑代码/布局更改(出现回归时),或在切换到人工调查时缩小分析的系统范围。 - - -对于人工调查,systrace 是一个很好的着手点,它可显示有关呈现管道的每个阶段、系统中的每个线程和核心以及您定义的任何自定义事件标记的精确时间信息。 - - -

    - -

    - 准确分析时间 -

    - -

    - 需要注意的是,获取和测量呈现性能所需的时间并非易事。 -就其本质而言,这些数字不具有确定性,往往会随系统状态、可用内存量、热节流以及太阳耀斑到达地面的最后时间而波动。 - -问题在于,尽管同一测试可以运行两次,但获得的结果可能略有不同,它们彼此接近,却并不完全相同。 - - -

    - -

    - 以这种方式准确收集和分析数据意味着多次运行同一测试,且以平均值或中值的形式累积结果(为了简单起见,我们将其称为“批处理”)。这可为您提供测试性能的粗略近似值,而无需确切的时间。 - - - -

    - -

    - 您可对代码更改前和更改后的应用均使用批处理,了解这些更改对性能产生的相对影响。 -如果更改前批处理的平均帧率大于更改后批处理的平均帧率,则此特定更改通常可为您带来全面的性能优势。 - - -

    - -

    - 这意味着您执行的任何自动化 UI 测试均应考虑这一概念以及测试期间可能出现的任何异常。 -例如,如果应用性能因某些设备问题(不是由应用引起)骤降,则您可能需要重新运行批处理以便获得更精确的时间。 - - - -

    - -

    - 那么,在获得更有意义的测量结果之前,您应运行多少次测试?至少应运行 10 次,次数越多(例如 50 或 100 次)获得的结果更精确(当然,您现在是牺牲时间换取精确度) - - -

    diff --git a/docs/html-intl/intl/zh-cn/sdk/index.jd b/docs/html-intl/intl/zh-cn/sdk/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..4de602227c7fb7bcf58bcd8ce68610d54008b188 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/sdk/index.jd @@ -0,0 +1,429 @@ +page.title=下载 Android Studio 和 SDK 工具 +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=下载官方 Android IDE 和开发者工具,构建适用于 Android 手机、平板电脑、可穿戴设备、电视等设备的应用。 + +@jd:body + + + + + + + +
    + + + + + + + + + +
    + +
     
    + + + +
    + +

    Android Studio

    + +

    官方 Android IDE

    + +
      +
    • Android Studio IDE
    • +
    • Android SDK 工具
    • +
    • Android 6.0 (Marshmallow) 平台
    • +
    • Android 6.0 模拟器系统映像(附带 Google API)
    • +
    + +Download Android Studio
    + + +

    +如需获得 Android Studio 或独立 SDK 工具,请访问 developer.android.com/sdk/ +

    +
    + + + + + + +

    智能代码编辑器

    + +
    + +
    + +
    +

    Android Studio 的核心是一个智能代码编辑器,可进行高级代码完成、重构和代码分析。 +

    +

    这款功能强大的代码编辑器可帮助您成为更高产的 Android 应用开发者。

    +
    + + + + + +

    代码模板和 GitHub 集成

    + +
    + +
    + +
    +

    新项目向导让开始一个新项目变得前所未有的简单。

    + +

    可使用适用于不同模式(如抽屉式导航栏和视图分页器)的模板代码开始项目,甚至可以从 GitHub +导入 Google 代码示例。

    +
    + + + + +

    多屏幕应用开发

    + +
    + +
    + +
    +

    构建适用于 Android 手机、平板电脑、Android Wear、Android TV、Android Auto +以及 Google Glass 的应用。

    +

    Android Studio 内全新的 Android +项目视图和模块支持让应用项目和资源管理变得更加轻松。 +

    + + + + +

    用于模拟所有形状和尺寸的虚拟设备

    + +
    + +
    + +
    +

    Android Studio 预先配置了经过优化的模拟器映像。

    +

    经过更新和精简的虚拟设备管理器可为常见 Android +设备提供预定义设备配置文件。

    +
    + + + + +

    +Android 版本借助 Gradle 演进

    + +
    + +
    + +
    +

    使用同一项目为您的 Android 应用创建多个具有不同功能的 APK。

    +

    使用 Maven 管理应用依赖项。

    +

    使用 Android Studio 或命令行构建 APK。

    +
    + + + + +

    有关 Android Studio 的详细信息

    +
    + +Download + +
      +
    • 这款由 JetBrains 推出并广受欢迎的 Java IDE 以 IntelliJ IDEA Community Edition 为基础构建
    • +
    • 基于 Gradle 的灵活构建系统
    • +
    • 构建变体和多 APK 生成
    • +
    • 为 Google 服务和各种设备类型提供扩展模板支持
    • +
    • 支持主题编辑的富布局编辑器
    • +
    • 可捕捉性能、可用性、版本兼容性以及其他问题的 Lint 工具
    • +
    • ProGuard 和应用签名功能
    • +
    • 内置对 Google 云平台的支持,可轻松集成 Google Cloud + Messaging 和应用引擎
    • +
    + +

    +有关 Android Studio +所提供功能的更多详细信息,请阅读 Android Studio 基础知识指南。

    +
    + + +

    如果您一直在使用附带 ADT 的 Eclipse,请注意 Android Studio 现已成为 Android +的官方 IDE,因此您应迁移至 Android Studio,以获得所有最新的 IDE +更新。如果在迁移项目时需要帮助,请参阅迁移至 +Android +Studio

    + + + + + + + +

    系统要求

    + +

    Windows

    + +
      +
    • Microsoft® Windows® 8/7/Vista/2003(32 位或 64 位)
    • +
    • 最低:2GB RAM,推荐:4GB RAM
    • +
    • 400MB 硬盘空间
    • +
    • Android SDK、模拟器系统映像及缓存至少需要 1GB 空间
    • +
    • 最低屏幕分辨率:1280 x 800
    • +
    • Java 开发工具包 (JDK) 7
    • +
    • 用于加速模拟器的选件:支持 Intel® VT-x、Intel® EM64T (Intel® 64) 和禁止执行 +(XD) 位功能的 Intel® 处理器
    • +
    + + +

    Mac OS X

    + +
      +
    • Mac® OS X® 10.8.5 或更高版本,直至 10.9 (Mavericks)
    • +
    • 最低:2GB RAM,推荐:4GB RAM
    • +
    • 400MB 硬盘空间
    • +
    • Android SDK、模拟器系统映像及缓存至少需要 1GB 空间
    • +
    • 最低屏幕分辨率:1280 x 800
    • +
    • Java 运行组件环境 (JRE) 6
    • +
    • Java 开发工具包 (JDK) 7
    • +
    • 用于加速模拟器的选件:支持 Intel® VT-x、Intel® EM64T (Intel® 64) +和禁止执行 (XD) 位功能的 Intel® 处理器
    • +
    + +

    在 Mac OS 上运行附带 Java 运行组件环境 (JRE) 6 的 Android Studio +可优化字体渲染。您随后可将您的项目配置为使用 Java 开发工具包 (JDK) 6 或 JDK 7。

    + + + +

    Linux

    + +
      +
    • GNU 网络对象模型环境或 KDE 桌面
    • +
    • GNU C Library (glibc) 2.15 或更高版本
    • +
    • 最低:2GB RAM,推荐:4GB RAM
    • +
    • 400MB 硬盘空间
    • +
    • Android SDK、模拟器系统映像及缓存至少需要 1GB 空间
    • +
    • 最低屏幕分辨率:1280 x 800
    • +
    • Oracle® Java 开发工具包 (JDK) 7
    • +
    +

    已在 +Ubuntu® 14.04 (Trusty Tahr)(能够运行 32 位应用的 64 位分发)上进行了测试。

    + + + + +

    其他下载选项

    + + diff --git a/docs/html-intl/intl/zh-cn/sdk/installing/adding-packages.jd b/docs/html-intl/intl/zh-cn/sdk/installing/adding-packages.jd new file mode 100644 index 0000000000000000000000000000000000000000..4d5d7ec8b0c90794f18e944afaf1d8b7c32b5e43 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=添加 SDK 软件包 + +page.tags=sdk 管理器 + +@jd:body + + + + +

    +默认情况下,Android SDK +并不包括您着手开发所需的一切内容。SDK +将工具、平台和其他组件分成若干个软件包,您可以通过 +Android SDK 管理器根据需要下载这些软件包。因此您需要先为 Android SDK +添加几个软件包,然后才能着手开发。

    + +

    要添加软件包,请先通过下列方式之一启动 Android SDK 管理器:

    +
      +
    • 在 Android Studio 中,点击工具栏中的 SDK 管理器 +
    • +
    • 如果您未使用 Android Studio: +
        +
      • Windows:双击 Android + SDK 根目录中的 SDK Manager.exe 文件。
      • +
      • Mac/Linux:打开一个终端,导航到 Android SDK 安装 +位置的 tools/ 目录,然后执行 android sdk
      • +
      +
    • +
    + +

    当您首次打开 SDK +管理器时,有几个软件包默认处于选定状态。保持它们的选定状态,但务必按照下列步骤获得着手开发所需的一切内容: +

    + + +
      +
    1. +

      获得最新的 SDK 工具

      + + + +

      在安装 Android SDK 时, +您至少应下载最新工具和 Android 平台:

      +
        +
      1. 打开 Tools 目录并选择: +
          +
        • Android SDK Tools
        • +
        • Android SDK Platform-tools
        • +
        • Android SDK Build-tools(最高版本)
        • +
        +
      2. +
      3. 打开第一个 Android X.X 文件夹(最新版本)并选择: +
          +
        • SDK Platform
        • +
        • 模拟器系统映像,例如
          + ARM EABI v7a System Image
        • +
        +
      4. +
      +
    2. + +
    3. +

      获得其他 API 的支持库

      + + + +

      Android 支持库提供了一组丰富的 API,可兼容大多数版本的 Android。 +

      + +

      打开 Extras 目录并选择:

      +
        +
      • Android Support Repository
      • +
      • Android Support Library
      • +
      + +

       

      +

       

      + +
    4. + + +
    5. +

      为更多 API 获得 Google Play 服务

      + + + +

      要使用 Google API 进行开发,您需要 Google Play 服务包:

      +

      打开 Extras 目录并选择:

      +
        +
      • Google Repository
      • +
      • Google Play services
      • +
      + +

      注:并非所有采用 + Android 技术的设备上都提供 Google Play Services API,但所有安装了 Google Play 商店的设备均提供这些 API。要在 Android 模拟器中使用这些 + API,您还必须安装 SDK 管理器中最新 Android X.X 目录内的 Google APIs + 系统映像。

      +
    6. + + +
    7. +

      安装软件包

      +

      选择想要安装的所有软件包后,即可继续执行安装:

      +
        +
      1. 点击 Install X packages
      2. +
      3. 在下一个窗口中,双击左侧的每个软件包名称, +以接受各软件包的许可协议。
      4. +
      5. 点击 Install
      6. +
      +

      SDK 管理器窗口底部显示下载进度。 + 切勿退出 SDK 管理器,否则它会取消下载。

      +
    8. + +
    9. +

      赶快着手开发吧!

      + +

      现在您的 Android SDK 已包含上述软件包,您随时可以着手开发 + Android 应用。当新工具以及其他 API 推出时,只需启动 SDK 管理器 +,为您的 SDK 下载新软件包。

      + +

      以下几个方案可为您的开发工作提供指导:

      + +
      +
      +

      入门指南

      +

      如果您是第一次开发 Android +应用,请按照开发您的第一款应用指南了解 Android 应用的基础知识。

      + +
      +
      +

      开发穿戴式设备应用

      +

      如果您已准备好着手开发 Android +穿戴式设备应用,请参阅开发 Android Wear 应用

      + +
      +
      +

      使用 Google API

      +

      要开始使用 Google API,例如 Google Maps API 或 Google +Play Game Services +API,请参阅设置 Google Play +服务指南。

      + +
      +
      + + +
    10. + +
    + + diff --git a/docs/html-intl/intl/zh-cn/training/material/animations.jd b/docs/html-intl/intl/zh-cn/training/material/animations.jd new file mode 100644 index 0000000000000000000000000000000000000000..75e5badd21dd8751d63fc3933d9c2acc735ac04f --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=定义定制动画 + +@jd:body + + + + +

    材料设计中的动画将为用户提供操作反馈并在用户与您的应用进行互动时提供视觉连续性。 +材料主题将为按钮与操作行为转换提供一些默认动画,而 Android 5.0(API 级别 21)及更高版本可让您定制这些动画,同时也可创建新动画: + +

    + +
      +
    • 触摸反馈
    • +
    • 循环揭露
    • +
    • 操作行为转换
    • +
    • 曲线运动
    • +
    • 视图状态改变
    • +
    + + +

    定制触摸反馈

    + +

    材料设计中的触摸反馈可在用户与 UI 元素互动时,在接触点上提供即时视觉确认。 +适用于按钮的默认触摸动画使用全新 {@link android.graphics.drawable.RippleDrawable} 类别,以波纹效果实现不同状态间的转换。 + +

    + +

    在大多数情况下,您应以下列方式指定视图背景,在您的视图 XML 中应用此功能: +

    + +
      +
    • ?android:attr/selectableItemBackground 指定有界的波纹。
    • +
    • ?android:attr/selectableItemBackgroundBorderless 指定越过视图边界的波纹。 +它将由一个非空背景的视图的最近父项所绘制和设定边界。 +
    • +
    + +

    注意:selectableItemBackgroundBorderless 是 API 级别 21 中推出的新属性。 +

    + + +

    此外,您可利用 ripple 元素将 {@link android.graphics.drawable.RippleDrawable} +定义为一个 XML 资源。

    + +

    您可以为 {@link android.graphics.drawable.RippleDrawable} 对象指定一种颜色。如果要改变默认触摸反馈颜色,请使用主题的 android:colorControlHighlight +属性。 +

    + +

    如果要了解更多信息,请参阅 {@link +android.graphics.drawable.RippleDrawable} 类别的 API 参考文档。

    + + +

    使用揭露效果

    + +

    当您显示或隐藏一组 UI +元素时,揭露动画可为用户提供视觉连续性。{@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()} 方法让您能够为裁剪区域添加动画以揭露或隐藏视图。 +

    + +

    如果要使用此效果揭露之前不可见的视图:

    + +
    +// previously invisible view
    +View myView = findViewById(R.id.my_view);
    +
    +// get the center for the clipping circle
    +int cx = (myView.getLeft() + myView.getRight()) / 2;
    +int cy = (myView.getTop() + myView.getBottom()) / 2;
    +
    +// get the final radius for the clipping circle
    +int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
    +
    +// create the animator for this view (the start radius is zero)
    +Animator anim =
    +    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
    +
    +// make the view visible and start the animation
    +myView.setVisibility(View.VISIBLE);
    +anim.start();
    +
    + +

    如果要使用此效果隐藏之前可见的视图:

    + +
    +// previously visible view
    +final View myView = findViewById(R.id.my_view);
    +
    +// get the center for the clipping circle
    +int cx = (myView.getLeft() + myView.getRight()) / 2;
    +int cy = (myView.getTop() + myView.getBottom()) / 2;
    +
    +// get the initial radius for the clipping circle
    +int initialRadius = myView.getWidth();
    +
    +// create the animation (the final radius is zero)
    +Animator anim =
    +    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
    +
    +// make the view invisible when the animation is done
    +anim.addListener(new AnimatorListenerAdapter() {
    +    @Override
    +    public void onAnimationEnd(Animator animation) {
    +        super.onAnimationEnd(animation);
    +        myView.setVisibility(View.INVISIBLE);
    +    }
    +});
    +
    +// start the animation
    +anim.start();
    +
    + + +

    定制操作行为转换

    + + +
    +
    + +
    +
    +

    图 1 - 拥有共享元素的转换。 +

    + 如果要重新播放影片,请点击设备屏幕 +
    +
    + +

    材料设计应用中的操作行为转换透过通用元素之间的移动和转换提供不同状态之间的视觉连接。 +您可为进入、退出转换以及操作行为之间的共享元素转换指定定制动画。 +

    + +
      +
    • 进入转换将决定操作行为中视图如何进入场景。例如,在分解进入转换中,视图将从屏幕外进入场景并飞往屏幕中心。 + +
    • + +
    • 退出转换将决定操作行为中应用行为的显示视图如何退出场景。例如,在分解退出转换中,视图将从屏幕中心退出场景。 + +
    • + +
    • 共享元素转换将决定两个操作行为转换之间共享的视图如何在这些操作行为中转换。 +例如,如果两个操作行为拥有相同的图像,但其位置与大小不同,changeImageTransform 共享元素转换将在这些操作行为之间平滑地转换与缩放图像。 + +
    • +
    + +

    Android 5.0(API 级别 21)支持这些进入与退出转换:

    + +
      +
    • 分解 - 从场景中心移入或移出视图。
    • +
    • 滑动 - 从场景边缘移入或移出视图。
    • +
    • 淡入淡出 - 通过调整透明度在场景中增添或移除视图。
    • +
    + +

    任何扩展 {@link android.transition.Visibility} 类别的转换均将获得进入或退出转换支持。 +如果要了解更多信息,请参阅 +{@link android.transition.Transition} 类别的 API 参考文档。

    + +

    Android 5.0(API 级别 21)也支持这些共享元素转换:

    + +
      +
    • changeBounds - 为目标视图的布局边界的变化添加动画。
    • +
    • changeClipBounds - 为目标视图的裁剪边界的变化添加动画。
    • +
    • changeTransform - 为目标视图的缩放与旋转变化添加动画。
    • +
    • changeImageTransform - 为目标图像的大小与缩放变化添加动画。
    • +
    + +

    当您在您的应用中启用操作行为转换,默认的交叉淡入淡出转换将在进入与退出操作行为之间激活。 +

    + + +

    图 2 - 拥有一个共享元素的场景转换。 +

    + +

    指定定制转换

    + +

    首先,在定义您从材料主题继承的风格时,使用 android:windowContentTransitions +属性启用窗口内容转换。您也可在您的风格定义中指定进入、退出以及共享元素转换: +

    + +
    +<style name="BaseAppTheme" parent="android:Theme.Material">
    +  <!-- enable window content transitions -->
    +  <item name="android:windowContentTransitions">true</item>
    +
    +  <!-- specify enter and exit transitions -->
    +  <item name="android:windowEnterTransition">@transition/explode</item>
    +  <item name="android:windowExitTransition">@transition/explode</item>
    +
    +  <!-- specify shared element transitions -->
    +  <item name="android:windowSharedElementEnterTransition">
    +    @transition/change_image_transform</item>
    +  <item name="android:windowSharedElementExitTransition">
    +    @transition/change_image_transform</item>
    +</style>
    +
    + +

    此示例中的 change_image_transform 转换定义如下:

    + +
    +<!-- res/transition/change_image_transform.xml -->
    +<!-- (see also Shared Transitions below) -->
    +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    +  <changeImageTransform/>
    +</transitionSet>
    +
    + +

    changeImageTransform 元素与 +{@link android.transition.ChangeImageTransform} 类别相对应。如果要了解更多信息,请参阅 {@link android.transition.Transition} 的 API +参考。

    + +

    如果要在您的代码中启用窗口内容转换,请调用 +{@link android.view.Window#requestFeature Window.requestFeature()} 方法:

    + +
    +// inside your activity (if you did not enable transitions in your theme)
    +getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    +
    +// set an exit transition
    +getWindow().setExitTransition(new Explode());
    +
    + +

    如果要在您的代码中指定转换,请以 {@link +android.transition.Transition} 对象调用这些方法:

    + +
      +
    • {@link android.view.Window#setEnterTransition Window.setEnterTransition()}
    • +
    • {@link android.view.Window#setExitTransition Window.setExitTransition()}
    • +
    • {@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()}
    • +
    • {@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}
    • +
    + +

    {@link android.view.Window#setExitTransition setExitTransition()} 和 {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} 方法定义正在调用的操作行为的退出转换。 +{@link android.view.Window#setEnterTransition +setEnterTransition()} 与 {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} 方法定义被调用的操作行为的进入转换。

    + +

    如果要取得转换的完整效果,您必须在正在调用以及被调用的操作行为上启用窗口内容转换。 +否则,正在调用的操作行为将开始退出转换,但是您将会看到窗口转换(例如缩放或淡入淡出)。 +

    + +

    如果要尽快开始进入转换,请针对被调用的操作行为使用 +{@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} +方法。这可以让您实现更生动的进入转换。

    + +

    使用转换启动一个操作行为

    + +

    如果您启用转换并且为一个操作行为设置退出转换,那么在您启动另一个操作行为时,转换将以下列方式激活: +

    + +
    +startActivity(intent,
    +              ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    +
    + +

    如果您已为第二个操作行为设置进入转换,则转换也将会在操作行为启动时激活。 +如果要在您启动另一个操作行为时禁用转换,请提供 +null 选项包。

    + +

    以共享元素启动一个操作行为

    + +

    如果要在两个拥有共享元素的操作行为之间安排屏幕转换动画:

    + +
      +
    1. 请在您的主题中启用窗口内容转换。
    2. +
    3. 在您的风格中指定一个共享元素转换。
    4. +
    5. 将您的转换定义为 XML 资源。
    6. +
    7. 利用 +android:transitionName 属性对两个布局中的共享元素指定一个通用名称。
    8. +
    9. 使用 {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()} 方法。
    10. +
    + +
    +// get the element that receives the click event
    +final View imgContainerView = findViewById(R.id.img_container);
    +
    +// get the common element for the transition in this activity
    +final View androidRobotView = findViewById(R.id.image_small);
    +
    +// define a click listener
    +imgContainerView.setOnClickListener(new View.OnClickListener() {
    +    @Override
    +    public void onClick(View view) {
    +        Intent intent = new Intent(this, Activity2.class);
    +        // create the transition animation - the images in the layouts
    +        // of both activities are defined with android:transitionName="robot"
    +        ActivityOptions options = ActivityOptions
    +            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
    +        // start the new activity
    +        startActivity(intent, options.toBundle());
    +    }
    +});
    +
    + +

    对于在您的代码中生成的共享动态视图,请使用 +{@link android.view.View#setTransitionName View.setTransitionName()} 方法在两个操作行为中指定一个通用元素名称。 +

    + +

    如果要在完成第二项操作行为时反转场景转换动画,请调用 +{@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} +方法而非 {@link android.app.Activity#finish Activity.finish()}。

    + +

    以多个共享元素启动一个操作行为

    + +

    如果要在两个拥有多个共享元素的操作行为之间安排场景转换动画,请以 android:transitionName +属性(或在两个操作行为中使用 {@link android.view.View#setTransitionName View.setTransitionName()} 方法)定义共享元素,并以下列方式创建一个 {@link android.app.ActivityOptions} 对象: + +

    + +
    +ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
    +        Pair.create(view1, "agreedName1"),
    +        Pair.create(view2, "agreedName2"));
    +
    + + +

    使用曲线运动

    + +

    材料设计中的动画利用曲线实现时间内插与空间移动模式。 +利用 Android 5.0(API 级别 21)及更高版本,您可为动画定义定制时间曲线以及曲线运动模式。 +

    + +

    {@link android.view.animation.PathInterpolator} 类别是一个基于贝塞尔曲线或 {@link android.graphics.Path} 对象的全新插入器。 +此插入器在一个 1x1 的正方形内指定一个运动曲线,定位点位于 (0,0) 以及 (1,1),而控制点则使用构造函数参数指定。 + +您也可以将路径插入器定义为一个 XML 资源:

    + +
    +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:controlX1="0.4"
    +    android:controlY1="0"
    +    android:controlX2="1"
    +    android:controlY2="1"/>
    +
    + +

    系统将为材料设计规范中的三种基本曲线提供 XML 资源: +

    + +
      +
    • @interpolator/fast_out_linear_in.xml
    • +
    • @interpolator/fast_out_slow_in.xml
    • +
    • @interpolator/linear_out_slow_in.xml
    • +
    + +

    您可以将一个 {@link android.view.animation.PathInterpolator} 对象传递给 {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()} 方法。

    + +

    {@link android.animation.ObjectAnimator} 类别拥有新的构造函数,可让您一次使用两个或更多属性在路径上为坐标添加动画。 +例如,下列动画使用 {@link android.graphics.Path} 对象为视图的 X 和 Y 属性添加动画: +

    + +
    +ObjectAnimator mAnimator;
    +mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
    +...
    +mAnimator.start();
    +
    + + +

    为视图状态改变添加动画

    + +

    {@link android.animation.StateListAnimator} 类别让您能够定义视图状态改变时运行的动画。 +下列示例显示如何将 {@link +android.animation.StateListAnimator} 定义为一个 XML 资源:

    + +
    +<!-- animate the translationZ property of a view when pressed -->
    +<selector xmlns:android="http://schemas.android.com/apk/res/android">
    +  <item android:state_pressed="true">
    +    <set>
    +      <objectAnimator android:propertyName="translationZ"
    +        android:duration="@android:integer/config_shortAnimTime"
    +        android:valueTo="2dp"
    +        android:valueType="floatType"/>
    +        <!-- you could have other objectAnimator elements
    +             here for "x" and "y", or other properties -->
    +    </set>
    +  </item>
    +  <item android:state_enabled="true"
    +    android:state_pressed="false"
    +    android:state_focused="true">
    +    <set>
    +      <objectAnimator android:propertyName="translationZ"
    +        android:duration="100"
    +        android:valueTo="0"
    +        android:valueType="floatType"/>
    +    </set>
    +  </item>
    +</selector>
    +
    + +

    如果要将定制视图状态动画附加至一个视图,请依照此示例使用 XML 资源文件中的 +selector 元素定义一个动画,并使用 android:stateListAnimator 属性将此动画分配给您的视图。 +如果要将一个状态列表动画分配给您的代码内的一个视图,请使用 {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()} 方法,并以 +{@link android.view.View#setStateListAnimator View.setStateListAnimator()} 方法将动画分配给您的视图。 +

    + +

    当您的主题扩展材料主题时,在默认情况下按钮将拥有一个 Z 动画。如果要避免您的按钮出现这类行为,请将 android:stateListAnimator 属性设置为 +@null。 +

    + +

    {@link android.graphics.drawable.AnimatedStateListDrawable} 类别让您能够创建图片,显示相关视图之间的状态变化。 +Android 5.0 中的某些系统小组件在默认情况下使用这些动画。 +下列示例显示如何将 {@link android.graphics.drawable.AnimatedStateListDrawable} 定义为一个 XML 资源: +

    + +
    +<!-- res/drawable/myanimstatedrawable.xml -->
    +<animated-selector
    +    xmlns:android="http://schemas.android.com/apk/res/android">
    +
    +    <!-- provide a different drawable for each state-->
    +    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
    +        android:state_pressed="true"/>
    +    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
    +        android:state_focused="true"/>
    +    <item android:id="@id/default"
    +        android:drawable="@drawable/drawableD"/>
    +
    +    <!-- specify a transition -->
    +    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
    +        <animation-list>
    +            <item android:duration="15" android:drawable="@drawable/dt1"/>
    +            <item android:duration="15" android:drawable="@drawable/dt2"/>
    +            ...
    +        </animation-list>
    +    </transition>
    +    ...
    +</animated-selector>
    +
    + + +

    为矢量图片添加动画

    + +

    矢量图片可在不丢失定义的情况下缩放。 +{@link android.graphics.drawable.AnimatedVectorDrawable} +类别可让您为矢量图片的属性添加动画。

    + +

    您通常可以在 3 个 XML 文件中定义添加动画的矢量图片:

    + +
      +
    • 在 +res/drawable/ 中拥有 <vector> 元素的矢量图片
    • +
    • 在 +res/drawable/ 中拥有 <animated-vector> 元素且已添加动画的矢量图片
    • +
    • 在 +res/anim/ 中拥有 <objectAnimator> 元素的一个或多个对象动画
    • +
    + +

    添加动画的矢量图片可为 <group> 以及 +<path> 元素的属性添加动画。<group> 元素定义路径集或子组,而 <path> 元素则定义将绘制的路径。 +

    + +

    当您定义一个您想要添加动画的矢量图片时,请使用 android:name +属性给这些群组和路径指定一个唯一名称,以便让您能够从您的动画定义中引用这些群组或路径。 +例如:

    + +
    +<!-- res/drawable/vectordrawable.xml -->
    +<vector xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:height="64dp"
    +    android:width="64dp"
    +    android:viewportHeight="600"
    +    android:viewportWidth="600">
    +    <group
    +        android:name="rotationGroup"
    +        android:pivotX="300.0"
    +        android:pivotY="300.0"
    +        android:rotation="45.0" >
    +        <path
    +            android:name="v"
    +            android:fillColor="#000000"
    +            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    +    </group>
    +</vector>
    +
    + +

    已添加动画的矢量图片定义按名称引用矢量图片内的群组和路径: +

    + +
    +<!-- res/drawable/animvectordrawable.xml -->
    +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    +  android:drawable="@drawable/vectordrawable" >
    +    <target
    +        android:name="rotationGroup"
    +        android:animation="@anim/rotation" />
    +    <target
    +        android:name="v"
    +        android:animation="@anim/path_morph" />
    +</animated-vector>
    +
    + +

    动画定义代表着 {@link android.animation.ObjectAnimator} 或 {@link +android.animation.AnimatorSet} 对象。此示例中的第一个动画将目标群组旋转 360 度: +

    + +
    +<!-- res/anim/rotation.xml -->
    +<objectAnimator
    +    android:duration="6000"
    +    android:propertyName="rotation"
    +    android:valueFrom="0"
    +    android:valueTo="360" />
    +
    + +

    此示例中的第二个动画对矢量图片的路径进行变形。 +两个路径均需可兼容变形操作:两个路径均需拥有相同数量的指令,而且每个指令均需拥有相同数量的参数。 +

    + +
    +<!-- res/anim/path_morph.xml -->
    +<set xmlns:android="http://schemas.android.com/apk/res/android">
    +    <objectAnimator
    +        android:duration="3000"
    +        android:propertyName="pathData"
    +        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
    +        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
    +        android:valueType="pathType" />
    +</set>
    +
    + +

    如果要了解更多信息,请参阅 {@link +android.graphics.drawable.AnimatedVectorDrawable} 的 API 参考文档。

    diff --git a/docs/html-intl/intl/zh-cn/training/material/compatibility.jd b/docs/html-intl/intl/zh-cn/training/material/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..aa23d4b5e61419fc9f5ca708d1b58f8b5bbff4d2 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=维护兼容性 + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 定义备用样式
    2. +
    3. 提供备用布局
    4. +
    5. 使用支持内容库
    6. +
    7. 检查系统版本
    8. +
    +

    您也应该阅读

    + +
    +
    + + +

    有些材料设计功能(例如材料主题和定制操作行为转换)仅在 Android 5.0(API 级别 21)及更高版本中提供。 +不过,您可为您的应用进行设计,使应用在支持材料设计的设备上运行时可使用这些功能,且同时能够与运行早期版本 Android 的设备兼容。 + +

    + + +

    定义备用样式

    + +

    您可对您的应用进行配置,使应用能够在支持它的设备上使用材料主题,并且能够在运行早期版本 Android 的设备上还原至早期版本的主题: +

    + +
      +
    1. 定义一个从 +res/values/styles.xml 中的早期版本主题(例如 Holo)继承的主题。
    2. +
    3. 定义一个从 +res/values-v21/styles.xml 中的材料主题继承且拥有相同名称的主题。
    4. +
    5. 在清单文件中将此主题设置为您的应用主题。
    6. +
    + +

    注意:如果您的应用使用材料主题,但没有以这方式提供备用主题,您的应用将无法在 Android 5.0 之前的 Android 版本上运行。 + + +

    + + +

    提供备用布局

    + +

    如果您根据材料设计指导方针设计的布局没有使用 Android 5.0(API 级别 21)所推出的新 XML 属性,这些布局将能够在早期版本的 Android 上运行。 + +或者,您也可以提供备用布局。您也可以提供备用布局以定制应用在早期版本的 Android 上的呈现方式。 +

    + +

    您可在 res/layout-v21/ 内创建用于 Android 5.0(API 级别 21)的布局文件,同时也可在 res/layout/ 内创建用于早期版本 Android 的备用布局文件。例如,res/layout/my_activity.xml 是 +res/layout-v21/my_activity.xml 的备用布局。 + +

    + +

    为避免代码重复,请在 res/values/ 内定义您的风格,在 res/values-v21/ 内为新 API 修改风格,以及使用风格继承(即:在 res/values/ 内定义基础风格,然后在 res/values-v21/ 内继承这些风格)。 + +

    + + +

    使用支持内容库

    + +

    v7 支持内容库 +r21 及更高版本包括下列材料设计功能:

    + + + +

    系统小组件

    + +

    Theme.AppCompat 主题可为这些小组件提供材料设计风格:

    + +
      +
    • {@link android.widget.EditText}
    • +
    • {@link android.widget.Spinner}
    • +
    • {@link android.widget.CheckBox}
    • +
    • {@link android.widget.RadioButton}
    • +
    • {@link android.support.v7.widget.SwitchCompat}
    • +
    • {@link android.widget.CheckedTextView}
    • +
    + +

    配色工具

    + +

    如果要利用 Android v7 支持内容库取得材料设计风格以及定制配色工具,请应用其中一个 Theme.AppCompat 主题: +

    + +
    +<!-- extend one of the Theme.AppCompat themes -->
    +<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
    +    <!-- customize the color palette -->
    +    <item name="colorPrimary">@color/material_blue_500</item>
    +    <item name="colorPrimaryDark">@color/material_blue_700</item>
    +    <item name="colorAccent">@color/material_green_A200</item>
    +</style>
    +
    + +

    列表和卡片

    + +

    {@link android.support.v7.widget.RecyclerView} 以及 {@link +android.support.v7.widget.CardView} 小组件可通过 Android v7 支持内容库提供给早期版本 Android,但有如下限制: +

    +
      +
    • {@link android.support.v7.widget.CardView} 使用额外边距返回编程阴影实现。 +
    • +
    • {@link android.support.v7.widget.CardView} 不会裁剪其与圆角相交的子视图。 +
    • +
    + + +

    依赖项

    + +

    如果要在 Android 5.0(API 级别 21)之前的 Android 版本中使用这些功能,请将 +Android v7 支持内容库作为 Gradle 依赖项包括在您的项目中:

    + +
    +dependencies {
    +    compile 'com.android.support:appcompat-v7:21.0.+'
    +    compile 'com.android.support:cardview-v7:21.0.+'
    +    compile 'com.android.support:recyclerview-v7:21.0.+'
    +}
    +
    + + +

    检查系统版本

    + +

    下列功能仅在 Android 5.0(API 级别 21)及更高版本中提供:

    + +
      +
    • 操作行为转换
    • +
    • 触摸反馈
    • +
    • 揭露动画
    • +
    • 基于路径的动画
    • +
    • 矢量图片
    • +
    • 图片着色
    • +
    + +

    如果要保留与早期版本 Android 的兼容性,请您在运行时检查系统 {@link +android.os.Build.VERSION#SDK_INT version},然后再为下列的任何一个功能调用 API: +

    + +
    +// Check if we're running on Android 5.0 or higher
    +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    +    // Call some material design APIs here
    +} else {
    +    // Implement this feature without material design
    +}
    +
    + +

    注意:如果要指定您的应用所支持的 Android 版本,请使用您的清单文件中的 android:minSdkVersion 以及 android:targetSdkVersion +属性。 +如果要在 Android 5.0 内使用材料设计功能,请将 android:targetSdkVersion 属性设置为 21。 +如果要了解更多信息,请参阅 +<uses-sdk> API +指南

    diff --git a/docs/html-intl/intl/zh-cn/training/material/drawables.jd b/docs/html-intl/intl/zh-cn/training/material/drawables.jd new file mode 100644 index 0000000000000000000000000000000000000000..8a654317fb318a72ae2e7325fc393b56c15cb677 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=使用图片 + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 为图片资源着色
    2. +
    3. 从图像萃取突出颜色
    4. +
    5. 创建矢量图片
    6. +
    +

    您也应该阅读

    + +
    +
    + +

    下列适用于图片的功能将协助您在您的应用中实现材料设计:

    + +
      +
    • 图片着色
    • +
    • 突出颜色萃取
    • +
    • 矢量图片
    • +
    + +

    本课程将向您展示如何在您的应用中使用这些功能。

    + + +

    为图片资源着色

    + +

    利用 Android 5.0(API 级别 21)及更高版本,您可为位图以及定义为 Alpha 蒙版的点九图着色。 +您可使用颜色资源或分解为颜色资源(例如 ?android:attr/colorPrimary)的主题属性为其着色。 +通常,您只有一次机会创建这些资产并自动为其上色以符合您的主题。 +

    + +

    您可利用 {@code setTint()} 方法为 {@link android.graphics.drawable.BitmapDrawable} 或 {@link +android.graphics.drawable.NinePatchDrawable} 对象着色。您也可以利用 android:tint 以及 +android:tintMode 属性设置您的布局中的着色颜色和模式。 +

    + + +

    从图像萃取突出颜色

    + +

    Android 支持内容库 r21 及更高版本包括 {@link +android.support.v7.graphics.Palette} 类别,可让您从图像萃取突出颜色。此类别将萃取下列突出颜色: +

    + +
      +
    • 鲜艳
    • +
    • 鲜艳深色
    • +
    • 鲜艳浅色
    • +
    • 低调
    • +
    • 低调深色
    • +
    • 低调浅色
    • +
    + +

    如果要萃取这些颜色,请将 {@link android.graphics.Bitmap} 对象传递给位于您上载图像的背景线程的 +{@link android.support.v7.graphics.Palette#generate Palette.generate()} 静态方法。 +如果您无法使用此线程,请调用 +{@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} 方法并提供一个侦听程序。 +

    + +

    您可使用 +Palette 类别(例如 Palette.getVibrantColor)中的 getter 方法从图像检索突出颜色。

    + +

    如果要在项目中使用 {@link android.support.v7.graphics.Palette} 类别,请将下列 +Gradle 依赖项添加至您的应用模块: +

    + +
    +dependencies {
    +    ...
    +    compile 'com.android.support:palette-v7:21.0.0'
    +}
    +
    + +

    如果要了解更多信息,请参阅{@link android.support.v7.graphics.Palette} +类别的 API 参考文档。

    + + +

    创建矢量图片

    + + + +
    +

    视频

    +

    Android 矢量图形

    +
    +
    + +

    在 Android 5.0(API 级别 21)及更高版本中,您可定义矢量图片,而且图片可在不损失清晰度的情况下缩放。 +您只需一个资产文件即可创建一个矢量图像,而位图图像则需要为每个屏幕密度提供一个资产文件。 +如果要创建一个矢量图像,请您在 <vector> XML 元素中定义形状的详情。 +

    + +

    下列示例以心形定义一个矢量图像:

    + +
    +<!-- res/drawable/heart.xml -->
    +<vector xmlns:android="http://schemas.android.com/apk/res/android"
    +    <!-- intrinsic size of the drawable -->
    +    android:height="256dp"
    +    android:width="256dp"
    +    <!-- size of the virtual canvas -->
    +    android:viewportWidth="32"
    +    android:viewportHeight="32">
    +
    +  <!-- draw a path -->
    +  <path android:fillColor="#8fff"
    +      android:pathData="M20.5,9.5
    +                        c-1.955,0,-3.83,1.268,-4.5,3
    +                        c-0.67,-1.732,-2.547,-3,-4.5,-3
    +                        C8.957,9.5,7,11.432,7,14
    +                        c0,3.53,3.793,6.257,9,11.5
    +                        c5.207,-5.242,9,-7.97,9,-11.5
    +                        C25,11.432,23.043,9.5,20.5,9.5z" />
    +</vector>
    +
    + +

    矢量图像在 Android 中以 {@link android.graphics.drawable.VectorDrawable} +对象表示。如果要了解更多有关 pathData 语法的信息,请参阅 SVG 路径参考。如果要了解更多有关为矢量图片属性添加动画的信息,请参阅为矢量图片添加动画。 + +

    diff --git a/docs/html-intl/intl/zh-cn/training/material/get-started.jd b/docs/html-intl/intl/zh-cn/training/material/get-started.jd new file mode 100644 index 0000000000000000000000000000000000000000..16db97bd415707e6f870b4b4ffdd1da24491fb85 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=入门指南 + +@jd:body + + + + +

    如果要使用材料设计创建应用:

    + +
      +
    1. + 请查阅材料设计规范
    2. +
    3. + 在您的应用中使用材料主题
    4. +
    5. + 遵循材料设计指导方针创建您的布局
    6. +
    7. + 指定您视图要投射阴影的高度
    8. +
    9. + 使用系统小组件呈现列表与卡片。
    10. +
    11. + 定制您的应用中的动画
    12. +
    + +

    保持后向兼容性

    + +

    您可将多个材料设计功能添加至您的应用,同时保持与 Android 5.0 之前的 Android 版本的兼容性。 +如果要了解更多信息,请参阅 +保持兼容性

    + +

    使用材料设计更新您的应用

    + +

    如果要更新现有应用以纳入材料设计,请遵循材料设计指导方针更新您的布局。 +同时也须确保纳入深度、触摸反馈和动画。 +

    + +

    使用材料设计创建新应用

    + +

    如果您要使用材料设计功能创建新应用,材料设计指导方针将为您提供一个紧密结合的设计框架。 +请遵循这些指导方针并使用 Android +框架中的新功能来设计与开发您的应用。

    + + +

    使用材料主题

    + +

    如果要在您的应用中使用材料主题,请指定一个从 +android:Theme.Material 继承的风格:

    + +
    +<!-- res/values/styles.xml -->
    +<resources>
    +  <!-- your theme inherits from the material theme -->
    +  <style name="AppTheme" parent="android:Theme.Material">
    +    <!-- theme customizations -->
    +  </style>
    +</resources>
    +
    + +

    材料主题提供更新的系统小组件,让您能够为触摸反馈以及操作行为转换设置配色工具以及默认动画。 +有关更多详情,请参阅使用材料主题。 +

    + + +

    设计布局

    + +

    除了应用及定制材料主题,您的布局同时也应符合材料设计指导方针。 +设计布局时,请特别注意下列几点: +

    + +
      +
    • 基线格点
    • +
    • 关键线
    • +
    • 间距
    • +
    • 触摸目标大小
    • +
    • 布局结构
    • +
    + + +

    指定您视图内的高度

    + +

    视图可透射阴影,而视图的高度值将决定其阴影的大小以及其显示顺序。 +如果要设置视图的高度,请使用您的布局中的 +android:elevation 属性:

    + +
    +<TextView
    +    android:id="@+id/my_textview"
    +    android:layout_width="wrap_content"
    +    android:layout_height="wrap_content"
    +    android:text="@string/next"
    +    android:background="@color/white"
    +    android:elevation="5dp" />
    +
    + +

    新的 translationZ 属性可让您创建反映出视图高度临时变化的动画。 +高度变化可在响应触摸手势时发挥作用。 + +

    + +

    有关更多详情,请参阅定义阴影与裁剪视图。 +

    + + +

    创建列表与卡片

    + +

    {@link android.support.v7.widget.RecyclerView} 是 {@link +android.widget.ListView} 的可插入版本,支持不同布局类型,具有更高性能。 +{@link android.support.v7.widget.CardView} 让您能够展示卡片内的各种信息并且在各种应用中实现一致的呈现方式。 +下列代码示例将展示如何将 +{@link android.support.v7.widget.CardView} 包括在您的布局中:

    + +
    +<android.support.v7.widget.CardView
    +    android:id="@+id/card_view"
    +    android:layout_width="200dp"
    +    android:layout_height="200dp"
    +    card_view:cardCornerRadius="3dp">
    +    ...
    +</android.support.v7.widget.CardView>
    +
    + +

    如需了解详细信息,请参阅创建列表和卡片。 +

    + + +

    定制您的动画

    + +

    Android 5.0(API 级别 21)包括新 API,可用于在您的应用中创建定制动画。例如,您可启用操作行为转换并定义操作行为内的退出转换: + +

    + +
    +public class MyActivity extends Activity {
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // enable transitions
    +        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    +        setContentView(R.layout.activity_my);
    +    }
    +
    +    public void onSomeButtonClicked(View view) {
    +        getWindow().setExitTransition(new Explode());
    +        Intent intent = new Intent(this, MyOtherActivity.class);
    +        startActivity(intent,
    +                      ActivityOptions
    +                          .makeSceneTransitionAnimation(this).toBundle());
    +    }
    +}
    +
    + +

    当您从此操作行为启动另一个操作行为时,退出转换将被激活。

    + +

    如果要了解有关新动画 API 的更多详情,请参阅定义定制动画

    diff --git a/docs/html-intl/intl/zh-cn/training/material/index.jd b/docs/html-intl/intl/zh-cn/training/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..dea14839ad8dcd7919e5fcc7a0c991e1d61b77aa --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=面向开发者的材料设计 +page.type=设计 +page.image=images/cards/material_2x.png +page.metaDescription=学习如何将材料设计运用到您的应用中。 + + +@jd:body + +
    +
    +

    依赖项和先决条件

    +
      +
    • Android 5.0(API 级别 21)
    • +
    +
    +
    + +

    材料设计是专为设计适用于多个平台和设备的视觉、运动与互动效果而制定的综合指南。 +如果要在您的 Android 应用中使用材料设计,请遵循材料设计规范内所述的指导方针,并使用 Android 5.0(API 级别 21)所提供的新组件与功能。 + + + +

    + +

    本课程将向您介绍如何利用下列元素创建材料设计应用:

    + +
      +
    • 材料主题
    • +
    • 用于创建卡片与列表的小组件
    • +
    • 定制阴影以及视图裁剪
    • +
    • 矢量图片
    • +
    • 定制动画
    • +
    + +

    本课程也将向您展示在您的应用中使用材料设计功能时应如何保持与 Android +5.0(API 级别 21)之前的 Android 版本的兼容性。

    + +

    课程

    + +
    +
    入门指南
    +
    了解如何使用材料设计功能更新您的应用。
    + +
    使用材料主题
    +
    了解如何在您的应用中使用材料设计风格。
    + +
    创建列表与卡片
    +
    了解如何使用系统小组件创建拥有一致的呈现方式与风格的列表与卡片。
    + +
    定义阴影与裁剪视图
    +
    了解如何为您的视图设置高度以便创建定制阴影以及如何裁剪视图。
    + +
    使用 Drawables
    +
    了解如何创建矢量图片以及如何为资源着色。
    + +
    定义定制动画
    +
    了解如何为视图以及拥有共享元素的操作行为创建定制动画。
    + +
    维护兼容性
    +
    了解如何维护与 Android 5.0 之前各种平台版本的兼容性。
    +
    diff --git a/docs/html-intl/intl/zh-cn/training/material/lists-cards.jd b/docs/html-intl/intl/zh-cn/training/material/lists-cards.jd new file mode 100644 index 0000000000000000000000000000000000000000..47efe26b4559cefe14b59f70d11fc303c191db09 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=创建列表与卡片 + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 创建列表
    2. +
    3. 创建卡片
    4. +
    5. 添加依赖关系
    6. +
    +

    您也应该阅读

    + +
    +
    + + +

    如果要在您的应用中使用材料设计风格创建复杂列表与卡片,您可使用 +{@link android.support.v7.widget.RecyclerView} 以及 {@link android.support.v7.widget.CardView} +小组件。

    + + +

    创建列表

    + +

    {@link android.support.v7.widget.RecyclerView} 小组件比 {@link android.widget.ListView} 更高级且更具灵活性。 +此小组件是一个用于显示庞大数据集的容器,可通过保持有限数量的视图进行非常有效的滚动操作。 +如果您有数据集合,其中的元素将因用户操作或网络事件而发生改变,请使用 +{@link android.support.v7.widget.RecyclerView} 小组件。 +

    + +

    {@link android.support.v7.widget.RecyclerView} 类别将通过提供下列功能简化庞大数据集的显示与处理: +

    + +
      +
    • 用于项目定位的布局管理器
    • +
    • 用于通用项目操作(例如删除或添加项目)的默认动画
    • +
    + +

    您也可灵活选择如何为 {@link +android.support.v7.widget.RecyclerView} 小组件定义定制布局管理器与动画。

    + + +

    +图 1.RecyclerView 小组件。 +

    + +

    如果要使用 {@link android.support.v7.widget.RecyclerView} 小组件,您必须指定一个适配器和一个布局管理器。 +如果要创建一个适配器,请扩展 {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter} 类别。实现的详情将取决于数据集的具体信息以及视图的类型。 +如果要了解更多信息,请参阅下列示例。 +

    + +
    + +

    +图 2 - 随附 RecyclerView 的列表。 +

    +
    + +

    布局管理器将确定 {@link +android.support.v7.widget.RecyclerView} 内各项目视图的位置并决定何时重新使用用户已不可见的项目视图。 +如果要重新使用(或重复使用)一个视图,布局管理器可能会要求适配器以数据集中的另一个元素替换视图的内容。 +以此方式重复使用视图将可避免创建不必要的视图或执行成本高昂的 {@link android.app.Activity#findViewById findViewById()} 查找,从而改善性能。 + +

    + +

    {@link android.support.v7.widget.RecyclerView} 提供这些内置布局管理器:

    + +
      +
    • {@link android.support.v7.widget.LinearLayoutManager} 以垂直或水平滚动列表方式显示项目。 +
    • +
    • {@link android.support.v7.widget.GridLayoutManager} 在网格中显示项目。
    • +
    • {@link android.support.v7.widget.StaggeredGridLayoutManager} 在分散对齐网格中显示项目。
    • +
    + +

    如果要创建一个定制布局管理器,请扩展 {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager} 类别。

    + +

    动画

    + +

    {@link +android.support.v7.widget.RecyclerView} 在默认情况下启用增添与删除项目的动画。如果要定制这些动画,请扩展 +{@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} 类别并使用 {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} +方法。 +

    + +

    示例

    + +

    下列代码示例将展示如何将 +{@link android.support.v7.widget.RecyclerView} 添加至布局:

    + +
    +<!-- A RecyclerView with some commonly used attributes -->
    +<android.support.v7.widget.RecyclerView
    +    android:id="@+id/my_recycler_view"
    +    android:scrollbars="vertical"
    +    android:layout_width="match_parent"
    +    android:layout_height="match_parent"/>
    +
    + +

    将 {@link android.support.v7.widget.RecyclerView} 小组件添加至您的布局后,立即获取对象图柄并将其连接至布局管理器,同时附加一个适配器以便显示数据: + +

    + +
    +public class MyActivity extends Activity {
    +    private RecyclerView mRecyclerView;
    +    private RecyclerView.Adapter mAdapter;
    +    private RecyclerView.LayoutManager mLayoutManager;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.my_activity);
    +        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
    +
    +        // use this setting to improve performance if you know that changes
    +        // in content do not change the layout size of the RecyclerView
    +        mRecyclerView.setHasFixedSize(true);
    +
    +        // use a linear layout manager
    +        mLayoutManager = new LinearLayoutManager(this);
    +        mRecyclerView.setLayoutManager(mLayoutManager);
    +
    +        // specify an adapter (see also next example)
    +        mAdapter = new MyAdapter(myDataset);
    +        mRecyclerView.setAdapter(mAdapter);
    +    }
    +    ...
    +}
    +
    + +

    适配器可让您存取数据集中的项目,为项目创建视图,并且在原始项目不再可见时以新数据项目替换视图的某些内容。 + +下列代码示例将展示一个简单的实现,目标为一个包含使用 {@link android.widget.TextView} 小组件显示的字符串阵列的数据集: +

    + +
    +public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    +    private String[] mDataset;
    +
    +    // Provide a reference to the views for each data item
    +    // Complex data items may need more than one view per item, and
    +    // you provide access to all the views for a data item in a view holder
    +    public static class ViewHolder extends RecyclerView.ViewHolder {
    +        // each data item is just a string in this case
    +        public TextView mTextView;
    +        public ViewHolder(TextView v) {
    +            super(v);
    +            mTextView = v;
    +        }
    +    }
    +
    +    // Provide a suitable constructor (depends on the kind of dataset)
    +    public MyAdapter(String[] myDataset) {
    +        mDataset = myDataset;
    +    }
    +
    +    // Create new views (invoked by the layout manager)
    +    @Override
    +    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
    +                                                   int viewType) {
    +        // create a new view
    +        View v = LayoutInflater.from(parent.getContext())
    +                               .inflate(R.layout.my_text_view, parent, false);
    +        // set the view's size, margins, paddings and layout parameters
    +        ...
    +        ViewHolder vh = new ViewHolder(v);
    +        return vh;
    +    }
    +
    +    // Replace the contents of a view (invoked by the layout manager)
    +    @Override
    +    public void onBindViewHolder(ViewHolder holder, int position) {
    +        // - get element from your dataset at this position
    +        // - replace the contents of the view with that element
    +        holder.mTextView.setText(mDataset[position]);
    +
    +    }
    +
    +    // Return the size of your dataset (invoked by the layout manager)
    +    @Override
    +    public int getItemCount() {
    +        return mDataset.length;
    +    }
    +}
    +
    + + +
    + +

    +图 3.卡片示例。 +

    +
    + +

    创建卡片

    + +

    {@link android.support.v7.widget.CardView} 扩展 {@link android.widget.FrameLayout} 类别并让您能够显示卡片内的信息,这些信息在整个平台中拥有一致的呈现方式。{@link +android.support.v7.widget.CardView} 小组件可拥有阴影和圆角。 +

    + +

    如果要使用阴影创建卡片,请使用 card_view:cardElevation 属性。{@link android.support.v7.widget.CardView} 在 Android 5.0(API 级别 21)及更高版本中使用真实高度与动态阴影,而在早期的 Android 版本中则返回编程阴影实现。如需了解详细信息,请参阅保持兼容性 + + + +

    + +

    使用这些属性定制 +{@link android.support.v7.widget.CardView} 小组件的外观:

    + +
      +
    • 如果要在您的布局中设置圆角半径,请使用 card_view:cardCornerRadius +属性。
    • +
    • 如果要在您的代码中设置圆角半径,请使用 CardView.setRadius 方法。
    • +
    • 如果要设置卡片的背景颜色,请使用 card_view:cardBackgroundColor +属性。
    • +
    + +

    下列代码示例将展示如何将 {@link android.support.v7.widget.CardView} +小组件包括在您的布局中:

    + +
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    xmlns:tools="http://schemas.android.com/tools"
    +    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    +    ... >
    +    <!-- A CardView that contains a TextView -->
    +    <android.support.v7.widget.CardView
    +        xmlns:card_view="http://schemas.android.com/apk/res-auto"
    +        android:id="@+id/card_view"
    +        android:layout_gravity="center"
    +        android:layout_width="200dp"
    +        android:layout_height="200dp"
    +        card_view:cardCornerRadius="4dp">
    +
    +        <TextView
    +            android:id="@+id/info_text"
    +            android:layout_width="match_parent"
    +            android:layout_height="match_parent" />
    +    </android.support.v7.widget.CardView>
    +</LinearLayout>
    +
    + +

    如果要了解更多信息,请参阅 {@link android.support.v7.widget.CardView} 的 API 参考。

    + + +

    添加依赖项

    + +

    {@link android.support.v7.widget.RecyclerView} 与 {@link android.support.v7.widget.CardView} +小组件为 v7 支持内容库的一部分。 +如果要在您的项目中使用这些小组件,请将这些 +Gradle 依赖项添加至您的应用模块: +

    + +
    +dependencies {
    +    ...
    +    compile 'com.android.support:cardview-v7:21.0.+'
    +    compile 'com.android.support:recyclerview-v7:21.0.+'
    +}
    +
    diff --git a/docs/html-intl/intl/zh-cn/training/material/shadows-clipping.jd b/docs/html-intl/intl/zh-cn/training/material/shadows-clipping.jd new file mode 100644 index 0000000000000000000000000000000000000000..e063d33d4da4c4a55078af7c74b68c28da3f31a2 --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=定义阴影与裁剪视图 + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 指定视图高度
    2. +
    3. 定制视图阴影与轮廓
    4. +
    5. 裁剪视图
    6. +
    +

    您也应该阅读

    + +
    +
    + +

    材料设计为 UI 元素引入高度。高度将帮助用户了解每个元素的相对重要性,让他们能够集中于手边的工作。 +

    + +

    由 Z 属性所表示的视图高度将决定其阴影的视觉外观:拥有较高 Z 值的视图将投射更大且更柔和的阴影。 +拥有较高 Z 值的视图将挡住拥有较低 Z 值的视图;不过视图的 Z 值并不影响视图的大小。 +

    + +

    阴影是由提升的视图的父项所绘制,因此将受到标准视图裁剪的影响,而在默认情况下裁剪将由父项执行。 +

    + +

    此外,在创建其中的小组件在执行某些操作行为时会暂时升至视图平面之上的动画时,高度也很实用。 +

    + +

    要了解更多有关材料设计高度的信息,请参阅 +3D 空间内的对象。 +

    + + +

    指定视图高度

    + +

    视图的 Z 值包含两个组件: + +

      +
    • 高度:静态组件。
    • +
    • 转换:用于动画的动态组件。
    • +
    + +

    Z = elevation + translationZ

    + + +

    图 1 - 不同视图高度的阴影。

    + +

    如果要在布局定义中设置视图的高度,请使用 android:elevation + 属性。如果要在操作行为的代码中设置视图高度,请使用 +{@link android.view.View#setElevation View.setElevation()} 方法。

    + +

    如果要设置视图转换,请使用 {@link android.view.View#setTranslationZ +View.setTranslationZ()} 方法。

    + +

    全新的 {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} 与 {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} 方法让您能够轻松地为视图高度添加动画。 +如果要了解更多信息,请参阅 +{@link android.view.ViewPropertyAnimator} 的 API 参考以及属性动画开发指南。 +

    + +

    您也可使用 {@link android.animation.StateListAnimator} 以说明性方式指定这些动画。 +当状态改变会触发动画(例如当用户按下按钮)时,此方法特别有用。 +如果要了解更多信息,请参阅为视图状态改变添加动画。 +

    + +

    Z 值以 dp(密度独立像素)为单位度量。

    + + +

    定制视图阴影与轮廓

    + +

    视图的背景图片的边界将决定其阴影的默认形状。轮廓代表图形对象的外形并定义触摸反馈的波纹区域。 + +

    + +

    下面举一个以背景图片定义的视图示例:

    + +
    +<TextView
    +    android:id="@+id/myview"
    +    ...
    +    android:elevation="2dp"
    +    android:background="@drawable/myrect" />
    +
    + +

    背景图片被定义为一个拥有圆角的矩形:

    + +
    +<!-- res/drawable/myrect.xml -->
    +<shape xmlns:android="http://schemas.android.com/apk/res/android"
    +       android:shape="rectangle">
    +    <solid android:color="#42000000" />
    +    <corners android:radius="5dp" />
    +</shape>
    +
    + +

    视图将投射一个带有圆角的阴影,因为背景图片将定义视图的轮廓。 +如果提供一个定制轮廓,则此轮廓将替代视图阴影的默认形状。

    + +

    如果要为您的代码中的视图定义定制轮廓:

    + +

      +
    1. 扩展 {@link android.view.ViewOutlineProvider} 类别。
    2. +
    3. 替代 {@link android.view.ViewOutlineProvider#getOutline getOutline()} 方法。
    4. +
    5. 利用 {@link +android.view.View#setOutlineProvider View.setOutlineProvider()} 方法向您的视图指定新的轮廓提供程序。
    6. +
    + +

    您可使用 +{@link android.graphics.Outline} 类别中的方法创建带有圆角的椭圆形和矩形轮廓。视图的默认轮廓提供程序将从视图背景取得轮廓。 +如果要防止视图投射阴影,请将其轮廓提供程序设置为 null。 +

    + + +

    裁剪视图

    + +

    裁剪视图让您能够轻松改变视图形状。您可以裁剪视图,以便与其他设计元素保持一致,也可以根据用户输入改变视图形状。您可使用 {@link android.view.View#setClipToOutline +View.setClipToOutline()} 方法或 android:clipToOutline 属性将视图裁剪至其轮廓区域。 + +由 +{@link android.graphics.Outline#canClip Outline.canClip()} 方法所决定,仅有矩形、圆形和圆角矩形轮廓支持裁剪。 +

    + +

    如果要将视图裁剪至图片的形状,请将图片设置为视图背景(如上所示)并调用 {@link android.view.View#setClipToOutline View.setClipToOutline()} +方法。 +

    + +

    裁剪视图是一个成本高昂的操作,因此不可为您用于裁剪视图的形状添加动画。 +如果要实现此效果,请使用揭露效果动画。

    diff --git a/docs/html-intl/intl/zh-cn/training/material/theme.jd b/docs/html-intl/intl/zh-cn/training/material/theme.jd new file mode 100644 index 0000000000000000000000000000000000000000..5a45d4b93d6f3591026db75e58f60c97b4ccfe3c --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=使用材料主题 + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 定制配色工具
    2. +
    3. 定制状态栏
    4. +
    5. 主题个别视图
    6. +
    +

    您也应该阅读

    + +
    +
    + + +

    新材料主题将提供:

    + +
      +
    • 让您设置主题配色工具的系统小组件
    • +
    • 适用于系统小组件的触摸反馈动画
    • +
    • 操作行为转换动画
    • +
    + +

    您可使用您所控制的配色工具,按照您的品牌形象定制材料主题的外观。 +您可使用主题属性为操作栏和状态栏着色,如图 3 所示。 +

    + +

    系统小组件拥有全新的设计与触摸反馈动画。您可为您的应用定制配色工具、触摸反馈动画以及操作行为转换。 +

    + +

    材料主题的定义为:

    + +
      +
    • @android:style/Theme.Material(深色版本)
    • +
    • @android:style/Theme.Material.Light(浅色版本)
    • +
    • @android:style/Theme.Material.Light.DarkActionBar
    • +
    + +

    如果要了解您可使用的材料风格,请参阅 +{@link android.R.style R.style} 的 API 参考。

    + + +
    +
    + +
    +

    图 1.深色材料主题

    +
    +
    +
    + +
    +

    图 2.浅色材料主题

    +
    +
    +
    +
    + +

    +注意:材料主题仅在 Android 5.0(API 级别 21)及更高版本中提供。 +v7 支持内容库为一些小组件提供附带材料设计风格的主题,同时为配色工具定制提供支持。 + +如果要了解更多信息,请参阅 +保持兼容性。 +

    + + +

    定制配色工具

    + +

    如果要定制主题的基色以符合您的品牌,您可在进行材料主题继承时使用主题属性定义您的定制颜色: +

    + +
    +<resources>
    +  <!-- inherit from the material theme -->
    +  <style name="AppTheme" parent="android:Theme.Material">
    +    <!-- Main theme colors -->
    +    <!--   your app branding color for the app bar -->
    +    <item name="android:colorPrimary">@color/primary</item>
    +    <!--   darker variant for the status bar and contextual app bars -->
    +    <item name="android:colorPrimaryDark">@color/primary_dark</item>
    +    <!--   theme UI controls like checkboxes and text fields -->
    +    <item name="android:colorAccent">@color/accent</item>
    +  </style>
    +</resources>
    +
    + +
    + +

    +图 3.定制材料主题。

    +
    + + +

    定制状态栏

    + +

    材料主题可让您轻松定制状态栏,因此您可以指定一个符合自己品牌特色且对比度足够高、能够显示白色状态图标的颜色。 +如果要为状态栏设置定制颜色,您可在扩展材料主题时使用 android:statusBarColor 属性。 + +默认情况下,android:statusBarColor 将继承 android:colorPrimaryDark 的值。 +

    + +

    您也可自行将状态栏移到后侧。例如,您想在一个照片上以透明的方式显示状态栏,同时利用细微的深色渐变以确保白色状态图标仍保持可见。 + +如果要执行此操作,请将 android:statusBarColor 属性设置为 +@android:color/transparent 并根据需要调整窗口标志。您也可以使用 {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} 方法进行动画或淡出设置。 + +

    + +

    +注意:在多数情况下,状态栏与主工具栏之间应该有一个清楚的分割,您在这些栏的后侧显示全屏的丰富图像或媒体内容以及使用颜色渐变以确保图表仍保持可见的情况除外。 + + +

    + +

    定制导航栏和状态栏时,您可选择将导航栏和状态栏变透明或仅修改状态栏。 +在所有其他情况中,导航栏均应保持黑色。

    + + +

    主题个别视图

    + +

    XML 布局定义内的元素可指定 android:theme 属性,而该属性将引用一个主题资源。 +此属性可修改元素以及任何子元素的主题,有助于改变界面中特定部分的主题配色工具。 + +

    diff --git a/docs/html-intl/intl/zh-cn/training/wearables/apps/creating-app-china.jd b/docs/html-intl/intl/zh-cn/training/wearables/apps/creating-app-china.jd new file mode 100644 index 0000000000000000000000000000000000000000..4e33d36cb849846d6e3b578e7a81f8c6413e3c0e --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/wearables/apps/creating-app-china.jd @@ -0,0 +1,156 @@ +page.title=创建面向中国市场的 Android Wear 应用 +parent.title=培训 +parent.link=creating.html +page.tags= "可穿戴式", "应用", "中国" +page.article=true + +@jd:body + +
    +
    +

    本课程将向您展示如何

    +
      +
    1. 在面向中国市场的 Android Wear 上支持您的应用
    2. +
    3. 使用其他 Google Play Services API
    4. +
    +

    依赖项和先决条件

    +
      +
    1. 在手持式设备和可穿戴式设备上安装 Android 4.3(API 级别 18)或更高版本。
    2. +
    +

    下载

    +
      +
      +独立客户端库 +

      google-play-services-7-8-87.zip

      +
      +
    +
    +
    + + +

    +在中国销售的手持式设备没有预装 Google Play 服务。为此,在面向中国市场的设备上运行的可穿戴式设备应用必须通过 Android Wear 协同应用与配对的手持式设备进行通信。 + +为了让您能够开发出可与面向中国市场的 Android Wear 和面向全球其他市场的 Android Wear 配合使用的 APK,我们提供了专用版本的 Google Play services 客户端库。 + +

    + +

    +此客户端库与 Android 4.3(API 级别 18)及更高版本兼容,您只需将其拖放到您的应用中。 +您无需编写任何新代码,只需更改几个项目配置设置,然后重新编译应用。 + +

    + + +

    本页面的其余部分将介绍如何执行此过程。

    + + + +

    在面向中国市场的 Android Wear 上支持您的应用

    + +

    +

    为了在所有手持式设备上支持您的可穿戴式设备应用,您必须下载 Google Play +services 7.8.87 客户端库,并将其作为 Maven 存储库添加到您的项目中,配置开发项目以使用该库,然后重新编译您的应用。 + +

    + +

    添加 Google Play services 7.8.87 库

    + +

    Google Play services 7.8.87 客户端库作为 Maven 存储库分发。要将此存储库添加到项目,请: +

    + +
      +
    1. 下载客户端库。 +文件名为 {@code google-play-services-7-8-87.zip}。 +
    2. +
    3. 通过从下载的 zip 文件提取 {@code google-play-services-7-8-87/} 目录来创建本地 Maven 存储库,并将其放入项目的根目录中。 + +
    4. +
    5. 在顶级项目 {@code build.gradle} 文件中,指定新创建的本地 Maven {@code google-play-services-7-8-87} 存储库的位置。 + +
    6. +

      +以下示例向您展示如何操作: +

      +
      +allprojects {
      +  repositories {
      +
      +        maven {
      +                url "${rootProject.projectDir}/google-play-services-7-8-87"
      +              }
      +       // ... other repositories may go here ...
      +
      +    }
      +
    +

    配置应用以使用库

    +

    mobile 模块的 {@code build.gradle} 文件中,将 Google Play services 依赖项替换为指向客户端库(来自新添加的存储库)的引用。 +以下示例向您展示如何操作: + +

    + + +
    +dependencies{
    +    ...
    +    wearApp project(':wear')
    +    compile 'com.google.android.gms:play-services-wearable:7.8.87'
    +    ...
    +    }
    +
    +

    wear 模块的 {@code build.gradle} 文件也必须使用此版本的客户端库,例如: + +

    +
    +dependencies {
    +    compile 'com.google.android.support:wearable:1.3.0'
    +    compile 'com.google.android.gms:play-services-wearable:7.8.87'
    +}
    +
    + +

    :如果您在可穿戴式设备应用中使用任何其他 Google Play services API,则必须有选择地将这些 Google Play services API 添加到应用中,并显式指定 7.8.87 版本。 + +例如,若要将 Google 位置 API 包含到可穿戴式设备应用中,则在您的 {@code build.gradle} 文件中添加以下行: + +

    +
    +compile 'com.google.android.gms:play-services-location:7.8.87'
    +
    +

    +

    构建项目

    + + +

    现在,您可以构建应用的新版本,并将其全局部署到 Android 手持式设备。 +

    + + + +

    使用其他 Google Play services API

    + +

    +如果您的应用使用 Google Play services API 而不是 Wearable API,那么您的应用需要检查这些 API 在运行时是否可用以及是否能够做出恰当的响应。 +检查 Google Play services API 可用性的方式有两种: + +

    + +
      +
    1. 使用独立的 {@code GoogleApiClient} 实例来连接到其他 API。此界面包含回调以提醒您的应用连接成功还是失败。若要了解如何处理连接故障,请参阅访问 Google API。 + + +
    2. + +
    3. 使用 +{@code GoogleApiClient.Builder} + +的 {@code addApiIfAvailable()} 方法以连接到必需的 API。在触发 +{@code onConnected()} 回调后,使用 + {@code hasConnectedApi()} 方法检查所请求的每个 API 是否均已正确连接。 + + +
    diff --git a/docs/html-intl/intl/zh-cn/training/wearables/apps/creating.jd b/docs/html-intl/intl/zh-cn/training/wearables/apps/creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..5c9ec3fbb3f8b12dba850b80db127cb65552f77b --- /dev/null +++ b/docs/html-intl/intl/zh-cn/training/wearables/apps/creating.jd @@ -0,0 +1,225 @@ +page.title=创建并运行可穿戴式设备应用 +page.tags=wear +helpoutsWidget=true + +@jd:body + + + +

    可穿戴式设备应用直接在可穿戴式设备上运行,让您可以直接访问可穿戴式设备上的底层硬件(如传感器)、Activity、服务等。 + +

    + +

    如果您想要发布到 Google Play 商店,还需要提供包含可穿戴式设备应用的协同手持式设备应用。可穿戴式设备不支持 Google Play 商店,因此,用户可下载协同手持式设备应用,后者自动将可穿戴式设备应用推送到可穿戴式设备上。 + + +手持式设备应用还可用于执行繁重的处理、网络操作或其他工作,以及将处理结果发送到可穿戴式设备。 + + +

    + +

    此课程介绍如何设置设备或模拟器,并创建一个同时包含可穿戴式设备应用和手持式设备应用的项目。 + +

    + +

    更新 SDK

    + +

    在您着手构建可穿戴式设备应用前,必须:

    + +
      +
    • 将您的 SDK 工具更新到 23.0.0 或更高版本 +
      + 利用更新后的 SDK 工具,您可以构建和测试可穿戴式设备应用。 +
    • +
    • 将您的 SDK 平台更新为 Android 4.4W.2 (API 20) 或更高版本 +
      + 更新后的平台版本提供了新的可穿戴式设备应用 API。 +
    • +
    + +

    若要将 SDK 更新为上述组件,请参阅获取最新的 SDK 工具 +。

    + + +

    设置 Android Wear 模拟器或设备

    +

    我们建议您在真实的硬件上进行开发,以便您可以更好地衡量用户体验。 +不过,通过模拟器,您可以测试不同类型的屏幕形状,这对于测试非常有用。 +

    + +

    设置 Android Wear 虚拟设备

    + +

    若要设置 Android Wear 虚拟设备,请:

    +
      +
    1. 点击 Tools > Android > AVD Manager
    2. +
    3. 点击 Create Virtual Device...
    4. +
        +
      1. 在“Category”列表中点击 Wear
      2. +
      3. 选择 Android Wear Square 或 Android Wear Round。
      4. +
      5. 点击 Next
      6. +
      7. 选择版本名称(例如,KitKat Wear)。
      8. +
      9. 点击 Next
      10. +
      11. (可选)更改虚拟设备的任意首选项。
      12. +
      13. 点击 Finish
      14. +
      +
    5. 启动模拟器: +
        +
      1. 选择您刚创建的虚拟设备。
      2. +
      3. 点击 Play 按钮。
      4. +
      5. 等待模拟器初始化并显示 Android Wear 主屏幕。
      6. +
      +
    6. +
    7. 将手持式设备与模拟器配对: +
        +
      1. 在手持式设备上,从 Google Play 安装 Android Wear 应用。
      2. +
      3. 通过 USB 将手持式设备连接到计算机。
      4. +
      5. 将 AVD 的通信端口转发到连接的手持式设备(每次连接手持式设备时必须进行此操作): + +
        adb -d forward tcp:5601 tcp:5601
        +
      6. +
      7. 在手持式设备上启动 Android Wear 应用并连接到模拟器。
      8. +
      9. 点击 Android Wear 应用右上角的菜单并选择 +Demo Cards
      10. +
      11. 您选择的卡片作为通知显示在模拟器主屏幕上。
      12. +
      +
    8. +
    + +

    设置 Android Wear 设备

    +

    若要设置 Android Wear 设备,请:

    +
      +
    1. 在手持式设备上安装 Android Wear 应用,可通过 Google Play 获取。
    2. +
    3. 按照应用的说明将手持式设备与可穿戴式设备配对。 + 这样,您可以测试同步的手持式设备通知(如果您正在构建这些通知)。
    4. +
    5. 在您的手机上使 Android Wear 应用保持打开状态。
    6. +
    7. 在 Android Wear 设备上启用 adb 调试。
    8. +
        +
      1. 请转到 Settings > About
      2. +
      3. 连续点击 Build number 七次。
      4. +
      5. 向右滑动返回“Settings”菜单。
      6. +
      7. 转到屏幕底部的 Developer options。 +
      8. +
      9. 点击 ADB Debugging 以启用 adb。
      10. +
      +
    9. 通过 USB 将可穿戴式设备连接到您的计算机,以便您可以在开发时将应用直接安装在可穿戴式设备上。 +可穿戴式设备应用和 Android Wear 应用上会显示一条消息,提示您允许调试。 +
    10. + +

      :如果您无法通过 USB 将可穿戴式设备连接到计算机,您可以尝试通过蓝牙连接。 + + +

      + +
    11. 在 Android Wear 应用上,选择 Always allow from this computer 并点击 +OK
    12. +
    + +

    Android Studio 上的 Android 工具窗口显示来自可穿戴式设备的系统日志。 +运行 adb devices 命令也可列出可穿戴式设备。

    + +

    创建项目

    + +

    若要着手开发,请先创建包含可穿戴式设备和手持式设备应用模块的应用项目。 +在 Android Studio 中,点击 File > + New Project,并按照项目向导说明操作,如创建项目中所述。 + +按照向导进行操作时,请输入以下信息:

    + +
      +
    1. Configure your Project 窗口中,输入应用名称和软件包名称。 +
    2. +
    3. Form Factors 窗口中: +
        +
      • 选择 Phone and Tablet,然后在 Minimum SDK 下选择 API 9: Android 2.3 (Gingerbread)。 +
      • +
      • 选择 Wear,然后在 Minimum SDK 下选择 API 20: Android 4.4 (KitKat Wear)。 +
      • +
      +
    4. +
    5. 在第一个 Add an Activity 窗口中,针对移动设备添加空白 Activity。
    6. +
    7. 在第二个 Add an Activity 窗口中,针对穿戴设备添加空白 Activity。
    8. +
    +

    向导完成时,Android Studio 将创建一个包含两个模块(mobile +和 wear)的新项目。现在,您有一个可供手持式设备应用和可穿戴式设备应用使用的项目,您可以针对该项目创建 Activity、服务和自定义布局。 +手持式设备应用处理大多数繁重事务,如网络通信、密集型处理或需要大量用户交互的任务。 + +当应用完成这些操作时,您的应用可通过通知或同步数据并将其发送到可穿戴式设备,将处理结果通知给可穿戴式设备。 + +

    + +

    wear 模块还包含一个使用 +WatchViewStub的 "Hello World" Activity。 + + 该类可根据设备屏幕是圆形还是方形扩充布局。 + WatchViewStub + 类是可穿戴式设备支持库提供的 UI 小工具之一。 + + +

    + + +

    安装可穿戴式设备应用

    + +

    开发时,与手持式设备应用相似,直接将应用安装到可穿戴式设备。使用 Android Studio 上的 adb installPlay 按钮。 +

    + +

    准备向用户发布应用时,将可穿戴式设备应用嵌入手持式设备应用的内部。 +用户从 Google Play 安装手持式设备应用时,已连接的可穿戴式设备会自动接收可穿戴式设备应用。 +

    + +

    :自动安装可穿戴式设备应用的功能仅支持发布密钥,如果您使用调试密钥签署应用,将无法自动安装。 +有关如何正确打包可穿戴式设备应用的完整信息,请参阅打包可穿戴式设备应用 + +。

    + +
  • +要将“Hello World”应用安装到可穿戴式设备,请从 Run/Debug +configuration 下拉菜单中选择 wear,然后点击 Play 按钮。此时,该 Activity 将出现在可穿戴式设备上并显示“Hello world!” + +
  • +

    包含正确的库

    + +

    项目向导将在相应模块的 build.gradle 文件中为您导入正确的依赖项。不过,这些依赖项并不是必需的,因此,请阅读以下说明以了解您是否需要它们: + + +

    + +通知 +

    Android +v4 支持库(或 v13,其包含 v4)包含的 API 可扩展手持式设备上的现有通知,使之支持可穿戴式设备。 +

    + +

    对于仅显示在可穿戴式设备上的通知(意味着这些通知由可穿戴式设备上运行的应用发出),您只需在可穿戴式设备上使用标准框架 API(API 级别 20),并在项目的 mobile 模块中移除支持库依赖项。 + + + +

    + +Wearable Data Layer +

    若要通过 Wearable Data Layer API 在可穿戴式设备和手持式设备之间同步和发送数据,您需要最新版本的 +Google Play 服务。如果您不打算使用这些 API,则从这两个模块中移除依赖项。 + +

    + +Wearable UI 支持库 +

    这是非官方库,其包含专为可穿戴式设备设计的 UI 小工具。 + +我们建议您在应用中使用这些库,因为它们可作为最佳实践的范例,但仍可随时进行更改。 +不过,更新库时,您的应用不会中断,因为这些库已编译到您的应用中。 +要从更新的库中获取新功能,您只需静态链接新版本,然后相应更新您的应用。 +此库仅适用于创建可穿戴式设备应用。 + +

    + +

    在后面的课程中,您将学习如何创建专为可穿戴式设备设计的布局,以及如何使用平台支持的各种语音操作。 +

    diff --git a/docs/html-intl/intl/zh-tw/design/get-started/principles.jd b/docs/html-intl/intl/zh-tw/design/get-started/principles.jd index 27cce81c7d2eed64ba66582cb82e7838957c9596..c5d40d4bdf86df084adf140574b926dea1e40872 100644 --- a/docs/html-intl/intl/zh-tw/design/get-started/principles.jd +++ b/docs/html-intl/intl/zh-tw/design/get-started/principles.jd @@ -9,13 +9,13 @@ page.title=Android 設計原則

    當您套用您本身的創意與設計思維時,請考慮這些原則。 -偏離一般做法要帶有目的。 +除非有特定目的,否則請遵循這些原則。

    使人著迷

    -
    -
    +
    +

    以出乎意外的方式取悅人

    漂亮的外觀、精心設置的動畫,或時機恰到好處的音效,都是令人感到喜悅的體驗。 @@ -23,7 +23,7 @@ page.title=Android 設計原則

    -
    +
    @@ -32,15 +32,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +
    -

    真實的物件比按鈕和功能表更有趣

    -

    讓人們可以在您應用程式中直接輕觸和操縱物件,這可減少執行某項工作所需的認知過程,同時在情緒上更令人感到滿足。 +

    真實的物件比生硬的按鈕和選單,更能夠引人入勝。

    +

    您所設計的應用程式應該要能讓使用者可直接輕觸並操縱物件。這將可減少執行工作所需的認知準備,同時也能讓使用者在情緒上更為滿足。

    -
    +
    @@ -49,16 +49,16 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    提供個人設定

    -

    人們喜歡加上個人風格,因為這有助於他們感到自在並握有主控權。提供能令人感受及美觀的預設設定,但也可以考慮使用不會阻礙主要工作卻又好玩的可選用性自訂項目。 +

    很多人喜歡加上個人風格,因為這有助於他們感到自在並握有主控權。提供實用且美觀的預設設定,但也可以考慮使用不會阻礙主要工作而且有趣的選用自訂項目。

    -
    +
    @@ -67,15 +67,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    設法了解使用者

    -

    隨著時間而學習使用者的偏好。讓人們易於取得之前的選擇,而不是一再詢問他們會做出相同選擇的問題。 +

    隨著時間而學習使用者的偏好。讓使用者容易取得之前的選擇,而不是一再請他們做出相同的選擇。

    -
    +
    @@ -84,14 +84,14 @@ page.title=Android 設計原則

    簡化生活

    -
    -
    +
    +

    保持簡潔

    -

    以簡單單字組成簡短語句。人們傾向於略過冗長的句子。

    +

    以簡單單字組成簡短語句。多數人傾向於略過冗長的句子。

    -
    +
    @@ -100,15 +100,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    圖片的傳達效果更勝於言語

    -

    請考慮使用圖片來解釋想法。圖片能吸引人們的注意力,並比言語更有效率。 +

    請考慮使用圖片來解釋想法。圖片能吸引目光,並比言語更有效率。

    -
    +
    @@ -117,15 +117,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    幫使用者決定,但使用者擁有最終決定權

    -

    做出最好的猜測,先行動,而非先詢問。太多的選擇和決定會讓人們不高興。 +

    做出最好的猜測,先行動,而非先詢問。太多的選擇和決定會讓使用者覺得很痛苦。 為防止錯誤,務必允許復原。

    -
    +
    @@ -134,15 +134,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +
    -

    必要時僅顯示使用者需要的東西

    -

    人們無法承受一次看到太多東西。將工作和資訊細分成小型、易消化的區塊。 -隱藏當下不重要的選項,但在人們需要選擇時要明確指導。

    +

    必要時僅顯示使用者需要的內容

    +

    多數人無法承受一次看到太多東西。將工作和資訊細分成小型、易消化的區塊。 +隱藏當下不重要的選項,但在需要選擇時要明確指導。

    -
    +
    @@ -151,15 +151,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +
    -

    使用者應該總是清楚所在位置

    -

    給人們信心,知道自己沒有迷路。讓您應用程式中的各處看起來都有獨特性,並使用轉換來顯示畫面之間的關係。 +

    使用者應該永遠清楚所在位置

    +

    給使用者信心,知道自己沒有迷路。讓您應用程式中的各個地方看起來都有獨特性,並使用轉換來顯示畫面之間的關係。 針對進行中的工作提供回饋。

    -
    +
    @@ -168,8 +168,8 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    別弄丟使用者的東西

    儲存使用者花時間所建立的資訊,並且讓使用者可從任何地方存取。跨手機、平板電腦和電腦記住設定、個人風格和建立的資訊。 @@ -177,7 +177,7 @@ page.title=Android 設計原則

    -
    +
    @@ -186,15 +186,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    如果看起來一樣,就應該有相同的動作

    -

    讓功能看起來就不一樣,而非變化微妙,這可以協助人們辨別功能差異。在相同輸入環境下,因為模式看起來很類似但卻有不同的動作,請盡量避免使用。 +

    讓功能看起來就不一樣,而非變化微妙,這可以協助使用者辨別功能差異。在相同輸入環境下,請盡量避免使用看起來很類似但作用卻不同的模式。

    -
    +
    @@ -203,15 +203,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    重要時才打斷

    -

    就像一位好的私人助理,讓人們免於無關緊要的枝微末節。人們總想集中注意力,除非很重要且具時效性,不然貿然中斷會令人感到費力且沮喪。 +

    就像一位好的私人助理,應該讓使用者免於無關緊要的枝微末節。使用者總想集中注意力,除非很重要且具時效性,否則貿然中斷會令人感到費力且沮喪。

    -
    +
    @@ -220,16 +220,16 @@ page.title=Android 設計原則

    讓使用者驚艷

    -
    -
    +
    +

    提供使用者各處通用的訣竅

    -

    當人們能自行弄清楚來龍去脈時是很棒的體驗。運用來自其他 Android 應用程式的視覺模式和肌肉記憶效應,讓使用者更易於學會您的應用程式。 +

    當使用者能自行弄清楚來龍去脈時是很棒的體驗。運用來自其他 Android 應用程式的視覺模式和肌肉記憶效應,讓使用者更易於學會您的應用程式。 例如,擺動手勢可能是很好的導覽捷徑。

    -
    +
    @@ -238,16 +238,16 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    不是使用者的錯

    -

    提示人們更正時要溫和。人們使用您的應用程式時,會想要感受到自已非常明智。如果有什麼不對,請提供明確的復原指示,但不需要使用者明瞭技術細節。如果可以,請盡量在幕後修正。 - +

    提示使用者進行更正時要溫和。使用者使用您的應用程式時,不會自覺是笨蛋。 +若有地方出錯,請提供明確的復原指示,但不需要使用者明瞭技術細節。如果可以,請盡量在幕後修正。

    -
    +
    @@ -256,15 +256,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    分段鼓勵

    -

    將複雜工作細分成更小的步驟,讓使用者可以輕鬆完成。對動作給予回饋,即使只是個微光效果。 +

    將複雜工作細分成更小的步驟,讓使用者可以輕鬆完成。對動作給予回饋,即使只是細微的動作。

    -
    +
    @@ -273,16 +273,16 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    為使用者處理繁重的工作

    -

    讓新手也能做出以前從未想像過可以辦到的事情,讓使用者有專家的感覺。例如,組合多個相片效果的捷徑,只要幾個步驟,即可讓業餘照片令人驚艷。 +

    讓新手也能做到以前從未想像過可以完成的事情,讓使用者覺得自己是專家。例如,組合多個相片效果的捷徑,只要幾個步驟,即可讓業餘照片令人驚艷。

    -
    +
    @@ -291,15 +291,15 @@ page.title=Android 設計原則
     
    -
    -
    +
    +

    快速找到重要的功能

    -

    並非所有的動作都一視同仁。決定應用程式中最重要的部分,並讓使用者易於找到並可迅速使用,例如相機的快門按鈕或音樂播放器的暫停按鈕。 +

    並非所有的動作都平等。決定應用程式中最重要的部分,並讓使用者易於找到並可迅速使用,例如相機的快門按鈕或音樂播放器的暫停按鈕。

    -
    +
    diff --git a/docs/html-intl/intl/zh-tw/design/material/index.jd b/docs/html-intl/intl/zh-tw/design/material/index.jd index 620ee6ec20623ac880bfaa6e3383ed2823d4e84e..464dc2a1b216de29864668e20d3541b7e0d6e52a 100644 --- a/docs/html-intl/intl/zh-tw/design/material/index.jd +++ b/docs/html-intl/intl/zh-tw/design/material/index.jd @@ -1,7 +1,6 @@ -page.title=材料設計 -page.tags=Material, design -page.type=設計 -page.image=design/material/images/MaterialLight.png +page.title=Android 材料設計 +page.tags=Material,design, 設計 +page.image=images/cards/design-material-for-android_2x.jpg @jd:body @@ -41,25 +40,25 @@ page.image=design/material/images/MaterialLight.png

    材料設計是一份內容廣泛的綜合性指南,引導您跨平台、跨裝置進行視覺、動態和互動的設計。 Android 現已納入對材料設計應用程式的支援。 -如果要在 Android 應用程式中使用材料設計,請依照材料設計規格中定義的指示,並使用 -Android -5.0 (API 層級 21) 或後續版本中的新元件和新功能。

    +如果要在 Android 應用程式中使用材料設計,請依照材料設計規格中定義的指示,並使用 Android 5.0 (API 級別 21) 或後續版本中的新元件和新功能。 + +

    Android 提供下列元素,供您打造材料設計應用程式:

      -
    • 一個新的設計風格
    • -
    • 用於複雜檢視的小工具
    • +
    • 新的設計風格
    • +
    • 用於複雜視圖的小工具
    • 自訂陰影和動畫的新 API
    -

    如需取得如何在 Android 上實作材料設計的詳細資訊,請詳見使用材料設計建立應用程式。 +

    如需取得如何在 Android 上實作材料設計的詳細資訊,請參閱使用材料設計建立應用程式

    材料設計風格

    -

    材料設計風格提供您應用程式使用的新樣式、可以讓您設定色板的系統小工具,並針對輕觸回饋與操作行為轉換提供預設動畫。 +

    材料設計風格提供您應用程式使用的新樣式、可以讓您設定色板的系統小工具,並針對輕觸回饋與操作 Activity 轉換提供預設動畫。

    @@ -92,8 +91,8 @@ Android
    -

    新的 RecyclerView 小工具是 ListView -更容易插入的版本,支援不同的版面配置類型,並提供效能改善。

    +

    新的 RecyclerView 小工具是 ListView 更容易插入的版本,支援不同的版面配置類型,並提供效能改善。 +

    @@ -109,12 +108,12 @@ Android

    檢視陰影

    -

    除了 X 和 Y 屬性外,Android 中的檢視現在也有 Z 屬性。 -這個新屬性代表檢視的高度,這會決定:

    +

    除了 X 和 Y 屬性外,Android 中的視圖現在也有 Z 屬性。 +這個新屬性代表視圖的高度,這會決定:

      -
    • 陰影大小:帶有較高 Z 值的檢視會投射更大的陰影。
    • -
    • 繪製順序:具有較高 Z 值的檢視會顯示在其他檢視之上。
    • +
    • 陰影大小:Z 值較高的視圖會投射更大的陰影。
    • +
    • 繪製順序:Z 值較高的視圖會顯示在其他視圖之上。
    @@ -130,39 +129,38 @@ Android
    -

    如需取得詳細資訊,請參閱定義陰影和裁剪檢視。 +

    如需詳細資訊,請參閱定義陰影和裁剪視圖

    動畫

    - -

    新的動畫 API 可針對 UI 控制項的輕觸回饋、檢視狀態中的變更,以及行為轉換,讓您建立自訂動畫。 +

    新的動畫 API 可針對 UI 控制項的輕觸回饋、視圖狀態中的變更,以及操作行為轉換,讓您建立自訂動畫。

    這些 API 讓您可以:

    • -回應您檢視中有輕觸回饋動畫的輕觸事件。 +回應您視圖中有輕觸回饋動畫的輕觸事件。
    • -隱藏和顯示有循環顯示動畫的檢視。 +隱藏和顯示有循環顯示動畫的視圖。
    • -在有自訂行為轉換動畫的行為間切換。 +在包含自訂操作行為轉換動畫的行為間切換。
    • 使用曲線動作建立更自然的動畫。
    • -在帶有檢視狀態變更動畫的一個或多個檢視屬性中變更動畫。 +在包含視圖狀態變更動畫的一個或多個視圖屬性中變更動畫。
    • -在檢視狀態變更間,顯示狀態清單可繪項目中的動畫。 +在視圖狀態變更之間,顯示狀態清單可繪項目中的動畫。
    -

    輕觸回饋動畫會內建於數個標準檢視中,例如按鈕等。新 API 可讓您自訂這些動畫,並將其新增至您的自訂檢視中。 +

    輕觸回饋動畫會內建於數個標準視圖中,例如按鈕等。新 API 可讓您自訂這些動畫,並將其新增至您的自訂視圖中。

    如需詳細資訊,請參閱定義自訂動畫。 @@ -176,7 +174,7 @@ Android

    • 矢量可繪項目可以調整大小,但又不會喪失定義,最適合於應用程式中的單色圖示。
    • -
    • 可繪項目著色可讓您在執行階段將點陣圖定義為 Alpha 遮罩,並以一個顏色進行著色。 +
    • 繪製著色可讓您在執行階段將點陣圖定義為 Alpha 遮罩,並以一個顏色進行著色。
    • 顏色提取可讓您自動從點陣圖影像中提取顯著顏色。
    • diff --git a/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd b/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..b65b90be849c0194dee2760a59a30a68b7117d36 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/design/patterns/compatibility.jd @@ -0,0 +1,70 @@ +page.title=向下相容性 +page.tags="support" +page.metaDescription=有關 Android 4.x 改寫針對舊硬體和 OS 版本設計的 UI 之備註。 +@jd:body + + +
      +

      開發人員文件

      +

      支援不同裝置

      +
      +
      + +

      Android 3.0 主要更新包含:

      +
        +
      • 淘汰導覽硬體鍵 (返回、選單、搜尋、首頁),以虛擬控制按鍵取代實體導覽按鍵 (返回、首頁、最近使用記錄)。 +
      • +
      • 健全的動作列選單使用模式。
      • +
      +

      Android 4.0 版本將平板電腦的變更套用於手機平台。

      + +

      針對舊版本的硬體及應用程式,改寫 Android 4.0

      + +
      +
      + +

      使用虛擬導覽控制鍵的手機

      +

      專為 Android 3.0 及之後版本設計的應用程式會在動作列中顯示動作。擠不進動作列或重要性不足的動作,會顯示在動作溢位中。 + +

      +

      使用者可輕觸動作列中的動作溢位以存取它。

      + +
      +
      + + + +
      +
      + +
      +
      + +

      使用實體導覽鍵的手機

      +

      使用實體導覽鍵的 Android 手機不會在螢幕底部顯示虛擬導覽列。 +如要存取動作溢位,請使用選單硬體按鍵。所產生的動作彈出式選單的風格與先前範例相同,但會顯示在螢幕頂部。 +

      + +
      +
      + + + +
      +
      + +
      +
      + +

      在使用虛擬導覽控制鍵的手機上執行舊版應用程式

      +

      當您在具備虛擬導覽控制鍵的手機上,執行為 Android 2.3 或之前版本所設計的應用程式時,動作溢位控制會顯示在虛擬導覽列的右側。 +您可以輕觸控制鍵,以顯示傳統 Android 選單風格的應用程式動作。 +

      + +
      +
      + + + +
      +
      diff --git a/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd b/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd index ac8975f8092fdd23231e59fa572bf517c08fc76f..a92a017b32ff11604a70edfc26eaf41c8ab0057c 100644 --- a/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd +++ b/docs/html-intl/intl/zh-tw/design/patterns/confirming-acknowledging.jd @@ -2,14 +2,14 @@ page.title=確認及確認完成 page.tags=dialog,toast,notification @jd:body -

      在某些情況下,當使用者在您應用程式中呼叫一個動作時,最好是透過文字來「確認」(confirm) 或「確認完成」(acknowledge)。

      +

      在某些情況下,當使用者在您應用程式中呼叫一個動作時,最好是透過文字來「確認」或「確認完成」

      -
      -
      +
      +

      確認是要求使用者確認真的要進行剛剛呼叫的動作。在某些情況下,確認訊息出現時會伴隨警告或需要使用者考量是否採取動作的相關重要資訊。

      -
      +

      確認完成是顯示文字,讓使用者知道已經完成剛剛呼叫的動作。這會排除系統正在採取之隱式作業的不確定性。在某些情況下,確認完成出現時會伴隨復原動作的選項。

      @@ -22,14 +22,14 @@ page.tags=dialog,toast,notification

      確認

      -
      -
      +
      +

      範例:Google Play 書籍

      在此範例中,使用者已要求從其 Google Play 媒體庫中刪除一本書籍。顯示警示來確認此動作,因為使用者必須了解將不再針對任何裝置提供這本書籍。

      設計一個確認的對話方塊時,要讓標題具有意義就必須回應要求的動作。

      -
      +

      範例:Android Beam

      確認不一定要以具有兩個按鈕的警示來呈現。在啟動 Android Beam 之後,會提示使用者輕觸要共用的內容 (在此範例中是一張照片)。如果他們決定不進行,只要移開他們的電話即可。

      @@ -37,15 +37,15 @@ page.tags=dialog,toast,notification

      確認完成

      -
      -
      +
      +

      範例:已儲存放棄的 Gmail 草稿

      -

      在此範例中,如果使用者從 Gmail 撰寫畫面返回,可能會發生預期外的狀況:會自動儲存目前的草稿。以快顯通知 (toast) 形式出現的確認完成,會讓使用者明瞭此情況。確認完成會在幾秒鐘後淡出。

      +

      在此範例中,如果使用者從 Gmail 撰寫畫面返回,可能會發生預期外的狀況:會自動儲存目前的草稿。以快顯通知形式出現的確認完成,會讓使用者明瞭此情況。確認完成會在幾秒鐘後淡出。

      在此並不合適使用復原功能,因為儲存動作是由應用程式發起,而非使用者。瀏覽至草稿清單,就可以方便且快速地繼續撰寫。

      -
      +

      範例:已刪除 Gmail 會話群組

      使用者從 Gmail 清單中刪除一個會話群組後,會出現確認完成訊息,並提供一個復原選項。確認完成會持續出現,直到使用者採取不相關的動作,例如捲動清單。

      @@ -53,14 +53,14 @@ page.tags=dialog,toast,notification

      無「確認」或「確認完成」

      -
      -
      +
      +

      範例:+1 中

      確認並非必要。如果使用者不小心按了 + 1 按鈕,這並不是什麼大問題。他們可以再次輕觸按鈕,復原此動作。

      -

      確認完成並非必要。使用者將會看到 +1 彈起並變成紅色。這是個非常明確的訊號。

      +

      確認完成並非必要。使用者將會看到 +1 按鈕彈起並變成紅色。這是個非常明確的訊號。

      -
      +

      範例:從主螢幕移除應用程式

      確認並非必要。這是特意設計的動作:使用者必須拖曳項目放到相對較大且隔離的目標上。因此極不可能發生意外狀況。但如果使用者後悔所做的決定,只需幾秒鐘,就可以恢復原狀。

      diff --git a/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd b/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd index db160c2516d28cbddc9a333b10f95f2c7db66d97..a568cb1d6724c94c2356b77d007e8a7653ec51b8 100644 --- a/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd +++ b/docs/html-intl/intl/zh-tw/design/patterns/navigation.jd @@ -10,71 +10,71 @@ page.image=/design/media/navigation_between_siblings_gmail.png
      -

      一致的導覽是整體使用者體驗的必備組成。基本導覽的行為若不一致又令人意外,是最令使用者感到更沮喪的狀況。 +

      一致的導覽是整體使用者體驗的要件。以不一致且非預期方式運作的基本導覽行為最令使用者感到沮喪。 Android 3.0 已將重大變更導入全域的導覽行為中。 -完全遵循 [返回] 及 [上一層] 的方針,會讓使用者感到您的應用程式導覽既可靠又符合預期。 +仔細遵循以下指導方針原則以設定 [返回] 及 [上一層] 按鈕,讓您的應用程式更可靠且符合預期。 +

      +

      Android 2.3 及之前版本均使用 [返回] 按鈕在應用程式中進行導覽。 +在 Android 3.0 導入導覽列功能,使用者有了另一種導覽方式:[上一層] 按鈕,由應用程式圖示及左指插入號組成。

      -

      Android 2.3 和更早版本依賴系統 [返回] -按鈕,以支援應用程式內的導覽。在 Android 3.0 導入動作列之後,出現第二個導覽機制:[上一層] -按鈕,由應用程式圖示和左指符號組成。

      -

      [上一層] vs.[返回]

      +

      [返回] 及 [上一層]

      + +

      [上一層] 按鈕用於在應用程式中,根據畫面之間的階層關係進行導覽。 +例如,如果畫面 A 顯示項目清單,然後選擇其中一個項目導致進入畫面 B (更詳細呈現該項目),那麼畫面 B 應該提供 [上一層] 按鈕,以便返回畫面 A。 -

      [上一層] 按鈕用於在畫面間有階層關係的應用程式中導覽。 -例如,如果畫面 A -顯示項目清單,然後選擇其中一個項目導致進入畫面 B (更詳細呈現該項目),那麼畫面 B 應該提供 [上一層] 按鈕,以便返回畫面 A。

      -

      如果畫面是在應用程式中的最頂端 (亦即應用程式的首頁),則不應該會有 [上一層]按鈕。 +

      如果畫面是在應用程式中的最頂端 (亦即應用程式的首頁),則不應該會有 [上一層] 按鈕。

      系統 [返回] 按鈕用於逆時間順序導覽,透過歷程記錄,可以經歷使用者最近使用過的畫面。 [返回] 通常基於畫面之間的暫時關係,而非應用程式的階層。

      -

      當先前檢視的畫面也是目前畫面的階層父項時,按下 -[返回] 按鈕和按下 [上一層] 按鈕效果相同 — 這是常見的狀況。 -然而,與 [上一層] 按鈕不同的是 (該按鈕可以確保使用者仍停留在您的應用程式內):[返回] -按鈕可以讓使用者返回主畫面,或甚至是不同的應用程式。

      +

      當先前檢視的畫面也是目前畫面的階層父項時,按下 [返回] 按鈕和按下 [上一層] 按鈕效果相同 — 這是常見的狀況。 + +然而,與 [上一層] 按鈕不同的是 (該按鈕可以確保使用者仍停留在您的應用程式內):[返回] 按鈕可以讓使用者返回主螢幕,或甚至是不同的應用程式。 +

      [返回] 按鈕還支援幾個間接關聯畫面對畫面導覽的行為:

        -
      • 關閉浮動視窗 (對話、快顯)
      • +
      • 關閉浮動視窗 (對話框、快顯)
      • 關閉內容相關的動作列,並從選取項目移除醒目顯示
      • 隱藏畫面鍵盤 (IME)

      在應用程式內導覽

      -

      導覽至具有多重入口的畫面

      -

      有時候,一個畫面在應用程式的階層中並沒有嚴謹的位置,而且可以從多個入口存取 — 例如可以從您應用程式中任何其他畫面存取的設定畫面。在這種情況下,[上一層] -按鈕應該選擇返回導引至此畫面的前一畫面,這個行為與 [返回] 相同。 +

      導覽至具有多個進入點的畫面

      +

      有時畫面在應用程式中的階層並不明確,可從多個入口點進入畫面 — 例如,可從應用程式中的任何其他畫面進入的設定畫面。 +在這種情況下,應按下 [上一層] 按鈕回到之前的參照畫面,操作方式與 [返回] 按鈕相同。

      -

      在畫面內變更檢視

      -

      變更畫面的檢視選項並不會變更 [上一層] 或 [返回] 的行為:畫面仍維持在應用程式階層中的相同位置,並不會建立新的導覽歷程記錄。 +

      在畫面內變更視圖

      +

      變更畫面的視圖選項並不會變更 [上一層] 或 [返回] 的行為:畫面仍維持在應用程式階層中的相同位置,並不會建立新的導覽歷程記錄。

      -

      這類檢視變更的範例如下:

      +

      這類視圖變更的範例如下:

        -
      • 使用標籤和/或左與右滑動來切換檢視
      • -
      • 使用下拉清單 (亦即折疊標籤) 切換檢視
      • +
      • 使用標籤和/或左與右滑動來切換視圖
      • +
      • 使用下拉清單 (亦即折疊標籤) 切換視圖
      • 篩選清單
      • 對清單排序
      • 變更顯示特性 (如縮放)

      在同層級畫面間導覽

      -

      當您的應用程式支援從項目清單導覽至項目之一的詳細檢視時,使用者通常會想使用方向導覽功能,以便從該項目導覽至清單中的前一個或後一個項目。 +

      當您的應用程式支援從項目清單導覽至項目之一的詳細視圖時,使用者通常會想使用方向導覽功能,以便從該項目導覽至清單中的前一個或後一個項目。 例如在 Gmail 中,可以很容易從會話群組向左或右滑動,方便檢視相同「收件匣」中的較新或舊會話群組。 -就像在一個畫面中變更檢視時,這類導覽不會變更 [上一層] 或 [返回] 的行為。 +就像在一個畫面中變更視圖時,這類導覽不會變更 [上一層] 或 [返回] 的行為。

      -

      然而有一個明顯的例外是,在不被引用清單綁在一起的相關詳細資料檢視之間瀏覽時 — 例如在 Play 商店中於相同開發者的不同應用程式之間瀏覽時,或是在相同演出者的專輯間瀏覽時。 +

      然而有一個明顯的例外是,在未與引用清單繫結在一起的相關詳細資料視圖之間瀏覽時 — 例如在 Play 商店中於相同開發者的不同應用程式之間瀏覽時,或是在相同演出者的專輯間瀏覽時。 在這些情況下,瀏覽每個連結都會產生歷程記錄,這會造成 [返回] 按鈕會經歷每個先前檢視過的畫面。 [上一層] 應該會繼續略過這些相關的畫面,並導覽到最近檢視過的容器畫面。 @@ -82,16 +82,16 @@ Android 3.0 已將重大變更導入全域的導覽行為中。 -

      基於您對詳細資料檢視的瞭解,您有能力讓 [上一層] 行為甚至變得更聰明。 +

      基於您對詳細資料視圖的瞭解,您有能力讓 [上一層] 行為甚至變得更聰明。 再延伸說明之前提及的 Play 商店範例,想像使用者已從最近檢視的「書籍」導覽至「電影」改編的詳細資料。 -在這種情況下,[上一層] -可以返回到使用者之前沒有導覽過的上層容器 (電影)。

      +在這種情況下,[上一層] 可以返回到使用者之前沒有導覽過的上層容器 (電影)。 +

      -

      透過「主畫面小工具」和「通知」,導覽至您的應用程式

      +

      透過「主螢幕小工具」和「通知」,導覽至您的應用程式

      -

      您可以使用主畫面小工具或通知,協助您直接導覽至深入您應用程式階層中的畫面。 +

      您可以使用主螢幕小工具或通知,協助您直接導覽至深入您應用程式階層中的畫面。 例如,Gmail 的「收件匣」小工具和新郵件通知,都可以略過「收件匣」畫面,將使用者直接帶到會話群組檢視之中。

      @@ -100,7 +100,7 @@ Android 3.0 已將重大變更導入全域的導覽行為中。
      • 如果目的地畫面通常是透過您應用程式中的一個特定畫面到達,那麼 [上一層] 應該要導覽至該畫面。
      • -
      • 否則, [上一層] 應該導覽至您應用程式的最頂端 (「主」) 畫面。
      • +
      • 否則, [上一層] 應該導覽至您應用程式的最頂端 (「主」) 螢幕。

      就 [返回] 按鈕而言,您應讓導覽更符合預期,方法是在工作的返回堆疊中,插入前往應用程式最頂端畫面的完整向上導覽路徑。 @@ -108,30 +108,30 @@ Android 3.0 已將重大變更導入全域的導覽行為中。

      -

      舉例來說,Gmail 的主畫面小工具有一個按鈕,可以直接往下進入撰寫畫面。 -來自撰寫畫面的 [上一層] 或 [返回] 按鈕,會將使用者帶到「收件匣」中,而此處的 [返回] 按鈕則可繼續前往至「主畫面」。 +

      舉例來說,Gmail 的主螢幕小工具有一個按鈕,可以直接往下進入撰寫畫面。 +來自撰寫畫面的 [上一層] 或 [返回] 按鈕,會將使用者帶到「收件匣」中,而此處的 [返回] 按鈕則可繼續前往至「主螢幕」。

      間接通知

      -

      當您的應用程式需要同時呈現多個事件的資訊時,可以使用單一通知,引導使用者進入一個插頁畫面。 -此畫面會摘要這些事件,並提供路徑,讓使用者可以深入應用程式之中。這種風格的通知稱為「間接通知」。 - +

      若應用程式必須同時呈現多個事件,可利用單一通知,引導使用者進入插頁畫面。 +插頁畫面會概述所有事件,並提供讓使用者可以深入應用程式之中的路徑。 +這種通知方式稱為間接通知

      與標準 (直接) 通知不同的是,從間接通知的插頁畫面按下 [返回],會讓使用者返回至通知觸發的起點 — 無其他畫面會插入至返回堆疊之中。 -一旦使用者從插頁畫面繼續進入應用程式之後,[上一層] -與 [返回] 會如上所述,其行為就像針對標準通知一樣:在應用程式內導覽,而非返回插頁畫面。 +一旦使用者從插頁畫面繼續進入應用程式之後,[上一層] 與 [返回] 會如上所述,其行為就像針對標準通知一樣:在應用程式內導覽,而非返回插頁畫面。 +

      例如,假設 Gmail 中的使用者收到來自「行事曆」的間接通知。輕觸這個通知會打開插頁畫面,而此畫面會顯示數個不同事件的提醒。 從插頁畫面輕觸 [返回],會讓使用者返回至 Gmail。輕觸特定事件會將使用者帶離插頁畫面,並進入完整的「行事曆」應用程式,顯示事件的詳細資料。 -從事件詳細資料中,[上一層] 和 [返回] 會導覽至「行事曆」的最頂層檢視。

      +從事件詳細資料中,[上一層] 和 [返回] 會導覽至「行事曆」的最頂層視圖。

      @@ -139,75 +139,75 @@ Android 3.0 已將重大變更導入全域的導覽行為中。

      快顯通知會略過通知匣,直接出現在使用者面前。 這不常使用,應該要保留在需要適時回應,以及必須中斷使用者前後關聯動作的時候。 -例如,Talk -就使用這種風格,用來提示使用者有朋友邀請加入視訊聊天,而且此邀請會在幾秒之後自動過期。 +例如,Talk 就使用這種風格,用來提示使用者有朋友邀請加入視訊聊天,而且此邀請會在幾秒之後自動過期。 +

      就導覽行為而言,快顯通知緊接著間接通知插頁畫面的行為。 -[返回] 會關閉快顯通知。如果使用者從快顯導覽進入通知應用程式,[上一層] -和 [返回] 會遵循標準通知的規則,只在應用程式內導覽。 +[返回] 會關閉快顯通知。如果使用者從快顯導覽進入通知應用程式,[上一層] 和 [返回] 會遵循標準通知的規則,只在應用程式內導覽。 +

      在應用程式間導覽

      -

      Android 系統的基本優點之一是應用程式互相啟動的能力,讓使用者能夠直接從一個應用程式導覽至另一個應用程式。 -例如,需要擷取一張相片的應用程式可以啟動「相機」應用程式,而此應用程式會將相片傳回引用的應用程式。開發人員可以輕鬆利用其他應用程式的程式碼,而使用者在經常執行的動作中可以享受一致性的體驗,這對雙方都是一大好處。 - +

      Android 系統的主要優勢在於應用程式之間可彼此互連,使用者可從一個應用程式中直接導覽至另一個應用程式。 +舉例來說,需要拍攝相片的的應用程式,可開啟「相機」應用程式,然後再返回原始參照應用程式中。 +這項功能不僅使開發人員可輕鬆地利用其他應用程式的程式碼,還可讓使用者透過一致的作業方式享受常用功能。

      -

      要瞭解應用程式對應用程式的導覽,重要的是要瞭解以下討論的 Android 架構行為。 +

      欲了解應用程式對應用程式的導覽行為,必須先了解以下介紹的 Android 架構行為。

      -

      活動、工作和意圖

      +

      Activity、工作和意圖

      -

      在 Android 中,活動是一個應用程式元件,定義了資訊畫面,以及使用者可以執行的所有關聯動作。 -您的應用程式是個活動的集合,包括您可以建立及您能從其他應用程式重複使用的活動。 +

      在 Android 中,Activity 是一個應用程式元件,定義了資訊畫面,以及使用者可以執行的所有關聯動作。 +您的應用程式是個 Activity 的集合,包括您可以建立及您能從其他應用程式重複使用的 Activity。

      -

      工作是使用者遵循以達到目標的一系列活動。單一工作可以利用來自單一應用程式的活動,或是汲取數個不同應用程式的活動。 +

      工作是使用者遵循以達到目標的一系列 Activity。單一工作可以利用來自單一應用程式的 Activity,或是汲取數個不同應用程式的 Activity。

      -

      意向是指應用程式所發出想要另一套應用程式協助執行某動作訊號的機制。 -應用程式的活動可指示其可回應哪些意向。 -針對如「共用」等常見意向,使用者可能已安裝許多可以滿足此要求的應用程式。 +

      意圖是指應用程式所發出想要另一套應用程式協助執行某動作訊號的機制。 +應用程式的 Activity 可指示其可回應哪些意圖。 +針對如「共用」等常見意圖,使用者可能已安裝許多可以滿足此要求的應用程式。

      -

      範例:在應用程式間導覽,以支援共用

      - -

      要理解活動、工作和意向如何攜手合作,請思考應用程式如何讓使用者使用另一套應用程式來共用內容。例如,從「主」畫面啟動 Play 商店應用程式,開始新工作 A (見下圖)。 +

      例如:在多個應用程式中導覽以支援共用功能

      -在導覽經歷 Play 商店,並輕觸一本促銷的書籍以查看詳細資料後,使用者仍會停留在相同工作中,並透過新增行為延伸工作。 -觸發「共用」動作會提示使用者一個對話框,列出已註冊可用來處理「共用」意向的每個活動 (來自不同的應用程式)。 +

      要了解使用活動、工作和意圖如何相互作用,必須先考慮一個應用程式如何讓使用者利用另一個應用程式共用內容。 +例如,在首頁中開啟 Play 商店應用程式,並開始工作 A (如下所示)。 +使用者在 Play 商店應用程式中結束導覽後,輕觸促銷的書籍以查看細節,這時仍會停留在相同的工作中,同時新增活動以延伸功能。 +觸發「共用」動作會提示使用者一個對話框,列出已註冊可用來處理「共用」意圖的每個 Activity (來自不同的應用程式)。

      -

      當使用者選擇透過 Gmail 共用,Gmail -的撰寫活動會被新增為工作 A 的延續 — 而不會建立新工作。如果 Gmail 有本身的工作正在背景執行,這並不會影響該工作。 +

      當使用者選擇透過 Gmail 共用,Gmail 的撰寫 Activity 會被新增為工作 A 的延續 — 而不會建立新工作。 +如果 Gmail 有本身的工作正在背景執行,這並不會影響該工作。

      -

      從撰寫活動中,傳送訊息或輕觸 [返回] 按鈕,會讓使用者返回至書籍詳細資料的活動。 -繼續輕觸 [返回] 則會經由 Play 商店往回導覽,最終到達「主畫面」。 +

      從撰寫 Activity 中,傳送訊息或輕觸 [返回] 按鈕,會讓使用者返回至書籍詳細資料的 Activity。 +繼續輕觸 [返回] 則會經由 Play 商店往回導覽,最終到達「主螢幕」。

      -

      然而,透過輕觸撰寫行為的 [上一層],代表使用者指明停留在 Gmail 的意願。 -Gmail 的會話群組清單活動會隨即出現,並針對該活動建立一個新的工作 B。新工作的最後根源都是「主畫面」,所以從會話群組輕觸 [返回] 會返回至該處。 +

      然而,透過輕觸撰寫 Activity 的 [上一層],代表使用者指明停留在 Gmail 的意願。 +Gmail 的會話群組清單 Activity 會隨即出現,並針對該 Activity 建立一個新的工作 B。新工作的最後根源都是「主螢幕」,所以從會話群組輕觸 [返回] 會返回至該處。

      -

      工作 A 仍然存在背景之中,而使用者可能會在稍後返回 (例如,透過「最近」畫面)。 +

      工作 A 仍然存在背景之中,而使用者可能會在稍後返回 (例如,透過「最近使用記錄」畫面)。 如果 Gmail 在背景正在執行自己的工作,則其會被工作 B 取代 — 並捨棄之前的前後關係,而就使用者的新目標。

      -

      當您的應用程式註冊來處理具有深入應用程式階層活動的意向時,請參考透過主畫面視窗小工具和通知,導覽至您的應用程式,取得如何指定 [上一層] 導覽的指導方針。 +

      當您的應用程式註冊來處理具有深入應用程式階層 Activity 的意圖時,請參考透過主螢幕小工具和通知,導覽至您的應用程式,取得如何指定 [上一層] 導覽的指導方針。

      diff --git a/docs/html-intl/intl/zh-tw/guide/components/activities.jd b/docs/html-intl/intl/zh-tw/guide/components/activities.jd new file mode 100644 index 0000000000000000000000000000000000000000..ea0934964fedf6a24827003d9aea08246ccf86ab --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Activity +page.tags=Activity,意圖 +@jd:body + + + + + +

      {@link android.app.Activity} 是提供畫面的應用程式元件,使用者可以與此畫面互動以執行動作,例如撥號、拍照、傳送電子郵件或檢視地圖。 + +每個 Activity 都會有專屬視窗,用於繪製其使用者介面。視窗一般會佔滿螢幕,但也可能小於螢幕或在其他視窗上方浮動。 + +

      + +

      應用程式通常由多個 Activity 組成,這些 Activity 之間的繫結鬆散。 +一般來說,應用程式中的某個 Activity 會指定為「主要」Activity。使用者第一次啟動應用程式時,會將此 Activity 向使用者顯示。 +然後,每個 Activity 可以啟動另一個 Activity,以執行不同的動作。 +每次啟動新的 Activity 時,之前的 Activity 會停止,但系統將該 Activity 保留在堆疊中(即「返回堆疊」)。 + +新的 Activity 啟動時會推送至返回堆疊,然後取得使用者焦點。 +返回堆疊遵循基本的「後進先出」堆疊機制,因此,使用者完成目前的 Activity,按下 [返回] 按鈕時,目前的 Activity 會從堆疊被推出 (並終止),然後繼續之前的 Activity。 + +(返回堆疊在工作和返回堆疊文件中,有更詳細的說明)。 + +

      + +

      Activity 因啟動新的 Activity 而停止時,Activity 的生命週期回呼方法會通知此狀態的變更。由於 Activity 狀態的變更 — 可能是系統建立、停止、繼續或終止 Activity 時 — Activity 會收到數個回呼方法,而且每個回呼都提供您執行適合該次狀態變更的特定工作機會。 + + + + +例如,停止時,您的 Activity 應釋放任何大型物件,例如網路或資料庫連線。 +Activity 繼續時,您可以重新取得必要資源,並繼續之前中斷的動作。 +這些狀態轉換都是 Activity 生命週期的一部分。 +

      + +

      本文件的其他部分會討論建置和使用 Activity 的基本概念,包括完整討論 Activity 生命週期的運作方式,讓您可以正確地管理各種 Activity 狀態之間的轉換。 + +

      + + + +

      建立 Activity

      + +

      如要建立 Activity,您必須建立 {@link android.app.Activity} 的子類別 (或它現有的子類別)。 +在您的子類別中,您需要實作回呼方法,當 Activity 在其生命週期的各種狀態之間轉換時,系統可以呼叫此回呼方法。例如,Activity 建立、停止、繼續或終止時。 + +最重要的兩個回呼方法如下: +

      + +
      +
      {@link android.app.Activity#onCreate onCreate()}
      +
      您必須實作此方法。系統建立您的 Activity 時會呼叫此方法。 +在您的實作中,應該初始化 Activity 的基本元件。 + + 最重要的是,您必須在這裡呼叫 {@link android.app.Activity#setContentView + setContentView()},才能定義 Activity 使用者介面的版面配置。
      +
      {@link android.app.Activity#onPause onPause()}
      +
      系統呼叫此方法做為使用者離開您的 Activity 的第一個指標 (但並非一定表示該 Activity 遭到終止)。 +您通常需要透過這個方法提交要在目前的使用者工作階段以外保留的任何變更 (原因在於使用者可能不會返回)。 + +
      +
      + +

      還有一些您應使用的其他生命週期回呼方法,以便提供 Activity 之間流暢的使用者體驗,並處理讓您的 Activity 停止、甚至終止的非預期中斷。 + +如需所有生命週期回呼方法的相關資訊,請參閱管理 Activity 生命週期。 +

      + + + +

      實作使用者介面

      + +

      Activity 的使用者介面是由階層的檢視所提供 — 衍生自 {@link android.view.View} 類別的物件。 +每個檢視都控制 Activity 視窗內特定的長方形空間,並且可以回應使用者的互動。 +例如,檢視可能是按鈕,使用者觸碰此按鈕時會初始化一個動作。 +

      + +

      Android 提供許多現成的檢視,您可以用於設計並組織您的版面配置。 +「小工具」是在畫面上提供視覺和互動元素的檢視,例如按鈕、文字欄位、核取方塊或只是一張影像。 +「版面配置」是衍生自 {@link +android.view.ViewGroup} 的檢視,讓子檢視可具有獨特的版面配置模型,例如線性版面配置、網格版面配置或相對版面配置。 +您也可以製作 {@link android.view.View} 和 +{@link android.view.ViewGroup} 類別 (或現有子類別) 的子類別,以建立您自己的小工具和版面配置,然後套用到您的 Activity 版面配置。 +

      + +

      使用檢視定義版面配置的最常見方式,是使用 XML 版面配置檔案 (儲存於您的應用程式資源)。 +這樣一來,您可以分別維護使用者介面的設計和定義 Activity 行為的原始程式碼。 +您可以使用 {@link android.app.Activity#setContentView(int) setContentView()} 傳送版面配置的資源 ID,將版面配置設為 Activity 的 UI。 + +不過,您也可以透過將新的 {@link +android.view.View} 插入 {@link android.view.ViewGroup},然後藉由將根 +{@link android.view.ViewGroup} 傳送給 {@link android.app.Activity#setContentView(View) +setContentView()} 來使用該版面配置,以便在您的 Activity 程式碼中建立新的 {@link android.view.View},並且建置視圖層次。 +

      + +

      如需關於建立使用者介面的詳細資訊,請參閱使用者介面

      + + + +

      在宣示說明中宣告 Activity

      + +

      您必須在宣示說明檔案中宣告 Activity,系統才能加以存取。 +如要宣告您的 Activity,請開啟宣示說明檔案,然後新增 {@code <activity>} 元素做為 {@code <application>} 元素的下層物件。 + +範例:

      + +
      +<manifest ... >
      +  <application ... >
      +      <activity android:name=".ExampleActivity" />
      +      ...
      +  </application ... >
      +  ...
      +</manifest >
      +
      + +

      您可以包括此元素中的其他屬性 (attribute) 來定義屬性 (property),例如 Activity 的標籤、Activity 的圖示或 Activity UI 的設計風格。{@code android:name} 屬性 (attribute) 是唯一必須的屬性 — 它會指定 Activity 的類別名稱。 + + +一旦發佈應用程式,您就不得變更此名稱,這是因為這樣做可能會破壞一些功能,例如應用程式捷徑 (閱讀部落格貼文:不能變更的事項)。 + + +

      + +

      請參閱 {@code <activity>} 元素參考文件,進一步瞭解如何在宣示說明中宣告 Activity。 +

      + + +

      使用意圖篩選器

      + +

      {@code +<activity>} 元素也可以指定各種意圖篩選器 — 使用 {@code +<intent-filter>} 元素 — 以便宣告其他應用程式元件啟動它的方式。 +

      + +

      使用 Android SDK 工具建立新的應用程式時,自動為您建立的 +虛設常式 Activity 會內含意圖篩選器。含意圖篩選器會宣告回應 +「主要」動作的 Activity,並且應放置在「啟動器」類別。意圖篩選器看起來會如下所示: +

      + +
      +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
      +    <intent-filter>
      +        <action android:name="android.intent.action.MAIN" />
      +        <category android:name="android.intent.category.LAUNCHER" />
      +    </intent-filter>
      +</activity>
      +
      + +

      {@code +<action>} 元素指出這是應用程式的「主要」進入點。{@code +<category>} 元素指出此 Activity 應列於系統的應用程式啟動器 (以允許使用者啟動此 Activity)。 +

      + +

      如果您本來就要讓應用程式獨自運作,不要讓其他應用程式啟動其 Activity,則不需要任何其他意圖篩選器。 +只有一個 Activity 可以有「主要」動作和「啟動器」類別,如同上一個範例所示。 +您不要讓其他應用程式使用的 Activity,則不應使用意圖篩選器,而且您可以使用明確的意圖自行加以啟動 (將於以下小節討論)。 + +

      + +

      不過,如果您的 Activity 需要回應從其他應用程式 (和您自己的應用程式) 傳送過來的隱含式意圖,則必須為您的 Activity 定義額外的意圖篩選器。 + +針對您要回應的意圖類型,您必須包括 {@code +<intent-filter>},其中內含 +{@code +<action>} 元素和 {@code +<category>} 元素 (選用) 和/或 {@code +<data>} 元素。這些元素會指定您的 Activity 可以回應哪些類型的意圖。 +

      + +

      如需關於您的 Activity 如何回應意圖的詳細資訊,請參閱意圖和意圖篩選器。 +

      + + + +

      啟動 Activity

      + +

      透過呼叫 {@link android.app.Activity#startActivity + startActivity()}、將 {@link android.content.Intent} (描述要啟動的 Activity) 傳給它,您可以啟動另一個 Activity。 +意圖會指出您要啟動的那個 Activity,或描述您要執行的動作類型 (而讓系統為您選取適當的 Activity,甚至可以是來自不同應用程式的 Activity)。 + + +意圖也可以攜帶少量的資料給已啟動的 Activity 使用。 +

      + +

      在您自己的應用程式內運作時,通常只要啟動已知的 Activity。 + 建立意圖並明確定義您要啟動的 Activity (使用類別名稱),可以達成此目的。 +例如,以下示範 Activity 如何啟動另一個名為 {@code +SignInActivity} 的 Activity:

      + +
      +Intent intent = new Intent(this, SignInActivity.class);
      +startActivity(intent);
      +
      + +

      不過,您的應用程式也希望可以執行其他動作,例如使用您 Activity 中的資料以傳送電子郵件、文字訊息或狀態更新。 +在此情況下,您的應用程式可能就沒有專屬的 Activity 來執行這類動作。因此,您可以改為運用裝置上其他應用程式提供的 Activity。讓這些 Activity 為您執行所需的動作。 + +這正是意圖寶貴的地方 — 您可以建立意圖,在其中描述您要執行的動作,然後系統會從另一個應用程式啟動適當的 Activity。 + + +如果有多個 Activity 都可以處理意圖,則使用者可以選取要使用哪個 Activity。 +例如,如果要讓使用者傳送電子郵件訊息,您可以建立下列意圖: + +

      + +
      +Intent intent = new Intent(Intent.ACTION_SEND);
      +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
      +startActivity(intent);
      +
      + +

      加入意圖的 {@link android.content.Intent#EXTRA_EMAIL} 額外值是一個字串陣列,由應該要寄送電子郵件的電子郵件地址所組成。 +電子郵件應用程式回應此意圖時,它會讀取額外值中提供的字串陣列,並將這些內容放置在編寫電子郵件表單的「收件人」欄位。 + +在此情況下,電子郵件應用程式的 Activity 會啟動,並且會在使用者完成後,繼續您的 Activity。 +

      + + + + +

      啟動 Activity 以取得結果

      + +

      有時候,您會想要收到由您啟動 Activity 的結果。如要接收結果,請透過呼叫 {@link android.app.Activity#startActivityForResult + startActivityForResult()} (而非 {@link android.app.Activity#startActivity + startActivity()}) 以啟動 Activity。 +如要接收後續 Activity 的結果,請實作 {@link android.app.Activity#onActivityResult onActivityResult()} 回呼方法。 + +後續 Activity 完成時,會將 {@link +android.content.Intent} 中的結果傳回到您的 {@link android.app.Activity#onActivityResult onActivityResult()} 方法。 +

      + +

      例如,您可能會讓使用者在聯絡人中挑選一位,讓您的 Activity 可以針對該聯絡人的資訊進行一些處理。 +以下示範如何建立這類意圖,並處理結果: +

      + +
      +private void pickContact() {
      +    // Create an intent to "pick" a contact, as defined by the content provider URI
      +    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
      +    startActivityForResult(intent, PICK_CONTACT_REQUEST);
      +}
      +
      +@Override
      +protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      +    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
      +    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
      +        // Perform a query to the contact's content provider for the contact's name
      +        Cursor cursor = getContentResolver().query(data.getData(),
      +        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
      +        if (cursor.moveToFirst()) { // True if the cursor is not empty
      +            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
      +            String name = cursor.getString(columnIndex);
      +            // Do something with the selected contact's name...
      +        }
      +    }
      +}
      +
      + +

      此範例顯示您應該在您的 {@link +android.app.Activity#onActivityResult onActivityResult()} 方法中使用的基本邏輯,以便處理 Activity 結果。 +第一個條件會檢查要求是否成功 — 如果成功,則{@code resultCode} 會是 {@link android.app.Activity#RESULT_OK} —,以及此結果回應的要求是否為已知的要求 — 範例中的 {@code requestCode} 符合以 {@link android.app.Activity#startActivityForResult +startActivityForResult()} 傳送的第二個參數。 + + +程式碼在這裡透過查詢 {@link android.content.Intent} ({@code data} 參數) 中傳回的資料,來處理 Activity 結果。 +

      + +

      其中的過程是,{@link +android.content.ContentResolver} 針對內容供應程式執行查詢,所傳回的 +{@link android.database.Cursor} 可以讀取查詢到的資料。如需詳細資訊,請參閱內容供應程式。 +

      + +

      如需關於使用意圖的詳細資訊,請參閱意圖和意圖篩選器。 +

      + + +

      關閉 Activity

      + +

      呼叫 Activity 的 {@link android.app.Activity#finish +finish()} 方法,可以關閉此 Activity。您也可以呼叫 +{@link android.app.Activity#finishActivity finishActivity()},關閉您之前啟動的個別 Activity。

      + +

      注意:大多數情況,您不應使用這些方法明確地結束 Activity。 +如同下一節所討論的 Activity 生命週期,Android 系統會為您管理 Activity 的生命週期,所以您不需要結束您自己的 Activity。 + +呼叫這些方法對使用者體驗有負面的影響,只有在您十分確定不希望使用者返回 Activity 的此執行個體時,才加以呼叫。 + +

      + + +

      管理 Activity 生命週期

      + +

      實作回呼方法來管理 Activity 的生命週期,對於開發強大且有彈性的應用程式來說,相當重要。 + +Activity 的生命週期受到相關其他 Activity、本身的工作以及返回堆疊的直接影響。 +

      + +

      Activity 基本上有以下三種狀態:

      + +
      +
      已繼續
      +
      Activity 位於螢幕的前景,具有使用者焦點 (此狀態有時候也稱為「執行中」)。 +
      + +
      已暫停
      +
      前景中有其他具備焦點的 Activity,但系統仍然可以看到這個 Activity。也就是說,這個 Activity 上方有另一個,該 Activity 為半透明,或是未覆蓋整個螢幕。 + +已暫停的 Activity 是完全有效的 ({@link android.app.Activity} 物件會保留在記憶體中、維護所有狀態和成員資訊,以及繼續附加至視窗管理員),但在系統記憶體極低的情況下會遭到終止。 + +
      + +
      已停止
      +
      另一個 Activity 已完全遮蓋原本的 Activity (原本的 Activity 現在位於「背景」)。 +已停止的 Activity 仍然是有效的 ({@link android.app.Activity} 物件會保留在記憶體中、維護所有狀態和成員資訊,但「不會」附加至視窗管理員)。 + +只是使用者不會再看到已停止的 Activity,而且其他地方需要記憶體時,系統會將它終止。 +
      +
      + +

      如果 Activity 已暫停或已停止,系統可以透過要求它結束 (呼叫其 {@link android.app.Activity#finish finish()} 方法) 或直接終止其處理程序,將它從記憶體中刪除。 + +Activity 遭到結束或終止後,再次開啟時,必須全部重新建立。 +

      + + + +

      實作生命週期回呼

      + +

      Activity 在不同的狀態之間轉換時 (如上所述),會透過各種回呼方法進行通知。 +所有的回呼方法都是虛設,請加以覆寫,以便在 Activity 狀態變更時,執行適當的工作。 +下列主要 Activity 包括每個基礎生命週期方法: +

      + + +
      +public class ExampleActivity extends Activity {
      +    @Override
      +    public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        // The activity is being created.
      +    }
      +    @Override
      +    protected void {@link android.app.Activity#onStart onStart()} {
      +        super.onStart();
      +        // The activity is about to become visible.
      +    }
      +    @Override
      +    protected void {@link android.app.Activity#onResume onResume()} {
      +        super.onResume();
      +        // The activity has become visible (it is now "resumed").
      +    }
      +    @Override
      +    protected void {@link android.app.Activity#onPause onPause()} {
      +        super.onPause();
      +        // Another activity is taking focus (this activity is about to be "paused").
      +    }
      +    @Override
      +    protected void {@link android.app.Activity#onStop onStop()} {
      +        super.onStop();
      +        // The activity is no longer visible (it is now "stopped")
      +    }
      +    @Override
      +    protected void {@link android.app.Activity#onDestroy onDestroy()} {
      +        super.onDestroy();
      +        // The activity is about to be destroyed.
      +    }
      +}
      +
      + +

      注意:實作這些生命週期方法時,一定要先呼叫超級類別實作,才能執行任何工作,如同以上範例所示。 +

      + +

      總而言之,這些方法定義了 Activity 的整個生命週期。透過實作這些方法,您可以監視 Activity 生命週期中的三個巢狀迴圈: +

      + +
        +
      • Activity 的整個生命週期是介於 {@link +android.app.Activity#onCreate onCreate()} 呼叫和 {@link +android.app.Activity#onDestroy} 呼叫之間。您的 Activity 應在 {@link android.app.Activity#onCreate onCreate()} 中設定「全域」狀態 (例如定義版面配置),並且在 {@link android.app.Activity#onDestroy} 中釋放所有剩餘的資源。 + +例如,如果您的 Activity 有一個執行緒在背景執行,並從網路下載資料,那麼最好可以在 {@link android.app.Activity#onCreate onCreate()} 中建立該執行緒,然後在 {@link +android.app.Activity#onDestroy} 中將它停止。 + +
      • + +
      • Activity 的可見生命週期是介於 {@link +android.app.Activity#onStart onStart()} 呼叫和 {@link +android.app.Activity#onStop onStop()} 呼叫之間。在此期間,使用者可以在螢幕上看到該 Activity,並與之互動。 +例如,啟動新的 Activity,而這個 Activity 不再看得到時,會呼叫 {@link android.app.Activity#onStop onStop()}。 +在這兩個方法之間,您可以維護需要讓使用者看到的資源。 +例如,您可以在{@link +android.app.Activity#onStart onStart()} 中註冊 +{@link android.content.BroadcastReceiver},以監視影響到 UI 的變更,然後當使用者不再看到您顯示的內容時,在 {@link android.app.Activity#onStop onStop()} 中將它取消註冊。 + +系統可以在 Activity 的整個生命週期時,多次呼叫 {@link android.app.Activity#onStart onStart()} 和 {@link +android.app.Activity#onStop onStop()},因為 Activity 對使用者而言會一直在顯示和隱藏之間切換。 +

      • + +
      • Activity 的前景生命週期是介於 {@link +android.app.Activity#onResume onResume()} 呼叫和 {@link android.app.Activity#onPause +onPause()} 呼叫之間。在此期間,Activity 會在螢幕上所有其他 Activity 的前面,而且具有使用者輸入焦點。 +Activity 可以經常在前景和背景之間轉換 — 例如,裝置進入睡眠或顯示對話方塊時,會呼叫 {@link android.app.Activity#onPause onPause()}。 + +由於此狀態可能會經常轉換,因此這兩個方法中的程式碼應十分精簡,這樣可以避免使用者在轉換時等待。 +

      • +
      + +

      圖 1 說明 Activity 在轉換狀態時的可能迴圈和路徑。長方形代表您可以實作的回呼方法,以便 Activity 在轉換狀態時執行操作。 + +

      + + +

      圖 1.Activity 生命週期。

      + +

      表 1 列出相同的生命週期回呼方法,其中詳細描述每個回呼方法,並且指出每個回呼方法在 Activity 整個生命週期中的位置,包括系統是否可以在回呼方法完成後終止 Activity。 + + +

      + +

      表 1.Activity 生命週期回呼方法摘要。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      方法 描述 完成後是否可終止? 下一個方法
      {@link android.app.Activity#onCreate onCreate()}一開始建立 Activity 時呼叫。 + 您應該在這裡所有的一般靜態設定 — 建立檢視、將資料繫結至清單等等。 +「套件」物件會傳送給此方法,如果有擷取到狀態,此物件會內含 Activity 的上一個狀態 (請參閱下文的儲存 Activity 狀態)。 + + + +

      後面一定會接著 {@code onStart()}。

      {@code onStart()}
          {@link android.app.Activity#onRestart +onRestart()}Activity 已停止後,即將再次啟動之前呼叫。 + +

      後面一定會接著 {@code onStart()}。

      {@code onStart()}
      {@link android.app.Activity#onStart onStart()}Activity 即將要讓使用者看到之前呼叫。 +

      如果 Activity 移到前景,後面會接著 {@code onResume()},如果變成隱藏,後面會接著 {@code onStop()}。 +

      {@code onResume()}

      {@code onStop()}
          {@link android.app.Activity#onResume onResume()}Activity 即將與使用者開始互動之前呼叫。 +此時,Activity 位於 Activity 堆疊的最上方,接受使用的輸入。 + +

      後面一定會接著 {@code onPause()}。

      {@code onPause()}
      {@link android.app.Activity#onPause onPause()}系統即將開始繼續另一個 Activity 時呼叫。 +認可對永久資料的未儲存變更、停止動畫,以及會使用 CPU 等等資源的其他操作時,一般會使用此方法。 + +不論此操作會執行什麼動作,都要快速完成,這是因為此方法傳回後,才會繼續下一個 Activity。 + +

      如果 Activity 返回前景,後面會接著 {@code onResume()},如果變成使用者看不到它,後面會接著 {@code onStop()}。 + +

      {@code onResume()}

      {@code onStop()}
      {@link android.app.Activity#onStop onStop()}使用者看不到 Activity 時呼叫。Activity 遭到終止或另一個 Activity (不論是現有 Activity 或新的 Activity) 已經繼續,而且將它覆蓋住,就會發生此情形。 + + +

      如果 Activity 回來與使用者互動,後面會接著 {@code onRestart()},如果 Activity 離開,後面會接著 + {@code onDestroy()}。 +

      {@code onRestart()}

      {@code onDestroy()}
      {@link android.app.Activity#onDestroy +onDestroy()}在 Activity 終止前呼叫。Activity 會接收到的最後呼叫。 +Activity 正在完成 (有人在 Activity 上呼叫 {@link android.app.Activity#finish + finish()}),或系統正在暫時終止 Activity 的這個執行個體以節省空間時,會呼叫此方法。 + +您可以使用 {@link + android.app.Activity#isFinishing isFinishing()} 方法分辨這兩種情況。 +
      + +

      此欄標示為「完成後是否可終止?」表示系統是否可以「在方法傳回後」隨時終止代管 Activity 的處理程序,而不需要執行另一行 Activity 的程式碼。 + +有三個方法標示為「是」:({@link +android.app.Activity#onPause +onPause()}、{@link android.app.Activity#onStop onStop()} 以及 {@link android.app.Activity#onDestroy +onDestroy()})。由於 {@link android.app.Activity#onPause onPause()} 是這三個方法中的第一個方法,Activity 建立後,{@link android.app.Activity#onPause onPause()} 是一定會呼叫的最後一個方法,之後處理程序才能加以終止 — 如果系統必須緊急收回記憶體,可能就不會呼叫 {@link +android.app.Activity#onStop onStop()} 和 {@link android.app.Activity#onDestroy onDestroy()}。 + + + +因此,您應該使用 {@link android.app.Activity#onPause onPause()} 將極為重要的永久資料 (例如使用者的編輯內容) 寫入儲存空間。 +不過,您應選擇哪些資訊必須在 {@link android.app.Activity#onPause onPause()} 期間加以保留,這是因為此方法中的任何程序遭到封鎖,都無法轉換到下一個 Activity,而且會讓使用者體驗變慢。 + + +

      + +

      在「是否可終止」欄標示為「否」的方法,從呼叫這些方法的那一刻起,會保護代管 Activity 的處理程序不會遭到終止。 +因此,Activity 從 {@link android.app.Activity#onPause onPause()} 傳回到呼叫 + {@link android.app.Activity#onResume onResume()} 的期間為可終止。 +再次呼叫和傳回 +{@link android.app.Activity#onPause onPause()} 之前,Activity 為不可終止。

      + +

      注意:表 1 中定義為不是「可終止」的 Activity 仍可遭到系統終止 — 但只會發生在無技可施的情況下。 + +Activity 可加以終止的時機,於處理和執行緒文件中有更詳細的討論。 + +

      + + +

      儲存 Activity 狀態

      + +

      管理 Activity 生命週期的簡介中提到,當 Activity 暫停或停止時,會保留 Activity 的狀態。 + +這點是成立的,原因在於當 {@link android.app.Activity} 物件暫停或停止時,它仍然保留在記憶體中 — 關於它的成員和目前狀態的所有資訊,仍然為有效的。 + +因此,使用者在 Activity 內所做的任何變更,都會保留下來。所以,當 Activity 返回前景 (當它「繼續」時),那些變更仍然會在原地。 + +

      + +

      不過,當系統終止 Activity 以收回記憶體時,{@link +android.app.Activity} 物件會遭到終止,所以系統就無法將它及其狀態完好無缺地繼續。 +如果使用者瀏覽回 {@link android.app.Activity} 物件,系統必須加以重新建立。 +但是,使用者不會注意到系統已終止該 Activity 並加以重新建立,可能因此期待 Activity 就跟之前的狀態一樣。 + +如果是這樣,您可以實作額外的回呼方法,以確認 Activity 狀態相關的重要資訊會保留下來。此回呼方法讓您儲存關於 Activity 狀態的資訊:{@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}。 + +

      + +

      系統會在終止 Activity 之前呼叫 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()}。 +系統會將 {@link android.os.Bundle} 傳送給此方法,您可以使用 {@link +android.os.Bundle#putString putString()} 和 {@link +android.os.Bundle#putInt putInt()} 之類的方法,將 Activity 相關的狀態資訊以名稱-值組的方式儲存。 + +然後,如果系統終止應用程式處理程序,並且使用者瀏覽回您的 Activity,則系統會重新建立 Activity,然後將 {@link android.os.Bundle} 傳送給 {@link android.app.Activity#onCreate onCreate()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}。 + +您可以使用以上其中一種方法,從 {@link android.os.Bundle} 擷取已儲存的狀態,然後還原 Activity 狀態。 + +如果沒有狀態資訊可供還原,則傳送過來的 {@link +android.os.Bundle} 為空值 (null) (第一次建立 Activity 時,就是這種情況)。 +

      + + +

      圖 2.Activity 返回使用者焦點,同時具備完整狀態的兩種方式:Activity 遭到終止,然後重新建立,Activity 必須還原之前儲存的狀態;或者Activity 已停止,然後繼續,Activity 狀態維持完整。 + + +

      + +

      注意:不保證在您的 Activity 遭到終止之前,一定會呼叫 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},這是因為有時會發生不需要儲存狀態的情形 (例如,使用者使用「返回」按鈕離開您的 Activity 時,原因在於使用者明確地關閉 Activity)。 + + + +如果系統要呼叫 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},會在 {@link +android.app.Activity#onStop onStop()} 之前呼叫,可能會在 {@link android.app.Activity#onPause +onPause()} 之前呼叫。

      + +

      不過,即使您沒有做任何事,沒有實作 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()},有些 Activity 狀態會經由{@link android.app.Activity} 類別的預設實作 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 而還原。 +具體來說,預設實作會針對版面配置中的每一個 {@link +android.view.View} 呼叫對應的 {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} 方法,這樣可以讓每個檢視提供本身應該要儲存的相關資訊。 + +在 Android 架構中,幾乎每個小工具都適當地實作此方法,因此 UI 中可見的變更都會自動儲存,並於 Activity 重新建立時加以還原。 + +例如,{@link android.widget.EditText} 小工具會儲存使用者輸入的任何文字,而 {@link android.widget.CheckBox} 小工具則會儲存是否勾選。 + +您只要針對需要儲存狀態的每個小工具,提供唯一的 ID (使用 {@code android:id} 屬性) 即可。 +如果小工具沒有 ID,則系統無法儲存其狀態。 +

      + + + +

      儘管 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的預設實作會儲存 Activity UI 相關的實用資訊,您仍然需要加以覆寫,以儲存額外的資訊,例如,您需要儲存 Activity 生命期間變更的成員值 (此真可能與 UI 中要還原的值有關,但保留那些 UI 值的成員預設不會加以還原)。 + + + +

      + +

      由於 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的預設實作可協助儲存 UI 的狀態,因此,如果您覆寫此方法以儲存額外的狀態資訊,一定要再執行任何動作之前,呼叫 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 的超級類別實作。 + + +同樣的情況,您也要呼叫 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} 的超級類別實作 (如果您將它覆寫),讓預設實作可以還原檢視狀態。 +

      + +

      注意:由於不保證一定會呼叫 {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()},您只能用它來記錄 Activity 的短暫狀態 (UI 的狀態) — 不應該用它儲存永久資料。 + +而是要在使用者離開 Activity 時,利用 {@link +android.app.Activity#onPause onPause()} 來儲存永內資料 (例如要儲存到資料庫的資料)。 +

      + +

      測試應用程式是否能夠還原其狀態的好方式,只要旋轉裝置,改變螢幕方向即可。 +螢幕方向改變時,系統會終止 Activity 並重新建立,以便套用針對新的螢幕設定而提供使用的替代資源。 + +單單就這一點而言,您的 Activity 在重新建立時可以完整還原,就非常重要了,這是因為使用者操作應用程式時會經常旋轉螢幕。 + +

      + + +

      處理設定變更

      + +

      有些裝置設定可以在執行階段期間進行變更 (例如,螢幕方向、鍵盤可用性和語言)。 +發生這類變更時,Android 會重新建立執行中的 Activity (系統呼叫 {@link android.app.Activity#onDestroy},然後立即呼叫 {@link +android.app.Activity#onCreate onCreate()})。 +此行為的設計透過自動重新載入應用程式與提供的替代資源 (例如針對不同的螢幕方向和大小的不同版面配置),可協助應用程式適應新的設定。 + + +

      + +

      如果您如上所述正確地設計 Activity,處理由於螢幕方向變更的重新啟動,然後還原 Activity 的狀態,您的應用程式對於 Activity 生命週期中的不可預期事件,會更具有抗性。 + +

      + +

      處理這類重新啟動的最佳方式,是使用 {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} 和 {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (或 {@link +android.app.Activity#onCreate onCreate()}) 儲存並還原 Activity 的狀態,如同上一節所討論。 +

      + +

      如需關於執行階段發生的設定變更,以及如何加以處理的詳細資訊,請參閱處理執行階段變更指南。 + +

      + + + +

      協調 Activity

      + +

      Activity 啟動另一個 Activity 時,它們兩者都經歷生命週期轉換。第一個 Activity 暫停並停止 (雖然如果仍然可以在背景看到它,表示它並未真的「停止」),而另一個 Activity 建立起來。 + +若這些 Activity 共用儲存到磁碟或其他地方的資料,第一個 Activity 在第二個 Activity 已建立之前,不會完全停止,瞭解這件事很重要。然而,啟動第二個 Activity 的處理程序與停止第一個 Activity 的處理程序會重疊。 + + +

      + +

      生命週期回呼的順序定義的很好,尤其是當兩個 Activity 位於相同的處理程序,而其中一個 Activity 啟動另一個 Activity 時。 +Activity A 啟動 Activity B 時所發生的操作順利如下: +

      + +
        +
      1. Activity A 的 {@link android.app.Activity#onPause onPause()} 方法會執行。
      2. + +
      3. Activity B 按順序執行 {@link android.app.Activity#onCreate onCreate()}、{@link +android.app.Activity#onStart onStart()} 以及 {@link android.app.Activity#onResume onResume()} 方法。 +(Activity B 現在擁有使用者焦點)。
      4. + +
      5. 然後,如果螢幕上已經看不到 Activity A,就會執行 Activity A 的 {@link +android.app.Activity#onStop onStop()} 方法。
      6. +
      + +

      這一段可預測的生命週期回呼,可以讓您管理 Activity 之間資訊的轉換。 +例如,如果第一個 Activity 停止時,您必須寫入資料庫,讓接下來的 Activity 可以讀取,那麼您應該在 {@link android.app.Activity#onPause onPause()} 期間寫入,而不是在 {@link +android.app.Activity#onStop onStop()} 期間寫入。 + +

      + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd b/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd new file mode 100644 index 0000000000000000000000000000000000000000..da47634b894fa7be7a544427a5eed99d3d54bb60 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=繫結服務 +parent.title=服務 +parent.link=services.html +@jd:body + + +
      +
        +

        本文件內容

        +
          +
        1. 基本概念
        2. +
        3. 建立繫結服務 +
            +
          1. 延伸 Binder 類別
          2. +
          3. 使用 Messenger
          4. +
          +
        4. +
        5. 繫結至服務
        6. +
        7. 管理繫結服務的週期
        8. +
        + +

        重要類別

        +
          +
        1. {@link android.app.Service}
        2. +
        3. {@link android.content.ServiceConnection}
        4. +
        5. {@link android.os.IBinder}
        6. +
        + +

        範例

        +
          +
        1. {@code + RemoteService}
        2. +
        3. {@code + LocalService}
        4. +
        + +

        另請參閱

        +
          +
        1. 服務
        2. +
        +
      + + +

      繫結服務是主從介面中的伺服器。繫結服務讓元件 (例如 Activity) 可以繫結至服務、傳送要求、接收回應,甚至執行處理程序間通訊 (IPC)。 + +繫結服務通常只會在服務另一個應用程式元件時才存在,而且不會在背景中一直執行。 +

      + +

      本文會告訴您如何建立繫結服務,包括如何從其他應用程式元件繫結至服務。 +不過,如需關於服務的一般其他資訊,可參閱服務文件,例如如何從服務傳遞通知、設定服務在前景執行等等。 + +

      + + +

      基本概念

      + +

      繫結服務是 {@link android.app.Service} 類別的實作,允許其他應用程式繫結至此實作,並與之互動。 +若服務要要提供繫結功能,您必須實作 {@link android.app.Service#onBind onBind()} 回呼方法。 +此方法會傳回 {@link android.os.IBinder} 物件,其中定義程式設計介面。用戶端可以使用此介面與服務互動。 + +

      + + + +

      用戶端可以透過呼叫 {@link android.content.Context#bindService +bindService()} 繫結至服務。這麼做時,用戶端必須實作 {@link +android.content.ServiceConnection} 以監視與服務之間的連線狀況。{@link +android.content.Context#bindService bindService()} 方法會立即傳回 (不含值),當 Android 系統在用戶端和服務之間建立連線時,會呼叫 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} (位於 {@link +android.content.ServiceConnection}) 以傳遞 {@link android.os.IBinder} (用戶端可以用來與服務溝通)。 + + +

      + +

      多個用戶端可以同時連線至該服務。不過,系統只會在用戶端第一次繫結時,呼叫服務的 +{@link android.app.Service#onBind onBind()} 方法,以擷取 {@link android.os.IBinder}。 +然後,系統會將同一個 {@link android.os.IBinder} 傳遞給其他任何繫結的用戶端,不會再次呼叫 {@link android.app.Service#onBind onBind()}。 +

      + +

      最後一個用戶端從服務解除繫結時,系統會終結服務 (除非服務也是由 {@link android.content.Context#startService startService()} 所啟動)。 +

      + +

      實作繫結服務時,最重要的部分是定義您的 {@link android.app.Service#onBind onBind()} 回呼方法傳回的介面。 +您可以使用幾種不同的方式來定義服務的 {@link android.os.IBinder} 介面,以下小節將討論其中的每一種技術。 + +

      + + + +

      建立已繫結的服務

      + +

      建立提供繫結功能的服務時,您必須提供 {@link android.os.IBinder} 程式設計介面。用戶端可以使用此介面與服務互動。 +定義介面有三種方式: +

      + +
      +
      延伸 Binder 類別
      +
      如果您的服務只讓您自己的應用程式使用,而且跟用戶端執行在同一個處理程序 (此作法很常見),則應該要透過延伸 {@link android.os.Binder} 類別,並從 +{@link android.app.Service#onBind onBind()} 傳回此類別的執行個體來建立介面。 + +用戶端接收 {@link android.os.Binder} 後,可以用它直接存取 {@link android.os.Binder} 實作或 {@link android.app.Service} 提供的公用方法。 + + +

      若您的服務只是您自己應用程式的背景工作者,建議使用此方式。 +除非您的服務是由其他應用程式或跨個別處理程序使用,才不用以此方式建立自己的介面。 +

      + +
      使用 Messenger
      +
      如果您的介面需要跨不同處理程序運作,則可以建立內含 {@link android.os.Messenger} 服務的介面。 +此服務定義了回應不同類型 {@link +android.os.Message} 物件的 {@link android.os.Handler}。 +這個 {@link android.os.Handler} 是 {@link android.os.Messenger} 的基礎,之後可以與用戶端分享 {@link android.os.IBinder},讓用戶端使用 {@link +android.os.Message} 物件傳送命令給此服務。 + +此外,用戶端可以定義專屬的 {@link android.os.Messenger},服務就可以傳回訊息。 + +

      這是處理程序間通訊 (IPC) 最簡單的執行方式,因為 {@link +android.os.Messenger} 會將所有要求都排列到單一個執行緒,因此,就不用將服務設計成執行緒安全的形式。 +

      +
      + +
      使用 AIDL
      +
      AIDL (Android 介面定義語言) 的工作是將物件分解為作業系統瞭解的始類型,然後將這些原始類型在各個處理程序間進行封送,以執行 IPC。先前使用 {@link android.os.Messenger} 的技術,實際上就是以 AIDL 作為底層結構。 + + +如上所述,{@link android.os.Messenger} 會在單一執行緒中建立所有用戶端要求的佇列,所以服務一次會接收一個要求。 +不過,如果您要讓服務可以同時處理多個要求,則可以直接使用 AIDL。 + +在此情況下,您的服務必須具備多執行緒的功能,而且是以執行緒安全的形式建置。 +

      如要直接使用 AIDL,您必須建立 {@code .aidl} 檔案,並在其中定義程式設計介面。 +Android SDK 工具會使用此檔案產生一個抽象類別,以便實作介面並處理 IPC。您就可以在服務內加以延伸。 + +

      +
      +
      + +

      注意:大部分應用程式不應使用 AIDL 建立繫結服務,若自行建立的話,就需要實作多執行緒功能,可能會導致更複雜的實作。 + +因此,AIDL 不適用於大部分應用程式,而且本文不會討論如何在您的服務中使用 AIDL。 +如果您確定需要直接使用 AIDL,請參閱 AIDL 文件。 + +

      + + + + +

      延伸 Binder 類別

      + +

      如果您的服務只會在本機應用程式使用,而且不需要跨處理程序運作,則可以實作您自己的 {@link android.os.Binder} 類別,讓用戶端直接存取服務中的公用方法。 + +

      + +

      注意:用戶端和服務都位於相同應用程式和處理程序時才適用,這也是最常見的情況。 +例如,需要將 Activity 繫結到其專屬服務 (在背景播放音樂)的音樂應用程式,就很適合。 + +

      + +

      設定的方式如下:

      +
        +
      1. 在您的服務中建立 {@link android.os.Binder} 的執行個體,以具備以下其中一種功用: +
          +
        • 包含用戶端可以呼叫的公用方法
        • +
        • 傳回目前的 {@link android.app.Service} 執行個體,其中含有用戶端可以呼叫的公用方法 +
        • +
        • 傳回由服務所裝載另一個類別的執行個體,而此服務含有用戶端可以呼叫的公用方法 +
        • +
        +
      2. 從 {@link +android.app.Service#onBind onBind()} 回呼方法傳回此 {@link android.os.Binder} 的執行個體。
      3. +
      4. 在用戶端方面,從 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼方法接收 {@link android.os.Binder},然後使用提供的方法呼叫繫結服務。 +
      5. +
      + +

      注意:服務和用戶端必須位於相同的應用程式,是因為用戶端才可以轉換傳回的物件,然後正確地呼叫其 API。 +服務和用戶端也必須位於相同的處理程序,因為此技術不會執行任何跨處理程序間旳封送。 + +

      + +

      例如,以下的服務可以讓用戶端透過實作的 {@link android.os.Binder} 存取服務中的方法: +

      + +
      +public class LocalService extends Service {
      +    // Binder given to clients
      +    private final IBinder mBinder = new LocalBinder();
      +    // Random number generator
      +    private final Random mGenerator = new Random();
      +
      +    /**
      +     * Class used for the client Binder.  Because we know this service always
      +     * runs in the same process as its clients, we don't need to deal with IPC.
      +     */
      +    public class LocalBinder extends Binder {
      +        LocalService getService() {
      +            // Return this instance of LocalService so clients can call public methods
      +            return LocalService.this;
      +        }
      +    }
      +
      +    @Override
      +    public IBinder onBind(Intent intent) {
      +        return mBinder;
      +    }
      +
      +    /** method for clients */
      +    public int getRandomNumber() {
      +      return mGenerator.nextInt(100);
      +    }
      +}
      +
      + +

      {@code LocalBinder} 提供 {@code getService()} 方法,讓用戶端擷取 {@code LocalService} 目前的執行個體。 +這樣可以讓用戶端呼叫服務中的公用方法。 +例如,用戶端可以從服務呼叫 {@code getRandomNumber()}。

      + +

      當按一下按鈕時,會發生繫結至 {@code LocalService} 並呼叫 {@code getRandomNumber()}的 Activity: +

      + +
      +public class BindingActivity extends Activity {
      +    LocalService mService;
      +    boolean mBound = false;
      +
      +    @Override
      +    protected void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        setContentView(R.layout.main);
      +    }
      +
      +    @Override
      +    protected void onStart() {
      +        super.onStart();
      +        // Bind to LocalService
      +        Intent intent = new Intent(this, LocalService.class);
      +        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      +    }
      +
      +    @Override
      +    protected void onStop() {
      +        super.onStop();
      +        // Unbind from the service
      +        if (mBound) {
      +            unbindService(mConnection);
      +            mBound = false;
      +        }
      +    }
      +
      +    /** Called when a button is clicked (the button in the layout file attaches to
      +      * this method with the android:onClick attribute) */
      +    public void onButtonClick(View v) {
      +        if (mBound) {
      +            // Call a method from the LocalService.
      +            // However, if this call were something that might hang, then this request should
      +            // occur in a separate thread to avoid slowing down the activity performance.
      +            int num = mService.getRandomNumber();
      +            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
      +        }
      +    }
      +
      +    /** Defines callbacks for service binding, passed to bindService() */
      +    private ServiceConnection mConnection = new ServiceConnection() {
      +
      +        @Override
      +        public void onServiceConnected(ComponentName className,
      +                IBinder service) {
      +            // We've bound to LocalService, cast the IBinder and get LocalService instance
      +            LocalBinder binder = (LocalBinder) service;
      +            mService = binder.getService();
      +            mBound = true;
      +        }
      +
      +        @Override
      +        public void onServiceDisconnected(ComponentName arg0) {
      +            mBound = false;
      +        }
      +    };
      +}
      +
      + +

      上述範例顯示:用戶端如何使用 +{@link android.content.ServiceConnection} 的實作和 {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼,繫結至服務。下一節提供關於繫結至服務處理程序的詳細資訊。 +

      + +

      注意:上述範例未明確從服務解除繫結,但所有用戶端都應該在適當時間解除繫結 (例如,Activity 暫停時)。 +

      + +

      如要取得更多範例程式碼,請參閱 ApiDemos 中的 {@code +LocalService.java} 類別和 {@code +LocalServiceActivities.java} 類別。

      + + + + + +

      使用 Messenger

      + + + +

      如果服務要和遠端處理程序溝通,則可以使用 +{@link android.os.Messenger} 為您的服務提供介面。此技術讓您不需要使用 AIDL,就可以執行處理程序間通訊 (IPC)。 +

      + +

      以下是使用 {@link android.os.Messenger} 的摘要:

      + +
        +
      • 此服務實作 {@link android.os.Handler},可以從用戶端接收每個呼叫的回呼。 +
      • +
      • {@link android.os.Handler} 用於建立 {@link android.os.Messenger} 物件 (這是 {@link android.os.Handler} 的參照)。 +
      • +
      • {@link android.os.Messenger} 會建立 {@link android.os.IBinder},服務會從 {@link android.app.Service#onBind onBind()} 傳回給用戶端。 +
      • +
      • 用戶端使用 {@link android.os.IBinder} 將 {@link android.os.Messenger} (參照服務的 {@link android.os.Handler}) 具現化,用戶端就可以用來將 +{@link android.os.Message} 物件傳送給服務。 +
      • +
      • 服務會在其 {@link +android.os.Handler} 中接收每個 {@link android.os.Message} — 更明確地說,就是在 {@link android.os.Handler#handleMessage +handleMessage()} 方法中加以接收。
      • +
      + + +

      這樣一來,用戶端就不需要呼叫服務的任何「方法」。用戶端只要傳遞「訊息」({@link android.os.Message} 物件),服務就會在其 {@link android.os.Handler} 中接收。 + +

      + +

      以下是使用 {@link android.os.Messenger} 介面的簡單範例服務:

      + +
      +public class MessengerService extends Service {
      +    /** Command to the service to display a message */
      +    static final int MSG_SAY_HELLO = 1;
      +
      +    /**
      +     * Handler of incoming messages from clients.
      +     */
      +    class IncomingHandler extends Handler {
      +        @Override
      +        public void handleMessage(Message msg) {
      +            switch (msg.what) {
      +                case MSG_SAY_HELLO:
      +                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
      +                    break;
      +                default:
      +                    super.handleMessage(msg);
      +            }
      +        }
      +    }
      +
      +    /**
      +     * Target we publish for clients to send messages to IncomingHandler.
      +     */
      +    final Messenger mMessenger = new Messenger(new IncomingHandler());
      +
      +    /**
      +     * When binding to the service, we return an interface to our messenger
      +     * for sending messages to the service.
      +     */
      +    @Override
      +    public IBinder onBind(Intent intent) {
      +        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
      +        return mMessenger.getBinder();
      +    }
      +}
      +
      + +

      請注意, +{@link android.os.Handler} 中的 {@link android.os.Handler#handleMessage handleMessage()} 方法是服務接收傳入 {@link android.os.Message} 的位置,也是根據 {@link android.os.Message#what} 成員,決定後續執行動作的位置。 +

      + +

      用戶端只需要根據服務傳回的 {@link +android.os.IBinder},建立 {@link android.os.Messenger},然後使用 {@link +android.os.Messenger#send send()} 傳送訊息。例如,以下的簡單 Activity 會繫結至服務,然後將 {@code MSG_SAY_HELLO} 訊息傳遞給服務: +

      + +
      +public class ActivityMessenger extends Activity {
      +    /** Messenger for communicating with the service. */
      +    Messenger mService = null;
      +
      +    /** Flag indicating whether we have called bind on the service. */
      +    boolean mBound;
      +
      +    /**
      +     * Class for interacting with the main interface of the service.
      +     */
      +    private ServiceConnection mConnection = new ServiceConnection() {
      +        public void onServiceConnected(ComponentName className, IBinder service) {
      +            // This is called when the connection with the service has been
      +            // established, giving us the object we can use to
      +            // interact with the service.  We are communicating with the
      +            // service using a Messenger, so here we get a client-side
      +            // representation of that from the raw IBinder object.
      +            mService = new Messenger(service);
      +            mBound = true;
      +        }
      +
      +        public void onServiceDisconnected(ComponentName className) {
      +            // This is called when the connection with the service has been
      +            // unexpectedly disconnected -- that is, its process crashed.
      +            mService = null;
      +            mBound = false;
      +        }
      +    };
      +
      +    public void sayHello(View v) {
      +        if (!mBound) return;
      +        // Create and send a message to the service, using a supported 'what' value
      +        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
      +        try {
      +            mService.send(msg);
      +        } catch (RemoteException e) {
      +            e.printStackTrace();
      +        }
      +    }
      +
      +    @Override
      +    protected void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        setContentView(R.layout.main);
      +    }
      +
      +    @Override
      +    protected void onStart() {
      +        super.onStart();
      +        // Bind to the service
      +        bindService(new Intent(this, MessengerService.class), mConnection,
      +            Context.BIND_AUTO_CREATE);
      +    }
      +
      +    @Override
      +    protected void onStop() {
      +        super.onStop();
      +        // Unbind from the service
      +        if (mBound) {
      +            unbindService(mConnection);
      +            mBound = false;
      +        }
      +    }
      +}
      +
      + +

      注意,此範例並未顯示服務回應用戶端的方式。如果您希望 +服務有所回應,則同時需要在用戶端建立 {@link android.os.Messenger}。之後,用戶端接收 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 回呼時,會傳送 {@link android.os.Message} 給服務,其中包括用戶端的 {@link android.os.Messenger} (位於 {@link android.os.Messenger#send send()} 方法的 {@link android.os.Message#replyTo} 參數中)。 + + +

      + +

      您可以在 {@code +MessengerService.java} (服務) 和 {@code +MessengerServiceActivities.java} (用戶端) 的範例中看到如何進行雙向傳訊的例子。

      + + + + + +

      繫結至服務

      + +

      應用程式元件 (用戶端) 可以透過呼叫 +{@link android.content.Context#bindService bindService()},繫結至服務。然後,Android 系統會呼叫服務的 {@link android.app.Service#onBind +onBind()} 方法 (此方法傳回 {@link android.os.IBinder} 以便與服務互動)。 +

      + +

      繫結為非同步。{@link android.content.Context#bindService +bindService()} 會立即傳回,但「不會」將 {@link android.os.IBinder} 傳回給用戶端。 +如要接收 {@link android.os.IBinder},用戶端必須建立 {@link +android.content.ServiceConnection} 的執行個體,然後將此執行個體傳送給 {@link android.content.Context#bindService +bindService()}。{@link android.content.ServiceConnection} 內含回呼方法,系統會呼叫此方法以傳遞 {@link android.os.IBinder}。 +

      + +

      注意:只有 Activity、服務以及內容提供者可以繫結至服務 — 您不能從廣播接收者繫結至服務。 +

      + +

      因此,如要從用戶端繫結至服務,必須符合下列條件:

      +
        +
      1. 實作 {@link android.content.ServiceConnection}。 +

        實作中必須覆寫兩個回呼方法:

        +
        +
        {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
        +
        系統會呼叫此方法來傳遞 {@link android.os.IBinder} (由服務的 {@link android.app.Service#onBind onBind()} 方法所傳回)。 +
        +
        {@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}
        +
        與服務之間的連線突然遺失時 (例如服務當機或遭到終止時),Android 系統會呼收此方法。 +用戶端解除繫結時,「不會」呼叫此方法。 +
        +
        +
      2. +
      3. 呼叫會傳送 {@link +android.content.ServiceConnection} 實作的 {@link +android.content.Context#bindService bindService()}。
      4. +
      5. 系統呼叫 {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} 回呼方法時,您就可以使用介面定義的方法,開始呼叫服務。 +
      6. +
      7. 如要與服務中斷連線,請呼叫 {@link +android.content.Context#unbindService unbindService()}。 +

        用戶端遭到終結時,會與服務解除繫結。不過,您應該要在與服務完成互動時,或者 Activity 暫停,要讓服務未使用時可以加以關閉的情況下,一定要解除繫結。 + +(以下將有繫結和解除繫結適當時機的詳細討論)。 +

        +
      8. +
      + +

      例如,下列程式碼片段會透過延伸 Binder 類別,將用戶端連線到上述建立的服務,因此用戶端只要將傳回的 +{@link android.os.IBinder} 轉換為 {@code LocalService} 類別,然後要求 {@code +LocalService} 執行個體: +

      + +
      +LocalService mService;
      +private ServiceConnection mConnection = new ServiceConnection() {
      +    // Called when the connection with the service is established
      +    public void onServiceConnected(ComponentName className, IBinder service) {
      +        // Because we have bound to an explicit
      +        // service that is running in our own process, we can
      +        // cast its IBinder to a concrete class and directly access it.
      +        LocalBinder binder = (LocalBinder) service;
      +        mService = binder.getService();
      +        mBound = true;
      +    }
      +
      +    // Called when the connection with the service disconnects unexpectedly
      +    public void onServiceDisconnected(ComponentName className) {
      +        Log.e(TAG, "onServiceDisconnected");
      +        mBound = false;
      +    }
      +};
      +
      + +

      使用此 {@link android.content.ServiceConnection},用戶端可以透過將它傳送給 {@link android.content.Context#bindService bindService()} 而繫結至服務。 +例如:

      + +
      +Intent intent = new Intent(this, LocalService.class);
      +bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
      +
      + +
        +
      • {@link android.content.Context#bindService bindService()} 的第一個參數是 +{@link android.content.Intent},明確地指定服務進行繫結 (儘管意圖是隱含的)。 +
      • +
      • 第二個參數是 {@link android.content.ServiceConnection} 物件。
      • +
      • 第三個參數是旗標,用來指出繫結的選項。如果服務尚未存在,通常是使用 {@link +android.content.Context#BIND_AUTO_CREATE} 以建立服務。其他可能的值為 {@link android.content.Context#BIND_DEBUG_UNBIND}和 {@link android.content.Context#BIND_NOT_FOREGROUND},或者 {@code 0} 代表無。 + +
      • +
      + + +

      其他注意事項

      + +

      以下是關於繫結至服務的一些重要注意事項:

      +
        +
      • 您一定要設陷 {@link android.os.DeadObjectException} 例外狀況 (連線中斷時會擲回此例外狀況)。 +遠端方法只會擲回這個例外狀況。
      • +
      • 物件會跨處理程序計算參照。
      • +
      • 繫結和解除繫結通常應成對使用,以符合用戶端的開始和結束週期。 +例如: +
          +
        • 如果您只要在 Activity 可見時與服務互動,則要在 {@link android.app.Activity#onStart onStart()} 時繫結,並於 {@link +android.app.Activity#onStop onStop()} 時解除繫結。 +
        • +
        • 如果您希望 Activity 即使在背景中停止時,仍然會接收回應,則可以在 {@link android.app.Activity#onCreate onCreate()} 時繫結,並於{@link android.app.Activity#onDestroy onDestroy()} 時解除繫結。 + +請注意,這表示您的 Activity 在整個執行期間 (即使是在背景執行也一樣) 都需要使用服務,因此,如果服務位於另一個處理程序,您要增加該處理程序的權重。但系統很可能因而將它終止。 + + +
        • +
        +

        注意:Activity 的 {@link android.app.Activity#onResume onResume()} 和 {@link +android.app.Activity#onPause onPause()} 期間,通常不要繫結和解除繫結,因為這些回呼會在每個週期轉換時發生,而且您應該要讓這些轉換期間所發生的處理動作保持在最少狀態。 + +另外,如果您的應用程式中有多個 Activity 繫結至同一個服務,而這兩個 Activity 之間會進行轉換,則服務會在目前的 Activity 解除繫結(暫停) 時,而下一個 Activity 繫結之前 (繼續時),先終結後再重新建立 + + +(此 Activity 轉換如何在 Activity 之間協調其週期的資訊,於 Activity 文件中說明)。 + +

        +
      + +

      如要取得如何繫結至服務的更多範例程式碼,請參閱 ApiDemos 中的 {@code +RemoteService.java} 類別。

      + + + + + +

      管理繫結服務的週期

      + +

      服務與所有用戶端解除繫結時,Android 系統會將服務終結 (除非服務是和 {@link android.app.Service#onStartCommand onStartCommand()} 一起啟動的)。 +如果您的服務純粹是繫結服務,就不用管理它的週期— Android 系統會根據服務是否繫結至任何用戶端,為您管理服務。 + +

      + +

      不過,如果您選擇實作 {@link android.app.Service#onStartCommand +onStartCommand()} 回呼方法,則必須明確停止服務,因為服務現在會視為「已啟動」。 +如果是此情形,除非服務本身使用 {@link android.app.Service#stopSelf()} 自行停止,或另一個元件呼叫 {@link +android.content.Context#stopService stopService()} 加以停止,否則服務會持續執行,不論它是否繫結至任何用戶端。 + +

      + +

      此外,如果您的服務已啟動並且接受繫結,當系統呼叫您的 {@link android.app.Service#onUnbind onUnbind()} 方法時,可以選擇傳回 +{@code true} (如果您希望用戶端下次繫結至服務時,可以接收 {@link android.app.Service#onRebind +onRebind()} 呼叫,而不是接收 {@link +android.app.Service#onBind onBind()} 的呼叫)。{@link android.app.Service#onRebind +onRebind()} 會傳回空值,但用戶端仍然會在其 +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} 回呼中接收到 {@link android.os.IBinder}。以下「圖 1」說明這類週期的邏輯。 + +

      + + + +

      圖 1.服務的週期開始後,就允許繫結行為。 +

      + + +

      如需關於已啟動服務週期的詳細資訊,請參閱服務文件。

      + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/fragments.jd b/docs/html-intl/intl/zh-tw/guide/components/fragments.jd new file mode 100644 index 0000000000000000000000000000000000000000..e54769b0c88b39e1d6159cf7eac77ce7d32415c7 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=片段 +parent.title=Activity +parent.link=activities.html +@jd:body + +
      +
      +

      本文件內容

      +
        +
      1. 設計概念
      2. +
      3. 建立片段 +
          +
        1. 新增使用者介面
        2. +
        3. 將片段新增到 Activity 中
        4. +
        +
      4. +
      5. 管理片段
      6. +
      7. 進行片段交易
      8. +
      9. 與 Activity 通訊 +
          +
        1. 為 Activity 建立事件回呼
        2. +
        3. 將項目新增到動作列中
        4. +
        +
      10. +
      11. 處理片段生命週期 +
          +
        1. 調整 Activity 生命週期
        2. +
        +
      12. +
      13. 範例說明
      14. +
      + +

      重要類別

      +
        +
      1. {@link android.app.Fragment}
      2. +
      3. {@link android.app.FragmentManager}
      4. +
      5. {@link android.app.FragmentTransaction}
      6. +
      + +

      另請參閱

      +
        +
      1. 使用片段建置動態 UI
      2. +
      3. 支援平板電腦和手機 +
      4. +
      +
      +
      + +

      {@link android.app.Fragment} 代表一種行為或 +{@link android.app.Activity} 中的一部分使用者介面。您可以合併單一 Activity 中的多個片段,藉此建置 +多窗格 UI 以及在多個 Activity 中重複使用片段。您可以將片段想成是 Activity 的模組化區段,片段擁有自己的生命週期、接收自己的輸入事件,而且您可以在 Activity 執行時新增或移除片段 (有點像是您可以在不同 Activity 中重複使用的「子 Activity」)。 + + +

      + +

      片段必須一律嵌入 Activity 中,而主要 Activity 的生命週期會直接影響片段的生命週期。 +例如,當 Activity 暫停時,其中的所有片段也會一併暫停;而當 Activity 遭到刪除時,所有片段也會一併刪除。 +不過,當 Activity 執行時 (該 Activity 會處於繼續進行生命週期狀態),您可以個別操縱所有片段,例如新增或移除片段。 + +當您進行片段交易這類操作時,您也可以將片段加到 Activity 所管理的返回堆疊中 — Activity 中的所有返回堆疊項目均為所發生片段交易的記錄。 + + +返回堆疊可讓使用者復原片段交易 (往回瀏覽),只要按下 [返回] 按鈕即可。 +

      + +

      當您將片段新增為 Activity 版面配置的一部分後,片段就會位於 Activity 檢視階層中的 {@link +android.view.ViewGroup},而且片段會自行定義專屬的版面配置。您可以宣告 Activity 版面配置檔案中的片段,或是在應用程式的程式碼中將片段加到現有的 {@link android.view.ViewGroup} 中,藉此在 Activity 版面配置中將片段插入為 {@code <fragment>} 元素。 + + + +不過,片段未必要成為 Activity 版面配置的一部分;您也可以選擇不透過其 UI,以隱形工作人員的身分使用 Activity 的片段。 + +

      + +

      本文說明如何建置應用程式以使用片段,包括片段如何在加到 Activity 返回堆疊時保持自身狀態、如何與 Activity 和 Activity 中的其他片段共用活動、如何製作 Activity 欄等等。 + + +

      + + +

      設計概念

      + +

      我們在 Android 3.0 (API 級別 11) 中導入了片段,主要目的是為了在大型螢幕 (例如平板電腦) 上支援更多動態和彈性 UI 設計。 +由於平板電腦的螢幕比手機大上許多,因此有更多空間可結合及交換 UI 元件。 + +片段可實現這種介面設計,而不必讓您管理複雜的檢視階層變更。 +將 Activity 的版面配置劃分成片段後,您就可以修改 Activity 在執行階段的外觀,以及保留 Activity 所管理返回堆疊的相關變更。 + +

      + +

      例如,某個新聞應用程式可使用單一片段在畫面左側顯示文章清單,並且使用另一個片段在畫面右側顯示某篇文章 — 這兩個片段是以並排方式出現在某個 Activity 中,而每個片段都有自己的一組生命週期回呼方法,可自行處理其使用者輸入事件。 + + +因此,使用者可以在相同 Activity 中選取並閱讀某篇文章 (如圖 1 中的平板電腦版面配置所示),而不必使用不同 Activity 選取及閱讀文章。 + +

      + +

      請務必將每個片段設計成模組化和可重複使用的 Activity 元件。這是因為每個片段會根據其生命週期回呼,定義專屬版面配置和行為,而您可將單一片段加到多個 Activity 中,故請將其設計成可重複使用的元件,同時避免直接操縱個別片段。 + + +由於模組化片段可讓您針對不同螢幕大小變更片段組合,因此請務必這麼做。 +設計您的應用程式以支援平板電腦和手機時,您可以在不同版面配置設定中重複使用片段,藉此根據可用的螢幕空間提供最佳的使用者體驗。 + +以手機為例說明,如果相同 Activity 中有多個片段不相符,則只要分割片段即可提供單一面板式的 UI。 + +

      + + +

      圖 1.片段所定義的兩個 UI 模組如何針對平板電腦設計合併成單一 Activity、如何針對手機設計分割成個別 Activity。 + +

      + +

      例如 — 延續新聞應用程式範例加以說明 — 在平板電腦大小的裝置上執行的應用程式可將兩個片段嵌入「Activity A」。 +不過,在手機大小的螢幕上,由於螢幕空間不足以容納兩個片段,因此「Activity A」只會包含文章清單的片段,而當使用者選取文章後,系統就會啟動內含第二個片段的「Activity B」,讓使用者閱讀文章。 + + +因此,應用程式可透過重複使用不同片段組合的方式,同時支援平板電腦和手機 (如圖 1 所示)。 + +

      + +

      如要進一步瞭解如何使用不同片段組合針對各種螢幕設定設計應用程式,請參閱支援平板電腦和手機指南。 +

      + + + +

      建立片段

      + +
      + +

      圖 2.片段的生命週期 (當其中的 Activity 處於執行狀態時)。 +

      +
      + +

      如要建立片段,您必須建立 {@link android.app.Fragment} 的子類別 (或是其現有的子類別)。 +{@link android.app.Fragment} 類別內含與{@link android.app.Activity} 十分雷同的程式碼。 +該程式碼包括與 Activity 類似的回呼方法,例如 {@link android.app.Fragment#onCreate onCreate()}、{@link android.app.Fragment#onStart onStart()}、 +{@link android.app.Fragment#onPause onPause()} 和 {@link android.app.Fragment#onStop onStop()}。 +事實上,如果您是設定現有 Android 應用程式改用片段,只要將 Activity 的回呼方法中的程式碼移到片段的個別回呼方法即可。 + + +

      + +

      一般來說,您至少必須實作下列生命週期方法:

      + +
      +
      {@link android.app.Fragment#onCreate onCreate()}
      +
      系統會在建立片段時呼叫這個方法。在實作這個方法時,您必須初始化您想保留的必要片段元件,以便恢復已暫停或停止的片段。 + +
      +
      {@link android.app.Fragment#onCreateView onCreateView()}
      +
      系統會在片段初次顯示其使用者介面時呼叫這個方法。 +您必須透過這個方法傳回 {@link android.view.View} (片段版面配置的根目錄),才能顯示片段的 UI。 +如果片段並未提供 UI 的話,則可以傳回空值。 +
      +
      {@link android.app.Activity#onPause onPause()}
      +
      系統會在使用者初次離開片段時呼叫這個方法 (即使使用者這麼做未必會刪除片段)。 +您通常需要透過這個方法提交要在目前的使用者工作階段以外保留的任何變更 (原因在於使用者可能不會返回)。 + +
      +
      + +

      大多數應用程式都至少必須針對每個片段實作這三個方法,不過您也必須使用幾個其他回呼方法來控制片段生命週期的各種狀態。 + +如要進一步瞭解所有回呼方法,請參閱處理片段生命週期。 +

      + + +

      以下列出幾個您可能會想擴充的子類別 (基礎 {@link +android.app.Fragment} 類別除外):

      + +
      +
      {@link android.app.DialogFragment}
      +
      顯示浮動對話方塊。使用這個類別建立對話方塊是使用 {@link android.app.Activity} 類別中的對話方塊協助程式方法的推薦替代方法,這是因為使用此類別可將片段對話方塊納入 Activity 所管理的片段堆疊,讓使用者得已返回已關閉的片段。 + + +
      + +
      {@link android.app.ListFragment}
      +
      顯示配接器 (例如 {@link +android.widget.SimpleCursorAdapter}) 所管理的項目清單;與 {@link android.app.ListActivity} 方法相似。這個方法可提供數種管理清單檢視畫面的方法,例如可處理點擊事件的 {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()}回呼。 + +
      + +
      {@link android.preference.PreferenceFragment}
      +
      列出 {@link android.preference.Preference} 物件的階層;與 +{@link android.preference.PreferenceActivity} 方法相似。為應用程式建立「設定」Activity 時,這個方法就非常實用。 +
      +
      + + +

      新增使用者介面

      + +

      片段通常是當作某 Activity 的使用者介面使用,而且可將自身的版面配置提供給 Activity。 +

      + +

      如要提供片段的版面配置,您必須實作 {@link +android.app.Fragment#onCreateView onCreateView()} 回呼方法,讓 Android 系統呼叫片段顯示其版面配置。 +實作這個方法時,您必須傳回 +{@link android.view.View} (片段版面配置的根目錄)。

      + +

      注意:如果您的片段是 {@link +android.app.ListFragment} 的子類別,則實作完畢後系統預設會傳回 {@link android.app.Fragment#onCreateView onCreateView()} 的 +{@link android.widget.ListView},因此您不必加以實作。

      + +

      如要從 {@link +android.app.Fragment#onCreateView onCreateView()} 傳回版面配置,您可以從 XML 中定義的l版面配置資源擴大它。為協助您完成這項作業,{@link android.app.Fragment#onCreateView onCreateView()} 提供了 +{@link android.view.LayoutInflater} 物件。 +

      + +

      例如,以下是 {@link android.app.Fragment} 的子類別,可從 +{@code example_fragment.xml} 檔案載入版面配置:

      + +
      +public static class ExampleFragment extends Fragment {
      +    @Override
      +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
      +                             Bundle savedInstanceState) {
      +        // Inflate the layout for this fragment
      +        return inflater.inflate(R.layout.example_fragment, container, false);
      +    }
      +}
      +
      + + + +

      傳入 {@link android.app.Fragment#onCreateView +onCreateView()} 的 {@code container} 參數是上層 {@link android.view.ViewGroup} (來自 Activity 的版面配置),系統會將您的片段版面配置插入其中。 + +{@code savedInstanceState} 參數是 {@link android.os.Bundle},當片段即將恢復時 (如要進一步瞭解還原狀態,請參閱處理片段生命週期),這個參數就會提供先前的片段執行個體的相關資料。 + + +

      + +

      {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} 方法採用三種引數: +

      +
        +
      • 您想要擴大的版面配置的資源 ID。
      • +
      • 要設為擴大過後版面配置的上層檢視的 {@link android.view.ViewGroup}。請務必傳遞 {@code +container},以便讓系統將版面配置參數套用至擴大過後版面配置的根檢視 (由將做為其目標的父檢視所指定)。 +
      • +
      • 用於指示系統是否要在擴大期間將擴大過後的版面配置附加到 {@link +android.view.ViewGroup} (第二個參數) 的布林值 (由於系統已將擴大過後的版面配置插入 {@code +container},因此布林值應為 false — 如果您傳送 true,會導致系統在最終版面配置中建立多餘的檢視群組)。 +
      • +
      + +

      您現在已瞭解如何建立可提供版面配置的片段了。接著,請將建立好的片段新增至 Activity。 +

      + + + +

      將片段新增到 Activity 中

      + +

      片段通常會將一部分 UI 嵌入主要 Activity 的整體檢視階層中。 +您有兩種方式可將片段新增到 Activity 版面配置: +

      + +
        +
      • 宣告 Activity 版面配置檔案內含的片段。 +

        選用這種方式時,您可以將片段視為檢視,為其指定版面配置屬性。 +例如,以下是內含兩個片段的 Activity 版面配置檔案: +

        +
        +<?xml version="1.0" encoding="utf-8"?>
        +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        +    android:orientation="horizontal"
        +    android:layout_width="match_parent"
        +    android:layout_height="match_parent">
        +    <fragment android:name="com.example.news.ArticleListFragment"
        +            android:id="@+id/list"
        +            android:layout_weight="1"
        +            android:layout_width="0dp"
        +            android:layout_height="match_parent" />
        +    <fragment android:name="com.example.news.ArticleReaderFragment"
        +            android:id="@+id/viewer"
        +            android:layout_weight="2"
        +            android:layout_width="0dp"
        +            android:layout_height="match_parent" />
        +</LinearLayout>
        +
        +

        {@code <fragment>} 中的 {@code android:name} 屬性可指定系統呼叫版面配置中的 {@link +android.app.Fragment} 類別。

        + +

        系統建立這個 Activity 版面配置後,就會呼叫版面配置中指定的任何片段,並為每個片段呼叫 {@link android.app.Fragment#onCreateView onCreateView()} 方法,藉此擷取所有片段的版面配置。 + +系統會插入片段所傳回的 {@link android.view.View} 來取代 {@code <fragment>} 元素。 +

        + +
        +

        注意:您必須為每個片段提供專屬識別碼,以便系統在 Activity 重新開始時復原片段 (您也可以使用此識別碼擷取要交易的片段,例如移除片段)。 + +您有三種方式可提供片段的 ID: +

        +
          +
        • 提供內含專屬 ID 的 {@code android:id} 屬性。
        • +
        • 提供內含不重複字串的 {@code android:tag} 屬性。
        • +
        • 如果您未提供上述兩項屬性,系統會採用容器檢視的 ID。 +
        • +
        +
        +
      • + +
      • 或者,利用程式將片段新增至現有的 {@link android.view.ViewGroup}。 +

        只要 Activity 處於執行狀態,您都可以將片段新增至 Activity 版面配置。方法很簡單,只要指定您想在其中加入片段的 {@link +android.view.ViewGroup} 即可。 +

        +

        如要在 Activity 中進行片段交易 (例如新增、移除或替換片段),請使用 {@link android.app.FragmentTransaction} 中的 API 進行。 +您可以透過以下方式取得 {@link android.app.Activity} 的 {@link android.app.FragmentTransaction} 執行個體: +

        + +
        +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}
        +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
        +
        + +

        接著,您就可以使用 {@link +android.app.FragmentTransaction#add(int,Fragment) add()} 方法指定要新增的片段,以及要插入片段的目標檢視。 +例如:

        + +
        +ExampleFragment fragment = new ExampleFragment();
        +fragmentTransaction.add(R.id.fragment_container, fragment);
        +fragmentTransaction.commit();
        +
        + +

        第一個傳入 {@link android.app.FragmentTransaction#add(int,Fragment) add()} 的引數是要在其中插入片段的 {@link android.view.ViewGroup} (使用資源 ID 加以指定),而第二個參數則是要新增的引數。 + +

        +

        透過 +{@link android.app.FragmentTransaction} 完成變更後,請呼叫 {@link android.app.FragmentTransaction#commit} 以便讓變更生效。 +

        +
      • +
      + + +

      新增不顯示 UI 的片段

      + +

      上述範例說明如何將片段新增至 Activity,以提供 UI。但事實上,您也可以使用片段為 Activity 提供背景行為,避免顯示額外的 UI。 + +

      + +

      如要新增不顯示使 UI 的片段,請使用 {@link +android.app.FragmentTransaction#add(Fragment,String)} 從 Activity 新增片段 (請提供片段的不重複字串「標記」,而不是檢視 ID)。 +這樣即可新增片段,但由於該片段並未與 Activity 版面配置中的檢視相關聯,因此不會接收 {@link +android.app.Fragment#onCreateView onCreateView()} 的呼叫。 +如此一來,您就不必實作該方法。

      + +

      提供片段的字串標記並不是採用非 UI 片段時的必要步驟 — 您也可以提供沒有 UI 的片段的字串標記 — 不過,如果片段沒有 UI,則字串標記將成為識別片段的唯一途徑。 + +如果您想之後再從 Activity 中取得片段,請使用 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}。 +

      + +

      如需使用沒有 UI 的片段做為背景工作者的 Activity 範例,請參閱 SDK 範例中位於以下路徑的 {@code +FragmentRetainInstance.java} 範例 (可透過 Android SDK Manager 存取)<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java。 + +

      + + + +

      管理片段

      + +

      如要管理 Activity 中的片段,請使用 {@link android.app.FragmentManager}。如要取得這些片段,請呼叫 Activity 中的 {@link android.app.Activity#getFragmentManager()}。 +

      + +

      您可透過 {@link android.app.FragmentManager} 執行下列操作:

      + +
        +
      • 使用 {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (針對在 Activity 版面配置中提供 UI 的片段) 或 {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (針對未提供 UI 的片段) 取得 Activity 中的現有片段。 +
      • +
      • 使用 {@link +android.app.FragmentManager#popBackStack()} (模擬使用者的「返回」命令) 將片段從返回堆疊中推出。
      • +
      • 使用 {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()} 針對返回堆疊的變更項目註冊監聽器。
      • +
      + +

      如要進一步瞭解上述方法以及其他方法,請參閱 {@link +android.app.FragmentManager} 類別說明文件。

      + +

      如上一節所述,您也可以使用 {@link android.app.FragmentManager} 開啟 {@link android.app.FragmentTransaction},以便進行片段交易 (例如新增及移除片段)。 + +

      + + +

      進行片段交易

      + +

      使用 Activity 中片段的一項實用功能,就是新增、移除、替換片段以及對它們執行其他動作,藉此反映使用者互動。 +您針對 Activity 提交的每組變更稱為交易,而您可以使用 {@link +android.app.FragmentTransaction} 中的進行這種交易。 +此外,您也可以儲存對 Activity 所管理的返回堆疊進行的交易,讓使用者能夠往回瀏覽片段變更 (如同往回瀏覽 Activity)。 + +

      + +

      您可以從 {@link +android.app.FragmentManager} 中取得如下所示的 {@link android.app.FragmentTransaction}:

      + +
      +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()};
      +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
      +
      + +

      每次交易都是您想同時進行的一組變更。您可以使用 {@link +android.app.FragmentTransaction#add add()}、{@link android.app.FragmentTransaction#remove remove()}和 {@link android.app.FragmentTransaction#replace replace()} 等方法設定您想針對特定交易進行的變更。 + +接著,只要呼叫 {@link android.app.FragmentTransaction#commit()},就能將該交易套用至 Activity。 +

      + + +

      不過,您可能會為了新增交易至片段交易返回堆疊,先呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},然後再呼叫 {@link +android.app.FragmentTransaction#commit()}。 +返回堆疊是由 Activity 所管理,可讓使用者透過按下 [返回] 按鈕的方式,返回先前的片段狀態。 +

      + +

      以下範例可讓您替換片段,並且保留先前的返回堆疊狀態: +

      + +
      +// Create new fragment and transaction
      +Fragment newFragment = new ExampleFragment();
      +FragmentTransaction transaction = getFragmentManager().beginTransaction();
      +
      +// Replace whatever is in the fragment_container view with this fragment,
      +// and add the transaction to the back stack
      +transaction.replace(R.id.fragment_container, newFragment);
      +transaction.addToBackStack(null);
      +
      +// Commit the transaction
      +transaction.commit();
      +
      + +

      在這個範例中,{@code newFragment} 會針對依據 {@code R.id.fragment_container} ID 識別的版面配置容器, +替換其中的任何現有片段 (如果有的話)。系統會呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},將替換交易儲存到返回堆疊,以便使用者按下 [返回] 按鈕來復原交易以及返回上一個片段。 + +

      + +

      如果您將多項變更新增至交易 (例如新增另一個 {@link +android.app.FragmentTransaction#add add()} 或 {@link android.app.FragmentTransaction#remove +remove()}),並且呼叫 {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()},那麼您在呼叫 {@link android.app.FragmentTransaction#commit commit()} 之前套用的所有變更都會新增至返回堆疊做為單次交易,在這種情況下,按下 [返回] 按鈕就能一次復原所有變更。 + +

      + +

      您將變更新增至 {@link android.app.FragmentTransaction} 的順序並不會造成任何影響,但請注意以下例外狀況: +

      +
        +
      • 務必最後才呼叫 {@link android.app.FragmentTransaction#commit()}
      • +
      • 如果您是將多個片段新增至同一容器,那麼您新增片段的順序將決定這些片段在檢視階層中的顯示順序 +
      • +
      + +

      如果您並未在進行移除片段的交易時呼叫 {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()},該片段會在您提交交易後遭到刪除,而且使用者無法往回瀏覽至該片段。 +不過,如果您在移除片段時呼叫 {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()},則該片段將會遭到「停止」,而且會在使用者往回瀏覽時繼續進行。 + + +

      + +

      提示:您可以在進行每次片段交易時套用交易動畫,方法是在提交交易前呼叫 {@link android.app.FragmentTransaction#setTransition setTransition()}。 + +

      + +

      呼叫 {@link android.app.FragmentTransaction#commit()} 並不會立即進行交易, +而是會讓系統排定 UI 執行緒 (「主要」執行緒) 可執行這個方法時,立即加以執行。 +不過,您可以視需要透過 UI 執行緒呼叫 {@link +android.app.FragmentManager#executePendingTransactions()},立即執行 {@link android.app.FragmentTransaction#commit()} 所提交的交易。 +您通常不必這樣做,除非該交易是其他執行緒的工作的必要元件。 +

      + +

      注意:您可以使用 {@link +android.app.FragmentTransaction#commit commit()} 來提交交易,但僅限於 Activity 儲存其狀態之前 (也就是使用者離開 Activity 之前)。 +如果您在這個時間點之後嘗試提交交易,就會發生例外狀況, +這是因為狀態會在交易提交後遺失 (如果需要復原 Activity 的話)。 +如果想確保狀態遺失不會造成任何影響,請使用 {@link +android.app.FragmentTransaction#commitAllowingStateLoss()} 提交交易。

      + + + + +

      與 Activity 通訊

      + +

      雖然 {@link android.app.Fragment} 是實作成不同於 +{@link android.app.Activity} 的物件,而且可在多個 Activity 中使用,特定片段執行個體仍會直接與含有該物件的 Activity 建立關聯。 +

      + +

      因此,片段可存取內含{@link +android.app.Fragment#getActivity()} 的 {@link android.app.Activity} 執行個體,以及輕鬆進行在 Activity 版面配置中尋找檢視等工作: +

      + +
      +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
      +
      + +

      相同地,您的 Activity 可利用 {@link +android.app.FragmentManager#findFragmentById findFragmentById()} 或 {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()} 從 {@link android.app.FragmentManager} 中取得 {@link android.app.Fragment} 參照資料,以呼叫片段中的方法。 +例如:

      + +
      +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
      +
      + + +

      為 Activity 建立事件回呼

      + +

      在某些情況下,您可能需要可用來與 Activity 分享事件的片段。如果您需要這種片段,建議您在片段內定義回呼介面,然後要求主要 Activity 實作該片段。 + +當 Activity 透過介面接收回呼後,即可視需要與版面配置中的其他片段分享這項資訊。 +

      + +

      例如,假設新聞應用程式的 Activity 中有兩個片段 — 一個用於顯示文章清單 (片段 A),另一個用於顯示文章 (片段 B) — 其中的片段 A 必須告知 Activity 使用者選取清單項目的時間點,以便通知片段 B 顯示文章。 + +在這個範例中,{@code OnArticleSelectedListener} 介面是在片段 A 中完成宣告: +

      + +
      +public static class FragmentA extends ListFragment {
      +    ...
      +    // Container Activity must implement this interface
      +    public interface OnArticleSelectedListener {
      +        public void onArticleSelected(Uri articleUri);
      +    }
      +    ...
      +}
      +
      + +

      接著,代管片段的 Activity 會實作 {@code OnArticleSelectedListener} 介面,並且覆寫 {@code onArticleSelected()} 以便將片段 A 的事件告知片段 B。為了確保主要 Activity 可實作該介面,片段 A 的 {@link +android.app.Fragment#onAttach onAttach()} 回呼方法 (系統將片段新增至 Activity 時會呼叫這種方法) 轉換傳入 {@link android.app.Fragment#onAttach +onAttach()} 的 {@link android.app.Activity},藉此呼叫 {@code OnArticleSelectedListener} 執行個體: + + + + +

      + +
      +public static class FragmentA extends ListFragment {
      +    OnArticleSelectedListener mListener;
      +    ...
      +    @Override
      +    public void onAttach(Activity activity) {
      +        super.onAttach(activity);
      +        try {
      +            mListener = (OnArticleSelectedListener) activity;
      +        } catch (ClassCastException e) {
      +            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
      +        }
      +    }
      +    ...
      +}
      +
      + +

      如果 Activity 並未實作介面,那麼片段會擲回 +{@link java.lang.ClassCastException}。成功擲回時,{@code mListener} 成員會保留 Activity 所實作 +{@code OnArticleSelectedListener} 的參照資料,以便片段 A 呼叫 {@code OnArticleSelectedListener} 介面定義的方法與 Activity 分享事件。 + +例如,假設片段 A 是 +{@link android.app.ListFragment} 的延伸,則每當使用者點擊清單項目時,系統就會呼叫片段中的 {@link android.app.ListFragment#onListItemClick +onListItemClick()},讓該方法呼叫 {@code onArticleSelected()} 以便與 Activity 分享事件: + +

      + +
      +public static class FragmentA extends ListFragment {
      +    OnArticleSelectedListener mListener;
      +    ...
      +    @Override
      +    public void onListItemClick(ListView l, View v, int position, long id) {
      +        // Append the clicked item's row ID with the content provider Uri
      +        Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id);
      +        // Send the event and Uri to the host activity
      +        mListener.onArticleSelected(noteUri);
      +    }
      +    ...
      +}
      +
      + +

      傳入 {@link +android.app.ListFragment#onListItemClick onListItemClick()} 的 {@code id} 參數是使用者所點擊項目的資料列 ID,可讓 Activity (或其他片段) 用來從應用程式的 {@link +android.content.ContentProvider} 擷取文章。 +

      + +

      如要進一步瞭解如何使用內容供應程式,請參閱內容供應程式。 +

      + + + +

      將項目新增到動作列中

      + +

      片段可實作 +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()} 來為 Activity 的選項選單 (以及動作列) 提供選單項目。不過,您必須在呼叫 {@link +android.app.Fragment#onCreate(Bundle) onCreate()} 時呼叫 {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()},告知系統該片段會新增項目到「選項選單」,這個方法才能接收呼叫 (否則該片段將無法接收 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} 的呼叫)。 + +

      + +

      您之後透過片段新增到「選項選單」的任何物件都會附加到現有的選單項目。 +該片段也會在使用者選取選單項目時接收 {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} 的回呼。 +

      + +

      此外,您也可以在片段版面配置中註冊檢視來提供內容選單,方法是呼叫 {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}。當使用者開啟內容選單時,片段就會接收 {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()} 的呼叫。 +而當使用者選取某個項目時,片段則會接收 {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()} 的呼叫。

      + +

      注意:雖然片段會在使用者選取項目時,針對所有新增的選單項目接收回呼,不過最先在使用者選取選單項目時接收個別回呼的是 Activity。 + +如果 Activity 在使用者選取項目時所實作的回呼無法處理所選項目,則系統會將該事件傳送到片段的回呼中。 +這種情況會發生在「選項選單」和內容選單。 +

      + +

      如要進一步瞭解選單,請參閱選單動作列開發人員指南。

      + + + + +

      處理片段生命週期

      + +
      + +

      圖 3.Activity 生命週期對片段生命週期造成的影響。 +

      +
      + +

      管理片段生命週期的方式與管理 Activity 生命週期十分雷同。與 Activity 相同,片段有以下三種狀態: +

      + +
      +
      已繼續
      +
      系統會在執行中的 Activity 內顯示片段。
      + +
      已暫停
      +
      前景中有其他具備焦點的 Activity,但系統仍會顯示含有這個片段的 Activity (前景 Activity 處於半透明狀態,或是未覆蓋整個螢幕)。 + +
      + +
      已停止
      +
      系統不會顯示片段。這可能是因為主要 Activity 已停止,或是加到返回堆疊的片段已從 Activity 中移除。 +已停止的片段仍處於有效狀態 (系統會保留所有狀態和成員資訊), +但使用者無法看到這類片段,而且當 Activity 遭到刪除時,這些片段也會一併刪除。 +
      +
      + +

      與 Activity 相同,您可以使用 {@link +android.os.Bundle} 保留片段的狀態,以便在 Activity 的處理程序遭到刪除後想重新建立 Activity 時,還原片段狀態。 +您可以在片段的 {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} 回呼期間儲存狀態,並且在 +{@link android.app.Fragment#onCreate onCreate()}、{@link +android.app.Fragment#onCreateView onCreateView()} 或 {@link +android.app.Fragment#onActivityCreated onActivityCreated()} 時還原狀態。如要進一步瞭解如何儲存狀態,請參閱 Activity。 + +

      + +

      Activity 與片段生命週期之間最明顯的差異是,生命週期儲存在個別返回堆疊的方式。 +在預設情況下,Activity 停止後會插入系統所管理的 Activity 堆疊,方便使用者按下 [返回] 按鈕來返回該 Activity (如工作和返回堆疊所述)。不過,片段只會在您進行移除片段的交易期間,呼叫 {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} 來要求系統儲存執行個體時,插入主要 Activity 所管理的返回堆疊。 + + + + +

      + +

      在其他情況下,管理片段生命週期的方式與管理 Activity 生命週期十分雷同。 +因此,管理 Activity 生命週期的做法同樣適用於片段。 +不過,建議您參考相關資源,瞭解 Activity 生命週期對片段生命週期造成的影響。 +

      + +

      注意:如果您需要 {@link android.app.Fragment} 的 +{@link android.content.Context} 物件,請呼叫 {@link android.app.Fragment#getActivity()}。不過,請務必只在確定片段是附加到 Activity 的情況下,再呼叫 {@link android.app.Fragment#getActivity()}。 + +如果片段未附加到 Activity,或是片段因超過生命週期而遭到卸除,則 {@link android.app.Fragment#getActivity()} 將傳回空值。 +

      + + +

      調整 Activity 生命週期

      + +

      內含片段的 Activity 的生命週期會直接影響片段的生命週期,這樣一來,Activity 的每次生命週期回呼會針對每個片段產生相似的回呼。 + +例如,當 Activity 收到 {@link android.app.Activity#onPause} 後,Activity 中的每個片段都會收到 {@link android.app.Fragment#onPause}。 +

      + +

      不過,片段有幾個額外的生命週期回呼,可用於處理與 Activity 之間的特殊互動,以執行建置或刪除片段 UI 等動作。以下是這些額外的回呼方法: + +

      + +
      +
      {@link android.app.Fragment#onAttach onAttach()}
      +
      當片段與 Activity 建立關聯時,系統就會呼叫這個方法 ({@link +android.app.Activity} 會傳入與片段相關聯的 Activity)。
      +
      {@link android.app.Fragment#onCreateView onCreateView()}
      +
      系統會呼叫這個方法來建立與片段相關聯的檢視階層。
      +
      {@link android.app.Fragment#onActivityCreated onActivityCreated()}
      +
      當 Activity 的 {@link android.app.Activity#onCreate +onCreate()} 方法成功傳回時,系統就會呼叫這個方法。
      +
      {@link android.app.Fragment#onDestroyView onDestroyView()}
      +
      當使用者移除與片段相關聯的檢視階層時,系統就會呼叫這個方法。
      +
      {@link android.app.Fragment#onDetach onDetach()}
      +
      當使用者將片段與 Activity 解除關聯時,系統就會呼叫這個方法。
      +
      + +

      如圖 3 所述,片段生命週期的流程會受到主要 Activity 的影響。 +您可以透過該圖片瞭解 Activity 的連續狀態如何決定片段要接收的回呼方法。 +例如,當 Activity 收到 {@link +android.app.Activity#onCreate onCreate()} 回呼後,Activity 中的片段就不會收到 +{@link android.app.Fragment#onActivityCreated onActivityCreated()} 以外的回呼。

      + +

      Activity 一旦進入已恢復狀態,您便可視需要在 Activity 中新增或移除片段。 +因此,只有處於已恢復狀態的 Activity 會影響片段的生命週期。 +

      + +

      不過,當 Activity 不再處於已恢復狀態後,Activity 就會再次推送片段的生命週期。 +

      + + + + +

      範例說明

      + +

      以下提供使用兩個片段建立兩個面板的版面配置的 Activity 範例,藉此綜合說明本文所述內容。 +下方 Activity 包含一個用於顯示莎士比亞劇作清單的片段,以及另一個用於在使用者選取清單項目時顯示劇作摘要的片段。 + +這個 Activity 範例同時示範了如何根據螢幕設定提供不同的片段設定。 +

      + +

      注意:如需這個 Activity 的完整原始碼,請查閱 +{@code +FragmentLayout.java}

      + +

      主要 Activity 會在 {@link +android.app.Activity#onCreate onCreate()} 時以常見的方式套用版面配置:

      + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +

      Activity 套用的版面配置為 {@code fragment_layout.xml}:

      + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +

      使用這個版面配置可讓系統在 Activity 載入版面配置時,呼叫 {@code TitlesFragment} (這個類別會列出劇本名稱),而 {@link android.widget.FrameLayout} (顯示劇本摘要的片段將納入的目標類別) 則會佔用螢幕右側的空間,但在一開始會保持空白狀態。 + + +如下方所示,系統只會在使用者從清單中選取項目後,才將片段插入 {@link android.widget.FrameLayout}。 +

      + +

      不過,並非所有螢幕設定都有足夠的空間同時並排顯示劇作清單以及劇作摘要。 +因此,上方版面配置只適用於橫向螢幕設定,系統會將它儲存在 {@code res/layout-land/fragment_layout.xml} 中。 +

      + +

      而在螢幕為直向的情況下,系統會套用儲存在 {@code res/layout/fragment_layout.xml} 中的以下版面配置: +

      + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +

      這個版面配置只包含 {@code TitlesFragment}。也就是說,如果裝置採用直向螢幕設定,系統只會顯示劇作名稱清單。 +因此,當使用者在採用這種設定的裝置上點擊清單項目後,應用程式就會啟動新 Activity 來顯示劇作摘要,而不是載入第二個片段。 + +

      + +

      接著,請看看片段類別如何達到以上目標。首先是用於顯示莎士比亞劇作名稱清單的 {@code +TitlesFragment}。這個片段會延伸 {@link +android.app.ListFragment},並且依據該類別控制大多數的清單檢視工作。

      + +

      如果您檢查這個程式碼,將會發現使用者點擊清單項目後會觸發兩種行為:視採用的版面配置而定,系統會建立並呈現新的片段,以便在同一 Activity 中顯示詳細資料 (將片段加到 {@link +android.widget.FrameLayout}),或是啟動新的 Activity (藉此顯示片段)。 + +

      + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +

      第二個片段 {@code DetailsFragment} 則會針對 +{@code TitlesFragment} 中,使用者所選清單項目的劇本摘要:

      + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +

      針對 {@code TitlesFragment} 類別發出的回呼,如果使用者點擊清單項目,而且目前的版面配置「並未」包含 {@code R.id.details} 檢視 ({@code DetailsFragment} 所屬的檢視),則應用程式會執行 {@code DetailsActivity} Activity 來顯示項目內容。 + + +

      + +

      以下是會在螢幕採用橫向版面設定時,嵌入 {@code DetailsFragment} 以顯示所選劇本摘要的 {@code DetailsActivity}: +

      + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +

      請注意,這個 Activity 會在螢幕採用橫向版面配置的情況下自行結束,因此主要 Activity 會接續顯示 {@code TitlesFragment} 旁的 {@code DetailsFragment}。如果使用者在採用直向版面配置的裝置上執行 {@code DetailsActivity},然後將該裝置旋轉成橫向 (這會重新執行目前的 Activity),就可能會發生這種情況。 + + +

      + + +

      如需更多使用片段的範例 (以及本範例的原始檔案),請參閱ApiDemos 所提供的 API Demos 範例應用程式 (可透過 SDK 元件範本下載)。 + +

      + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd b/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd new file mode 100644 index 0000000000000000000000000000000000000000..d3b3c2899614a70221f20bda44b06e3a52af2d26 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=應用程式基礎知識 +@jd:body + + + +

      Android 應用程式是以 Java 程式語言編寫而成。Android SDK 工具可將您的程式碼 — 連同任何相關資料和資源檔案 — 編入 APK ( + Android 套件,使用 {@code .apk} 後綴字詞的封存檔)。 +APK 檔案包含 Android 應用程式的所有內容,搭載 Android 作業系統的裝置會使用這種檔案來安裝應用程式。 +

      + +

      Android 應用程式安裝到裝置之後,便可在專屬的安全性沙箱中執行:

      + +
        +
      • Android 作業系統是一種支援多位使用者的 Linux 系統;在這種系統中,每款應用程式即代表不同的使用者。 +
      • + +
      • 在預設情況下,系統會為每款應用程式指派一個不重複 Linux 使用者 ID (只有系統可使用這個 ID,應用程式無法取得這項資訊)。 +系統會為應用程式中的所有檔案設定權限,因此只有該應用程式指派的使用者 ID 可存取這些檔案。 +
      • + +
      • 所有處理程序都有專屬的虛擬機器 (VM),供系統在獨立環境中執行應用程式的程式碼。 +
      • + +
      • 在預設情況下,每款應用程式會在專屬的 Linux 處理程序中執行。Android 會在需要執行應用程式的任何元件時啟動處理程序,並且在不必執行應用程式元件,或系統必須復原記憶體供其他應用程式使用時關閉處理程序。 + +
      • +
      + +

      如此一來,Android 系統就會實作「最低權限原則」。換句話說,在預設情況下,所有應用程式只能存取執行工作時所需的元件。 + +這樣一來,應用程式便無法存取系統的某些部分,藉此建立十分安全的執行環境。 +

      + +

      不過,應用程式可透過一些方式與其他應用程式分享資料,以及存取系統服務: +

      + +
        +
      • 兩款應用程式可共用相同的 Linux 使用者 ID,以便存取彼此的檔案。 +為了節省系統資源,共用相同使用者 ID 的應用程式可在相同 Linux 處理程序中執行,以及共用相同的 VM (前提是應用程式必須使用相同的憑證進行簽署)。 + +
      • +
      • 應用程式可要求存取裝置資料 (例如使用者的聯絡人資料、簡訊、掛載式儲存空間 (SD 卡)、相機、藍牙等)。 +使用者必須在安裝期間授予所有應用程式權限。 +
      • +
      + +

      本文提供有關 Android 應用程式如何在系統中運作的基本概念,其餘部分則說明以下幾點: +

      +
        +
      • 定義應用程式的核心架構元件。
      • +
      • 用於宣告應用程式所需元件和裝置功能的宣示說明檔案。 +
      • +
      • 應用程式的程式碼以外的資源;這些資源可讓您的應用程式針對各種裝置設定最佳化本身的行為。 +
      • +
      + + + +

      應用程式元件

      + +

      應用程式元件是 Android 應用程式的重要設計模組。每個元件 都是系統進入您應用程式的不同要點。並非所有元件都是使用者的實際進入點;某些元件的定義取決於其他元件,但所有元件都是獨立的個體,扮演著特定角色 — 換句話說,每個元件都是獨特的設計模組,可協助定義您應用程式的整體行為。 + + + +

      + +

      應用程式元件可分為 4 種不同類型。每種類型的用途和生命週期均不相同,可定義元件的建立及刪除方式。 +

      + +

      以下是應用程式元件的 4 種類型:

      + +
      + +
      Activity
      + +
      單一 Activity 代表顯示使用者介面的一個畫面。例如,電子郵件應用程式可包含一個用於顯示新郵件清單的 Activity、一個用於撰寫郵件的 Activity,以及一個用於閱讀郵件的 Activity。 + +雖然電子郵件應用程式的 Activity 會共同運作,以提供豐富的使用者體驗,不過每個 Activity 都是不同的個體。 + +因此,其他應用程式可執行其中任何一項 Activity (如果電子郵件應用程式允許的話)。 +例如,相機應用程式可在電子郵件應用程式中,執行用於撰寫新郵件的 Activity,以便讓使用者分享相片。 + + +

      Activity 是實作成 {@link android.app.Activity} 的子類別;詳情請參閱 Activity 開發人員指南。 + +

      +
      + + +
      服務
      + +
      單一 服務 是在背景執行的元件,用於進行長期作業或遠端處理程序工作。 +服務並不會提供使用者介面。 +例如,服務可在使用者位於其他應用程式時在背景撥放音樂,或是透過網路擷取資料,同時允許使用者與 Activity 進行互動。 + +其他元件 (例如 Activity) 可啟動並讓服務執行,或是繫結至 Activity 以便與其進行互動。 + + +

      服務是實作成 {@link android.app.Service} 的子類別;詳情請參閱服務開發人員指南。 + +

      +
      + + +
      內容供應程式
      + +
      單一 內容供應程式 可管理一組已分享的應用程式資料。您可以將資料儲存在檔案系統、SQLite 資料庫、網路上,或是您應用程式可存取的任何其他永久儲存空間。 + +其他應用程式可透過內容供應程式查詢或甚至修改資料 (如果內容供應程式允許這麼做的話)。 +例如,Android 系統會提供用於管理使用者聯絡資訊的內容供應程式。 +因此,任何具備適當權限的應用程式均可查詢內容供應程式的一部分 (例如 {@link +android.provider.ContactsContract.Data}),以便讀取及寫入有關特定使用者的相關資訊。 + + +

      此外,內容供應程式也可用於讀取及寫入只有您應用程式能存取的不公開資料。 +例如,Note Pad 範例應用程式可使用內容供應程式儲存記事。 +

      + +

      內容供應程式是實作成 {@link android.content.ContentProvider} 的子類別,而且必須實作一組標準 API 以便讓其他應用程式執行交易。 + +如需詳細資訊,請參閱內容供應程式開發人員指南。 +

      +
      + + +
      廣播接收器
      + +
      單一 廣播接收器 是一種元件,可回應整個系統的廣播通知。 +大多數廣播都是由系統所發出 — 例如,系統會發出廣播來通知使用者螢幕已關閉、電池電量不足,或相片已拍攝完成。此外,應用程式也可發出廣播 — 例如說發出廣播來通知其他應用程式特定資料已下載到裝置,可供它們使用。 + + +雖然廣播接收器無法顯示使用者介面,但它們可建立狀態列通知,告訴使用者發生了廣播事件。 + +具體而言,廣播接收器只是其他元件的「閘道」,用於執行極少量的工作。 +例如,廣播接收器可啟動服務依據事件執行特定工作。 + + +

      廣播接收器是實作為成 {@link android.content.BroadcastReceiver} 的子類別,而每個廣播都是由 {@link android.content.Intent} 物件所發出。 +如需詳細資訊,請參閱 {@link android.content.BroadcastReceiver} 類別。 +

      +
      + +
      + + + +

      Android 系統設計的一項特色是,任何應用程式都可啟動其他應用程式的元件。 +例如,假設您想讓使用者透過裝置相機拍攝相片,您的應用程式可利用其他具備相關功能的應用程式 (如果有的話) 以達到這個目標,這樣您就不必自行建立用於拍攝相片的 Activity。 + +您不需要納入或連結相機應用程式的程式碼,只要啟動相機應用程式中用於拍攝像片的 Activity 即可。 + + +啟動相關 Activity 後,系統就會將相片傳回您的應用程式供您使用。對於使用者而言,相機就宛如是您應用程式的一部分。 +

      + +

      當系統啟動某個元件後,就會啟動該應用程式的處理程序 (如果該應用程式目前並非處於執行中狀態) 並且呼叫該元件所需的類別。 +例如,假設您的應用程式啟動相機應用程式中用於拍攝相片的 Activity,則該 Activity 會在隸屬於相機應用程式的處理程序中執行,而不是您應用程式的處理程序。因此,與大多數其他系統的應用程式不同,Android 應用程式沒有單一進入點 (例如沒有 {@code main()} 函式)。 + + + +

      + +

      系統是在個別處理程序 (具備檔案權限,可限制其他應用程式存取) 中執行每款應用程式,因此您的應用程式無法直接啟動其他應用程式的元件,不過 Android 系統可以。 + +基於這個原因,如要啟動其他應用程式的元件,您必須向指定「意圖」的系統發送訊息,以啟動特定元件。 + +系統隨後會為您啟用所需的元件。

      + + +

      啟用元件

      + +

      4 種元件類型的其中 3 種 — Activity、服務和廣播接收器 — 是透過「意圖」這種非同步訊息啟用。意圖會在執行階段將元件與彼此繫結 (您可以意圖想成要求其他元件進行動作的傳令員),不論元件是否屬於您的應用程式。 + + + +

      + +

      意圖是使用 {@link android.content.Intent} 物件建立而成,該物件可定義訊息來啟用特定元件或特定「類型」的元件 — 意圖可以採取明確或隱含設定。 + +

      + +

      針對 Activity 和服務,意圖會定義要執行的動作 (例如「查看」或「傳送」某項目),並且可能會指定執行動作的目標資料 URI (以及通知要啟用的元件)。 + +例如,意圖可傳達某 Activity 的要求,顯示圖片或開啟網頁。 +在某些情況下,您可以啟動 Activity 來接收結果,此時該 Activity 也會傳回{@link android.content.Intent} 的結果 (例如,您可以發出意圖讓使用者挑選聯絡人資料,並將該資訊傳回給您 — 傳回意圖會包含指向所選聯絡人的 URI )。 + + + +

      + +

      針對廣播接收器,意圖只會定義要廣播的通知 (例如,用於通知裝置電量不足的廣播只會包含指出「電池電量不足」的已知動作字串)。 + +

      + +

      其他元件類型和內容供應程式並非由意圖所啟用,而是在受 {@link android.content.ContentResolver} 發出的要求所指定時由系統啟用。 +內容解析程式可處理內容供應程式的所有直接交易,因此與供應程式進行交易的元件不必呼叫 {@link +android.content.ContentResolver} 物件的方法。 + +這樣會在內容供應程式與要求資訊 (基於安全目的) 之間保留抽象層。 +

      + +

      用於啟用各種元件的方法有以下幾種:

      +
        +
      • 將 {@link android.content.Intent} 傳送到 {@link android.content.Context#startActivity +startActivity()} 或 {@link android.app.Activity#startActivityForResult startActivityForResult()} (如果您想讓 Activity 傳回結果的話) 即可啟動 Activity (或是指派新工作給 Activity)。 + +
      • +
      • 將 {@link android.content.Intent} 傳送到 {@link android.content.Context#startService +startService()} 即可啟動服務 (或是指派新指示給正在執行的服務)。 +或者,您也可以將 {@link android.content.Intent} 傳送到 +{@link android.content.Context#bindService bindService()} 來繫結至服務。
      • +
      • 將 {@link android.content.Intent} 傳送到 +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、{@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()} 或 {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()} 等方法即可啟用廣播。
      • +
      • 對 {@link android.content.ContentResolver} 呼叫{@link +android.content.ContentProvider#query query()} 即可查詢內容供應程式。
      • +
      + +

      如要進一步瞭解如何使用意圖,請參閱意圖和意圖篩選器。 +如要進一步瞭解如何啟用特定元件,請參閱下列說明文件: +Activity服務、{@link +android.content.BroadcastReceiver} 和內容供應程式

      + + +

      宣示說明檔案

      + +

      Android 系統必須先讀取應用程式的 {@code AndroidManifest.xml} 檔案 (「宣示說明」檔案) 確認應用程式元件確實存在,才能啟動該元件。 + +您的應用程式必須在這個檔案中宣告本身的所有元件,而該檔案必須位於應用程式專案目錄的根目錄。 +

      + +

      除了宣告應用程式的元件以外,宣示說明還可以進行許多工作,包括: +

      +
        +
      • 識別應用程式所需的任何使用者權限,例如網際網路存取權或使用者合約的讀取存取權。 +
      • +
      • 根據應用程式使用的 API,宣告應用程式所需的最低 API 級別。 +
      • +
      • 宣告應用程式所使用或所需的硬體和軟體,例如相機、藍牙服務或多點觸控螢幕。 +
      • +
      • 應用程式需要連結的 API 程式庫 (除了 Android 架構 API 以外),例如 Google 地圖程式庫。 + +
      • +
      • 還有其他工作
      • +
      + + +

      宣告元件

      + +

      宣告說明的主要工作是將應用程式的元件告知系統。例如,宣告說明檔案可用如下方式宣告 Activity: +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<manifest ... >
      +    <application android:icon="@drawable/app_icon.png" ... >
      +        <activity android:name="com.example.project.ExampleActivity"
      +                  android:label="@string/example_label" ... >
      +        </activity>
      +        ...
      +    </application>
      +</manifest>
      + +

      <application>元素中,{@code android:icon} 屬性會指向識別應用程式的圖示資源。 + +

      + +

      而在 <activity> 元素中, +{@code android:name} 屬性會指定 {@link +android.app.Activity} 子類別的完整類別名稱,{@code android:label} 屬性則會指定要當作 Activity 的使用者可見標籤使用的字串。 +

      + +

      您必須用以下方式宣告所有應用程式元件:

      + + +

      系統看不到您納入來源但未在宣示說明中宣告的 Activity、服務和內容供應程式,因此系統無法執行這些項目。 +不過,您可在宣示說明宣告廣播接收器,或是透過程式碼以動態方式建立廣播接收器 (將廣播接收器建立為 +{@link android.content.BroadcastReceiver} 物件),然後呼叫 {@link android.content.Context#registerReceiver registerReceiver()} +向系統註冊廣播接收器。 + +

      + +

      如要進一步瞭解如何為應用程式建立宣示說明檔案,請參閱 AndroidManifest.xml 檔案。 +

      + + + +

      宣告元件功能

      + +

      啟用元件所述,您可以使用 +{@link android.content.Intent} 來啟動 Activity、服務和廣播接收器。如要這麼做,請在意圖中明確指定目標元件 (使用元件類別名稱)。 +不過,意圖最大的功能在於「隱含意圖」的概念。 +隱含意圖可簡單描述要執行的動作類型 (或是執行動作的資料依據) 以及讓系統在裝置中找出可執行動作的元件,然後加以啟動。 + + +如果意圖指出有多個元件可執行動作,則使用者可選取要使用的元件。 +

      + +

      系統會比對接受到的意圖與裝置上其他應用程式的宣示說明檔案中提供的意圖篩選器,藉此識別可回應意圖的元件。 + +

      + +

      在應用程式的宣示說明中宣告 Activity 時,您可視需要納入宣告 Activity 功能的意圖篩選器,以便讓 Activity 可回應其他應用程式的意圖。 + +您可以為元件宣告意圖篩選器,方法是將 {@code +<intent-filter>} 元素新增為元件宣告元素的子元素。 +

      + +

      例如,假設您以用於撰寫新郵件的 Activity 建置電子郵件應用程式,您可以下列方式宣告意圖篩選器來回應「傳送」意圖 (藉此傳送新郵件): +

      +
      +<manifest ... >
      +    ...
      +    <application ... >
      +        <activity android:name="com.example.project.ComposeEmailActivity">
      +            <intent-filter>
      +                <action android:name="android.intent.action.SEND" />
      +                <data android:type="*/*" />
      +                <category android:name="android.intent.category.DEFAULT" />
      +            </intent-filter>
      +        </activity>
      +    </application>
      +</manifest>
      +
      + +

      接著,如果其他應用程式透過 {@link +android.content.Intent#ACTION_SEND} 動作建立了意圖並傳送到 {@link android.app.Activity#startActivity +startActivity()},系統就可能會啟動您的 Activity 讓使用者撰寫及傳送郵件。 +

      + +

      如要進一不瞭解如何建立意圖篩選器,請參閱意圖和意圖篩選器。 +

      + + + +

      宣告應用程式需求

      + +

      並非所有搭載 Android 作業系統的裝置都能提供完整功能。 +為了避免使用者在缺少應用程式所需功能的裝置上安裝您的應用程式,請務必在宣示說明檔案中宣告裝置和軟體需求,清楚定義您的應用程式支援的裝置類型。 + + +大多數宣告僅供使用者參考,系統無法讀取,但 Google Play 等外部服務可讀取這些宣示,以便在使用者透過自己的裝置搜尋應用程式時提供篩選功能。 + +

      + +

      例如,假設您的應用程式需要相機且採用 Android 2.1 (API 級別 7) 導入的 API,建議您用下列方式在宣示說明檔案中宣告這些需求: +

      + +
      +<manifest ... >
      +    <uses-feature android:name="android.hardware.camera.any"
      +                  android:required="true" />
      +    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
      +    ...
      +</manifest>
      +
      + +

      如此一來,「沒有」相機且搭載 Android 2.1「以下版本」的裝置就無法從 Google Play 安裝您的應用程式。 +

      + +

      不過,您也可以宣告您的應用程式會使用相機,但相機並非應用程式的「必要」配備。 +在這種情況下,應用程式的 {@code required}屬性必須設為 {@code "false"},而且應用程式必須在執行階段檢查裝置是否具備相機,並且視需要停用任何相機功能。 + +

      + +

      如要進一步瞭解如何管理應用程式與不同裝置的相容性,請參閱裝置相容性。 + +

      + + + +

      應用程式資源

      + +

      Android 應用程式是以程式碼等其他要素開發而成 — 例如圖片、音訊檔案以及與應用程式視覺效果相關的其他資源。例如,您必須使用 XML 檔案定義 Activity 使用者介面的動畫、選單、樣式、顏色以及版面配置。 + + +使用應用程式資源可協助更新應用程式的各種特性,而不必修改程式碼 — 或是提供多組替代資源 — 藉此針對各種裝置設定 (例如不同的語言和螢幕大小) 最佳化您的應用程式。 + + +

      + +

      針對您在 Android 專案中加入的所有資源,SDK 建置工具會定義一個整數 ID,讓您用於從應用程式的程式碼或 XML 中定義的其他資源參照特定資源。 + +例如,假設您的應用程式含有名為 {@code +logo.png} 的圖片檔案 (儲存在 {@code res/drawable/} 目錄中),SDK 工具會產生名為 {@code R.drawable.logo} 的資源 ID,讓您用於參照圖片並將其插入使用者介面。 + +

      + +

      提供原始碼以外資源的一個重點是針對不同的裝置設定提供替代資源。 + +例如,您可以在 XML 中定義使用者介面字串,藉此將字串翻譯成其他語言,以及將這些字串儲存成個別檔案。 +接著,視您附加到資源目錄名稱的語言「修飾語」 (例如代表法文字串值的 {@code res/values-fr/}),以及使用者的語言設定而定,Android 系統會為您的 UI 套用適當的語言字串。 + + +

      + +

      Android 針對替代資源支援各種「修飾語」。修飾語是一個簡短字串;您可在資源目錄名稱中加入修飾語,藉此定義應使用這些資源的裝置設定。 + +例如,您通常需要為 Activity 建立多種版面配置 (視裝置螢幕的方向和大小而定)。 + +例如,假設裝置螢幕的方向為縱向 (直版),版面配置的按鈕就必須以垂直方向排列;假設裝置螢幕的方向為橫向 (寬版),則版面配置的按鈕就必須以水平方向排列。 + +如要根據螢幕方向變更版面配置,請建立兩種版面配置,然後為每個版面配置目錄名稱套用適當的修飾語。 + +如此系統就會根據目前的裝置方向,自動套用適當的版面配置。 +

      + +

      如要進一步瞭解您可在應用程式中加入的資源類型,以及如何針對不同的裝置設定建立替代資源,請詳閱提供資源。 +

      + + + +
      +
      +

      繼續閱讀有關下列主題的說明文章:

      +
      +
      意圖和意圖篩選器 +
      +
      說明如何使用 {@link android.content.Intent} API 來啟用應用程式元件 (例如 Activity 和服務),以及如何將應用程式元件提供給其他應用程式使用。 + +
      +
      Activity
      +
      說明如何建立 {@link android.app.Activity} 類別執行個體,以便讓應用程式的使用者介面提供不同內容。 +
      +
      提供資源
      +
      說明 Android 應用程式如何區別應用程式資源與程式碼,包括如何針對特定裝置設定供替代資源。 + + +
      +
      +
      +
      +

      您可能也會想瞭解下列主題:

      +
      +
      裝置相容性
      +
      說明 Android 如何在各種裝置上運作,以及如何針對各個裝置最佳化您的應用程式,或針對不同裝置限制應用程式提供的功能。 + +
      +
      系統權限
      +
      說明 Android 如何運用系統權限規定應用程式必須取得使用者同意才能使用特定 API。 +
      +
      +
      +
      + diff --git a/docs/html-intl/intl/zh-tw/guide/components/index.jd b/docs/html-intl/intl/zh-tw/guide/components/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..f34c7120e8ad39456e192bb91ca9274d7e605637 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=應用程式元件 +page.landing=true +page.landing.intro=Android 的應用程式架構可讓您使用一系列可重複使用的元件,建立內容豐富的新穎應用程式。本節說明如何建置元件來定義應用程式的設計模組,以及如何使用意圖連結這些元件。 +page.metaDescription=Android 的應用程式架構可讓您使用一系列可重複使用的元件,建立內容豐富的新穎應用程式。本節說明如何建置元件來定義應用程式的設計模組,以及如何使用意圖連結這些元件。 +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +
      + + + + + +
      diff --git a/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd b/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd new file mode 100644 index 0000000000000000000000000000000000000000..d3edac3f817abb1d9b7238254d5006402c53d84c --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=意圖和意圖篩選器 +page.tags="IntentFilter" +@jd:body + + + + + + +

      {@link android.content.Intent} 是可用來向另一個應用程式元件要求動作的傳訊物件。 + +雖然意圖有幾種方式可加速元件間的通訊,但共有三種基本使用案例: +

      + +
        +
      • 如何啟動 Activity: +

        {@link android.app.Activity} 代表應用程式中的單一畫面。您可以將 {@link android.content.Intent} 傳送至 {@link android.content.Context#startActivity startActivity()} 來啟動 +{@link android.app.Activity} 的新執行個體。 +{@link android.content.Intent} 可描述要啟動的 Activity 並攜帶任何必要資料。 +

        + +

        如果您想要在 Activity 完成時收到結果, +請呼叫 {@link android.app.Activity#startActivityForResult +startActivityForResult()}。Activity 的 {@link android.app.Activity#onActivityResult onActivityResult()}回呼中的個別 {@link android.content.Intent} 物件,就是 Activity 收到的結果。 + +如需詳細資訊,請參閱 Activity 指南。 +

      • + +
      • 如何啟動服務: +

        {@link android.app.Service} 是可以在背景中執行操作的元件,但沒有使用者介面。 +您可以將 {@link android.content.Intent} 傳送至 +{@link android.content.Context#startService startService()} 來啟動服務以執行一次性操作 (例如下載檔案)。 +{@link android.content.Intent} 可描述要啟動的服務並攜帶任何必要資料。 +

        + +

        如果服務是採用主從介面設計,您可以將 {@link android.content.Intent} 傳送至 {@link +android.content.Context#bindService bindService()} 來繫結至另一個元件的服務。 +如需詳細資訊,請參閱服務指南。

      • + +
      • 如何傳送廣播: +

        廣播是指任何應用程式都可接收的訊息。系統會傳送各種系統事件廣播,例如系統開機或裝置開始充電。 +您可以將 {@link android.content.Intent} 傳送至 {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}、 +{@link android.content.Context#sendOrderedBroadcast(Intent, String) 或{@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()},以向其他應用程式傳送廣播。 + + +

        +
      • +
      + + + + +

      意圖類型

      + +

      意圖類型分為兩種:

      + +
        +
      • 明確意圖:可依名稱 (完整類別名稱) 指定要啟動的元件。 +一般情況下,您會使用明確意圖啟動您應用程式中的元件,這是因為您知道 Activity 的類別名稱或您想要啟動的服務。 +例如,為回應使用者動作而啟動新的 Activity,或啟動服務以在背景下載檔案。 + +
      • + +
      • 隱含意圖:不會指定特定元件,而會宣告要執行的一般動作,讓另一個應用程式的元件來處理它。 +例如,如果您想要向使用者顯示地圖上的某個位置,可以使用隱含意圖,要求另一個支援應用程式在地圖上顯示指定的位置。 + +
      • +
      + +

      當您建立明確意圖以啟動 Activity 或服務時,系統會立即啟動 +{@link android.content.Intent} 物件中指定的應用程式元件。

      + +
      + +

      圖 1.說明如何透過系統傳送隱含意圖以啟動另一個 Activity:[1] Activity A 會建立含有動作描述的{@link android.content.Intent} 並傳送至 {@link +android.content.Context#startActivity startActivity()}。[2] Android 系統會搜尋所有應用程式,以找出符合該意圖的意圖篩選器。 + + +找到相符項目時,[3] 系統會呼叫其 {@link android.app.Activity#onCreate onCreate()} 方法,並將 {@link android.content.Intent} 傳送給它來啟動相符的 Activity (Activity B)。 + + +

      +
      + +

      當您建立隱含意圖時,Android 系統會比較意圖內容和裝置上其他應用程式的宣示說明檔案中宣告的「意圖篩選器」,以找出要啟動的適當元件。 + +如果意圖和意圖篩選器相符,系統會啟動該元件,並將 {@link android.content.Intent} 物件傳送給它。 +如果有多個意圖篩選器符合意圖,系統會顯示對話方塊,供使用者挑選要使用的應用程式。 +

      + +

      意圖篩選器是應用程式宣示說明檔案中的運算式,可指定元件要接收的意圖類型。 + +例如,藉由宣告 Activity 的意圖篩選器,可讓其他應用程式使用特定意圖類型直接啟動您的 Activity。 +同樣地,如果您「不」為 Activity 宣告任何意圖篩選器,就只能以明確意圖啟動它。 + +

      + +

      注意:為了確保您的應用程式安全,請一律使用明確意圖啟動 {@link android.app.Service},並且不要宣告服務的意圖篩選器。 + +使用隱含意圖啟動服務會危害安全性,原因在於您無法確定哪個服務會回應意圖,而且使用者無法得知系統會啟動哪項服務。 + +從 Android 5.0 (API 級別 21) 開始,如果您使用隱含意圖呼叫 {@link android.content.Context#bindService bindService()},系統都會擲回例外狀況。 + +

      + + + + + +

      建置意圖

      + +

      {@link android.content.Intent} 物件攜帶 Android 系統用來判斷要啟動哪個元件的資訊 (例如應接收意圖的確切元件名稱或元件類別),再加上接收者元件用以適當執行動作的資訊 (例如要執行的動作和據以執行的資料)。 + + +

      + + +

      {@link android.content.Intent} 包含的主要資訊如下:

      + +
      + +
      元件名稱
      +
      要啟動元件的名稱。 + +

      雖可選擇是否使用,但這卻是讓意圖「明確」的重要資訊,表示意圖只能傳送至元件名稱所定義的應用程式元件。 + +如果不使用元件名稱,意圖會是「隱含」的,因此系統會根據其他意圖資訊來決定哪個元件應接收意圖 (例如動作、資料及類別 — 如下所述)。 + +如果您需要啟動應用程式中的特定元件,就應該指定元件名稱。 +

      + +

      注意:啟動 {@link android.app.Service} 時,請務必指定元件名稱。 +否則,您無法確定哪個服務會回應意圖,而且使用者無法得知系統會啟動哪項服務。 +

      + +

      {@link android.content.Intent} 的這個欄位是 +{@link android.content.ComponentName} 物件,您可以使用目標元件的完整類別名稱加以指定,包括應用程式的封裝名稱。例如, + +{@code com.example.ExampleActivity}。您可以使用 {@link +android.content.Intent#setComponent setComponent()}、{@link android.content.Intent#setClass +setClass()}、{@link android.content.Intent#setClassName(String, String) setClassName()} 或 +{@link android.content.Intent} 建構函式來設定元件名稱。

      + +
      + +

      動作
      +
      以字串指定要執行的一般動作 (例如「檢視」或「挑選」)。 + +

      就廣播意圖而言,這是指已發生且系統回報的動作。動作大半決定其餘意圖的建構方式 — 特別是資料與額外資料中包含的項目。 + + + +

      您可以在應用程式內指定自己的動作以供意圖使用 (或由其他應用程式用來呼叫應用程式中的元件),但您應使用 {@link android.content.Intent} 類別或其他架構類別定義的動作常數。 + +以下是可啟動 Activity 的一些常見動作: +

      + +
      +
      {@link android.content.Intent#ACTION_VIEW}
      +
      當您有一些資訊可讓 Activity 向使用者顯示時,例如要在圖庫應用程式檢視的相片或要在地圖應用程式檢視的地址,就可以透過 {@link android.content.Context#startActivity startActivity()} 使用意圖中的這個動作。 + + +
      + +
      {@link android.content.Intent#ACTION_SEND}
      +
      也稱為「分享」意圖,當您有一些資料可供使用者透過其他應用程式分享時,例如電子郵件應用程式或社交分享應用程式,才應透過 {@link android.content.Context#startActivity startActivity()} 使用意圖中的這個動作。 + +
      +
      + +

      如要進一步瞭解定義一般動作的常數,請參閱 {@link android.content.Intent} 類別參考文件。 +其他動作則是在 Android 架構的其他位置完成定義,例如可在系統設定應用程式中開啟特定畫面的動作位在 {@link android.provider.Settings} 中。 + +

      + +

      您可以透過 {@link android.content.Intent#setAction +setAction()} 或 {@link android.content.Intent} 建構函式來指定意圖的動作。

      + +

      如果您定義自己的動作,請務必加入您應用程式的封裝名稱做為前置詞。 +例如:

      +
      static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";
      +
      + +
      資料
      +
      URI ({@link android.net.Uri} 物件) 可參考據以執行的資料和/或該資料的 MIME 類型。 +提供的資料類型通常是由意圖的動作控制。例如,如果動作是 {@link android.content.Intent#ACTION_EDIT},資料就應包含欲編輯文件的 URI。 + + + +

      建立意圖時,除了意圖的 URI 以外,請務必指定資料類型 (其 MIME 類型)。 + + +例如,能夠顯示影像的 Activity 可能無法播放音訊檔案,即使有類似的 URI 格式。 +因此,指定資料的 MIME 格式可協助 Android 系統找出最適合接收意圖的元件。 + +不過 — 尤其是當資料指出資料位在裝置何處且受 +{@link android.content.ContentProvider} 控制讓系統看見資料 MIME 類型的 +{@code content:} URI 時,有時能夠從 URI 推論出 MIME 類型。

      + +

      如果您只想設定資料 URI,請呼叫 {@link android.content.Intent#setData setData()}。 +如要設定 MIME 類型,請呼叫 {@link android.content.Intent#setType setType()}。您還可以視需要利用 +{@link +android.content.Intent#setDataAndType setDataAndType()} 明確設定兩者。

      + +

      注意:如果您想同時設定 URI 與 MIME 類型,「請勿」呼叫 {@link android.content.Intent#setData setData()} 和 +{@link android.content.Intent#setType setType()},原因是這兩者會抵銷彼此的值。 +請務必使用 {@link android.content.Intent#setDataAndType setDataAndType()} 來設定 URI 與 MIME 類型。 + +

      +
      + +

      類別
      +
      字串當中包含哪種元件應處理意圖的其他相關資訊。 +意圖中可放置的類別描述數目並沒有限制,但大多數意圖都不需要類別。 +以下是一些常見類別: + + +
      +
      {@link android.content.Intent#CATEGORY_BROWSABLE}
      +
      目標 Activity 允許自己由網頁瀏覽器啟動,以顯示連結所參照的資料 — 例如影像或電子郵件訊息。 + +
      +
      {@link android.content.Intent#CATEGORY_LAUNCHER}
      +
      Activity 是工作的初始 Activity,而且列在系統的應用程式啟動器中。 + +
      +
      + +

      如需類別的完整清單,請參閱 {@link android.content.Intent} 類別描述。 +

      + +

      您可以使用 {@link android.content.Intent#addCategory addCategory()} 來指定類別。

      +
      +
      + + +

      以上列出的屬性 (元件名稱、動作、資料及類別) 代表意圖的定義特性。 +藉由讀取這些屬性,Android 系統就能分析出應啟動的應用程式元件。 +

      + +

      不過,意圖還能攜帶其他不影響如何將它解析成應用程式元件的資訊。 +意圖還能提供:

      + +
      +
      額外資料
      +
      鍵值對當中攜帶完成要求動作所需的其他資訊。 +和有些動作會使用特定種類的資料 URI 一樣,有些動作也會使用特定的額外資料。 + +

      您可以使用各種 {@link android.content.Intent#putExtra putExtra()}方法來新增額外資料,每種方法都接受兩個參數:索引鍵名稱與值。 + + +您也可以使用所有的額外資料建立 {@link android.os.Bundle} 物件,再透過 {@link +android.content.Intent#putExtras putExtras()} 將 {@link android.content.Intent} 插入 {@link android.os.Bundle}。

      + +

      例如,建立意圖以使用 +{@link android.content.Intent#ACTION_SEND} 來傳送電子郵件時,您可以利用 +{@link android.content.Intent#EXTRA_EMAIL} 索引鍵指定「收件者」,並使用 +{@link android.content.Intent#EXTRA_SUBJECT} 索引鍵指定「主旨」。

      + +

      {@link android.content.Intent} 類別指定許多用於標準化資料類型的 +{@code EXTRA_*} 常數。如果您需要宣告自己的額外資料索引鍵 (用於您應用程式接收的意圖),請務必加入您應用程式的封裝名稱做為前置詞。 + +例如:

      +
      static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";
      +
      + +
      旗標
      +
      {@link android.content.Intent} 類別中定義的旗標,可當成意圖的中繼資料使用。 +旗標可指示 Android 系統如何啟動 Activity (例如,Activity 屬於哪個 +工作) 以及如何處理已啟動的 Activity (例如,它是否屬於最近 Activity 清單)。 + + + +

      如需詳細資訊,請參閱 {@link android.content.Intent#setFlags setFlags()} 方法。

      +
      + +
      + + + + +

      明確意圖範例

      + +

      您用來啟動特定應用程式元件的就是明確意圖,例如應用程式中的特定 Activity 或服務。如要建立明確意圖,請定義 + +{@link android.content.Intent}物件的元件名稱 — 其他意圖屬性均為選用性質。 +

      + +

      例如,您在應用程式中建置稱為 {@code DownloadService} 的服務,其設計為從網頁下載檔案,您可以使用下列程式碼來啟動該服務: +

      + +
      +// Executed in an Activity, so 'this' is the {@link android.content.Context}
      +// The fileUrl is a string URL, such as "http://www.example.com/image.png"
      +Intent downloadIntent = new Intent(this, DownloadService.class);
      +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl));
      +startService(downloadIntent);
      +
      + +

      {@link android.content.Intent#Intent(Context,Class)} 建構函式提供應用程式 {@link android.content.Context} 與元件 ({@link java.lang.Class} 物件)。 + +因此,這個意圖會明確啟動應用程式中的 +{@code DownloadService} 類別。

      + +

      如要進一步瞭解如何建置及啟動服務,請參閱服務指南。 +

      + + + + +

      隱含意圖範例

      + +

      隱藏意圖指定的動作會呼叫裝置上任何能執行該動作的應用程式。 +當您的應用程式無法執行該動作,但其他應用程式或許能執行,而您想讓使用者挑選要使用的應用程式時,使用隱含意圖相當有用。 +

      + +

      例如,您有想讓使用者與其他人分享的內容,可使用 +{@link android.content.Intent#ACTION_SEND} 動作來建立意圖,以及新增可指定要分享內容的額外資料。 +當您使用該意圖呼叫 +{@link android.content.Context#startActivity startActivity()} 時,使用者就能挑選要用以分享內容的應用程式。 +

      + +

      注意:使用者可能會沒有「任何」 +應用程式可處理您傳送至 {@link android.content.Context#startActivity +startActivity()} 的隱含意圖。如果發生這種情況,呼叫會失敗且應用程式將會當機。如要確認 Activity 將可收到意圖,請在 +{@link android.content.Intent} 物件上呼叫 {@link android.content.Intent#resolveActivity +resolveActivity()}。如果結果不是 null,表示至少有一個應用程式能夠處理該意圖,可以放心呼叫 + +{@link android.content.Context#startActivity startActivity()}。如果結果為 null,表示您不應使用該意圖,可以的話您還必須將發出該意圖的功能停用。 + +

      + + +
      +// Create the text message with a string
      +Intent sendIntent = new Intent();
      +sendIntent.setAction(Intent.ACTION_SEND);
      +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
      +sendIntent.setType("text/plain");
      +
      +// Verify that the intent will resolve to an activity
      +if (sendIntent.resolveActivity(getPackageManager()) != null) {
      +    startActivity(sendIntent);
      +}
      +
      + +

      注意:在這種情況下,不會使用 URI,但會宣告意圖的資料類型,以指定額外資料所攜帶的內容。 +

      + + +

      呼叫 {@link android.content.Context#startActivity startActivity()} 時,系統會檢查所有安裝的應用程式,判斷哪些可以處理這種意圖 (含有 {@link android.content.Intent#ACTION_SEND}動作和攜帶「純文字」資料的意圖)。 + + +如果只有一個應用程式能夠處理,該應用程式會立即開啟並獲派該意圖。 +如果有多個 Activity 接受該意圖,系統會顯示對話方塊,供使用者挑選要使用的應用程式。 +

      + + +
      + +

      圖 2.選擇器對話方塊。

      +
      + +

      強制顯示應用程式選擇器

      + +

      有多個應用程式均回應您的隱含意圖時,使用者可以選擇要使用的應用程式,並將該應用程式當成動作的預設選擇。 + +如果在執行動作時使用者希望之後都使用同一應用程式 (例如使用者偏好使用某個特定網頁瀏覽器開啟網頁,這項功能就非常實用。 + +

      + +

      不過,如果有多個應用程式能回應該意圖,而使用者每次都想使用不同的應用程式,您應該明確顯示選擇器對話方塊。 +選擇器對話方塊每次都會要求使用者選取要用於該動作的應用程式 (使用者無法為該動作選取預設應用程式)。 + +例如,當您的應用程式使用 {@link +android.content.Intent#ACTION_SEND} 動作執行「分享」時,使用者可能希望根據當下的情況使用不同的應用程式來分享,此時您應該一律使用選擇器對話方塊,如圖 2 所示。 +

      + + + + +

      如要顯示選擇器,請使用 {@link +android.content.Intent#createChooser createChooser()} 建立 {@link android.content.Intent},並將其傳送至 {@link +android.app.Activity#startActivity startActivity()}。例如:

      + +
      +Intent sendIntent = new Intent(Intent.ACTION_SEND);
      +...
      +
      +// Always use string resources for UI text.
      +// This says something like "Share this photo with"
      +String title = getResources().getString(R.string.chooser_title);
      +// Create intent to show the chooser dialog
      +Intent chooser = Intent.createChooser(sendIntent, title);
      +
      +// Verify the original intent will resolve to at least one activity
      +if (sendIntent.resolveActivity(getPackageManager()) != null) {
      +    startActivity(chooser);
      +}
      +
      + +

      以上範例會顯示對話方塊 (將回應意圖的應用程式清單傳送至 {@link +android.content.Intent#createChooser createChooser()} 方法),並使用提供的文字做為對話方塊的標題。 +

      + + + + + + + + + +

      接收隱含意圖

      + +

      如要通知應用程式可接收的隱含內容,請在宣告說明檔案中利用 {@code <intent-filter>} 元素,針對您的每個應用程式元件宣告一或多個意圖篩選器。每個意圖篩選器都會根據意圖的動作、資料和類別,指定其接受的意圖類型。 + + + +只有在意圖通過您的其中一個意圖篩選器時,系統才會將隱含意圖傳送至您的應用程式元件。 +

      + +

      注意:不論元件宣告的意圖篩選器為何,明確意圖一律會傳送至其目標。 +

      + +

      應用程式元件應為其所能執行的各個獨特工作宣告不同的篩選器。例如,圖片庫應用程式中的一個 Activity 可能會有兩個篩選器:一個用於檢視圖片的篩選器,以及另一個用於編輯圖片的篩選器。 + +當 Activity 啟動時,它會檢查 +{@link android.content.Intent} 並根據 +{@link android.content.Intent} 中的資訊決定如何運作 (例如,是否要檢視編輯器控制項)。

      + +

      每個意圖篩選器都是由 {@code <intent-filter>} 元素定義在應用程式的宣示說明檔案中,以巢狀方式置於對應的應用程式元件 (例如,{@code <activity>} 元素)。 + + +在 {@code <intent-filter>} 內,您可以使用以下三個元素當中的一或多個元素,指定要接受的意圖類型: + +

      + +
      +
      {@code <action>}
      +
      在 {@code name} 屬性中,宣告接受的意圖動作。值必須是動作的常值字串值,不得為類別常數。 +
      +
      {@code <data>}
      +
      使用一或多項屬性指定資料 URI ( +schemehostport、 +path 等) 與 MIME 類型的各層面,以宣告接受的資料類型。
      +
      {@code <category>}
      +
      在 {@code name} 屬性中,宣告接受的意圖類別。值必須是動作的常值字串值,不得為類別常數。 + + +

      注意:如要接收隱含意圖,您「必須」在意圖篩選器中納入 {@link android.content.Intent#CATEGORY_DEFAULT} 類別。 + + +{@link android.app.Activity#startActivity startActivity()} 與 +{@link android.app.Activity#startActivityForResult startActivityForResult()} 方法對所有意圖進行處理時,就像已宣告 +{@link android.content.Intent#CATEGORY_DEFAULT} 類別一樣。 + 如果您未在意圖篩選器中宣告此類別,任何隱含意圖都不會解析為 Activity。 +

      +
      +
      + +

      例如,假設資料類型為文字,以下 Activity 宣告的意圖篩選器都可接受 +{@link android.content.Intent#ACTION_SEND} 意圖:

      + +
      +<activity android:name="ShareActivity">
      +    <intent-filter>
      +        <action android:name="android.intent.action.SEND"/>
      +        <category android:name="android.intent.category.DEFAULT"/>
      +        <data android:mimeType="text/plain"/>
      +    </intent-filter>
      +</activity>
      +
      + +

      想要建立包含多個 +{@code <action>}、 +{@code <data>} 或 +{@code <category>} 執行個體的篩選器也沒關係。 +如果您這樣做,只需要確定該元件能處理這些篩選器元素的任何組合。 +

      + +

      當您想要處理多種意圖,但只限特定的動作、資料及類別類型組合時,您必須建立多個意圖篩選器。 +

      + + + + +

      藉由將意圖與三個元素個別比較,針對篩選器來測試隱含意圖。 +如要傳送至元件,意圖必須通過共三個測試。 + +如果無一相符,Android 系統就不會將意圖傳送至該元件。不過,由於元件可能會有多個意圖篩選器,未通過其中一個元件篩選器的意圖可能會通過另一個篩選器。 + + +如要進一步瞭解系統如何解析意圖,請參閱下文的意圖解析

      + +

      注意:如要避免一時疏忽而執行不同應用程式的 +{@link android.app.Service},請一律使用明確意圖啟動您自己的服務,同時不要宣告服務的意圖篩選器。 +

      + +

      注意:對於所有 Activity,您都必須在宣示說明檔案中宣告意圖篩選器。 + +不過,廣播接收器的篩選器可以藉由呼叫 +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()} 進行動態註冊。您之後可以利用 {@link +android.content.Context#unregisterReceiver unregisterReceiver()} 來取消註冊接收器。這樣做可在您的應用程式執行時,讓應用程式只能在指定期間內接聽特定廣播。 + +

      + + + + + + + +

      篩選器範例

      + +

      如要進一步瞭解意圖篩選器行為,可以看看社交分享應用程式宣示說明檔案中的以下程式碼片段。 +

      + +
      +<activity android:name="MainActivity">
      +    <!-- This activity is the main entry, should appear in app launcher -->
      +    <intent-filter>
      +        <action android:name="android.intent.action.MAIN" />
      +        <category android:name="android.intent.category.LAUNCHER" />
      +    </intent-filter>
      +</activity>
      +
      +<activity android:name="ShareActivity">
      +    <!-- This activity handles "SEND" actions with text data -->
      +    <intent-filter>
      +        <action android:name="android.intent.action.SEND"/>
      +        <category android:name="android.intent.category.DEFAULT"/>
      +        <data android:mimeType="text/plain"/>
      +    </intent-filter>
      +    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
      +    <intent-filter>
      +        <action android:name="android.intent.action.SEND"/>
      +        <action android:name="android.intent.action.SEND_MULTIPLE"/>
      +        <category android:name="android.intent.category.DEFAULT"/>
      +        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
      +        <data android:mimeType="image/*"/>
      +        <data android:mimeType="video/*"/>
      +    </intent-filter>
      +</activity>
      +
      + +

      第一個 Activity {@code MainActivity} 是應用程式的主要進入點 — 這個 Activity 會在使用者使用啟動器圖示初次啟動應用程式時開啟: +

      +
        +
      • {@link android.content.Intent#ACTION_MAIN} 動作可指出這是主要進入點且預期沒有任何意圖資料。 +
      • +
      • {@link android.content.Intent#CATEGORY_LAUNCHER} 類別可指出此 Activity 的圖示應該放在系統的應用程式啟動器中。 +如果 {@code <activity>}元素未以 {@code icon} 指定圖示,系統會使用 {@code <application>} 元素中的圖示。 + +
      • +
      +

      上述兩項必須成對,Activity 才會顯示在應用程式啟動器中。

      + +

      第二個 Activity {@code ShareActivity} 是用來加速分享文字與媒體內容。 +雖然使用者可能藉由從 {@code MainActivity} 來瀏覽它而進入此 Activity,但它們也能從發出隱含意圖 (符合兩個意圖篩選器之一) 的另一款應用程式直接進入 {@code ShareActivity}。 + +

      + +

      注意: +{@code +application/vnd.google.panorama360+jpg} MIME 類型是指定全景相片的特殊資料類型,您可以透過 +Google 全景 API 來處理。 +

      + + + + + + + + + + + + + +

      使用待處理意圖

      + +

      {@link android.app.PendingIntent} 物件是環繞 {@link +android.content.Intent} 物件的包裝函式。{@link android.app.PendingIntent} 的主要用途是將權限授予外部應用程式,以便使用包含的 {@link android.content.Intent},有如從應用程式自己的程序執行一般。 + + +

      + +

      待處理意圖的主要使用案例包括:

      +
        +
      • 宣告當使用者透過您的通知執行動作時要執行的意圖 (Android 系統的 {@link android.app.NotificationManager} 會執行 {@link android.content.Intent})。 + + +
      • 宣告當使用者透過您的應用程式小工具執行動作時要執行的意圖 (主螢幕應用程式會執行 {@link android.content.Intent})。 + + +
      • 宣告要在未來的指定時間內執行的意圖 (Android 系統的 {@link android.app.AlarmManager} 會執行 {@link android.content.Intent})。 + +
      + +

      由於每個 {@link android.content.Intent} 物件都設計為要由特定的應用程式元件類型來處理 ({@link android.app.Activity}、{@link android.app.Service} 或 {@link android.content.BroadcastReceiver}),因此請務必以相同的考量建立 {@link android.app.PendingIntent}。 + + +使用待處理意圖時,您的應用程式將不會透過像是 +{@link android.content.Context#startActivity +startActivity()} 的呼叫來執行意圖。當您藉由呼叫以下的個別建立者方法建立 +{@link android.app.PendingIntent} 時,您必須改為宣告意圖元件類型:

      + +
        +
      • {@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} 適用於啟動 {@link android.app.Activity} 的 {@link android.content.Intent}。 +
      • +
      • {@link android.app.PendingIntent#getService PendingIntent.getService()} 適用於啟動 {@link android.app.Service} 的 {@link android.content.Intent}。 +
      • +
      • {@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} 適用於啟動 {@link android.content.BroadcastReceiver} 的 {@link android.content.Intent}。 +
      • +
      + +

      除非您的應用程式「收到」來自其他應用程式的待處理意圖,否則您可能只需要上述建立 +{@link android.app.PendingIntent} 的 +{@link android.app.PendingIntent} 方法。

      + +

      每個方法會採用目前的應用程式 {@link android.content.Context}、您要包裝的 +{@link android.content.Intent} 以及一或多個指定應如何使用意圖的旗標 (例如該意圖是否能使用多次)。 +

      + +

      如要進一步瞭解如何使用待處理意圖,請參閱個別使用案例的參考文件,例如通知以及應用程式小工具 API 指南。 + +

      + + + + + + + +

      意圖解析

      + + +

      當系統收到要啟動 Activity 的隱含意圖時,會根據三個層面來比較意圖與意圖篩選器,以便為該意圖搜尋最適合的 Activity。 +

      + +
        +
      • 意圖動作 +
      • 意圖資料 (URI 與資料類型) +
      • 意圖類別 +
      + +

      下列各節就如何在應用程式宣示說明檔案中宣告意圖篩選器這點,說明如何比對意圖以找出適當的元件。 +

      + + +

      動作測試

      + +

      如要指定接受的意圖動作,意圖篩選器可以宣告零或多個 +{@code +<action>} 元素。例如:

      + +
      +<intent-filter>
      +    <action android:name="android.intent.action.EDIT" />
      +    <action android:name="android.intent.action.VIEW" />
      +    ...
      +</intent-filter>
      +
      + +

      如要通過此篩選器,{@link android.content.Intent} 中指定的動作必須與篩選器中列出的其中一個動作相符。 +

      + +

      如果篩選器未列出任何可供意圖比對的動作,所有意圖都無法通過測試。 +不過,如果 {@link android.content.Intent} 未指定任何動作,它就會通過測試 (只要篩選器至少包含一個動作即可)。 + +

      + + + +

      類別測試

      + +

      如要指定接受的意圖類別,意圖篩選器可以宣告零或多個 +{@code +<category>} 元素。例如:

      + +
      +<intent-filter>
      +    <category android:name="android.intent.category.DEFAULT" />
      +    <category android:name="android.intent.category.BROWSABLE" />
      +    ...
      +</intent-filter>
      +
      + +

      {@link android.content.Intent} 中的每個類別都必須與篩選器中的類別相符,意圖才會通過類別測試。 +不需要反向進行 — 意圖篩選器宣告的類別可以比 +{@link android.content.Intent} 中指定的類別多,而 +{@link android.content.Intent} 仍可通過測試。因此,不論篩選器中宣告的類別為何,未包含類別的意圖一律可通過此測試。 +

      + +

      注意:Android 會將 {@link android.content.Intent#CATEGORY_DEFAULT} 類別自動套用到所有傳送至 {@link +android.content.Context#startActivity startActivity()} 與 {@link +android.app.Activity#startActivityForResult startActivityForResult()} 的隱含意圖。因此,如果您希望 Activity 收到隱含意圖,Activity 的意圖篩選器就必須包含 +{@code "android.intent.category.DEFAULT"} 的類別 (如先前的 {@code <intent-filter>} 範例中所示)。 + + + +

      + + + +

      資料測試

      + +

      如要指定接受的意圖資料,意圖篩選器可以宣告零或多個 +{@code +<data>} 元素。例如:

      + +
      +<intent-filter>
      +    <data android:mimeType="video/mpeg" android:scheme="http" ... />
      +    <data android:mimeType="audio/mpeg" android:scheme="http" ... />
      +    ...
      +</intent-filter>
      +
      + +

      每個 <data> + 元素都能指定 URI 結構與資料類型 (MIME 媒體類型)。URI 的每個部分都有個別的屬性: +{@code scheme}、{@code host}、{@code port} 和 {@code path}。 + +

      + +

      {@code <scheme>://<host>:<port>/<path>}

      + +

      +例如: +

      + +

      {@code content://com.example.project:200/folder/subfolder/etc}

      + +

      在這個 URI 中,配置為 {@code content}、主機為 {@code com.example.project}、連接埠為 {@code 200},而路徑為 {@code folder/subfolder/etc}。 + +

      + +

      {@code <data>} 元素中,上述所有屬性均為選用性質,但具有線性相依性: +

      +
        +
      • 如果未指定配置,就會忽略主機。
      • +
      • 如果未指定主機,就會忽略連接埠。
      • +
      • 如果配置與主機均未指定,就會忽略路徑。
      • +
      + +

      將意圖中的 URI 與篩選器中的 URI 規格比較時,只會比較篩選器中所包含的 URI 部分。 +例如:

      +
        +
      • 如果篩選器只指定配置,含有該配置的所有 URI 都會與篩選器相符。 +
      • +
      • 如果篩選器指定了配置與授權,但未指定路徑,不論其路徑為何,含有相同配置與授權的所有 URI 都會通過篩選器。 +
      • +
      • 如果篩選器指定了配置、授權和路徑,則只有包含相同配置、授權和路徑的 URI 才會通過篩選器。 +
      • +
      + +

      注意:路徑規格可以包含萬用字元星號 (*),以要求僅部分相符的路徑名稱。 +

      + +

      資料測試會比較意圖中的 URI 與 MIME 類型,以及篩選器中指定的 URI 與 MIME 類型。 +以下說明規則: +

      + +
        +
      1. 只有在篩選器未指定任何 URI 或 MIME 類型時,未包含 URI 或 MIME 類型的意圖才會通過測試。 +
      2. + +
      3. 只有在其 URI 與篩選器的 URI 格式相符,而且篩選器同樣未指定 MIME 類型時,包含 URI 但沒有 MIME 類型 (既非明確也無法從 URI 推測得出) 的意圖才會通過測試。 + +
      4. + +
      5. 只有在篩選器列出相同的 MIME 類型且未指定 URI 格式時,包含 MIME 類型但沒有 URI 的意圖才會通過測試。 +
      6. + +
      7. 只有在 MIME 類型與篩選器中列出的類型相符時,包含 URI 與 MIME 類型 (明確或可從 URI 推測得出) 的意圖才會通過 MIME 類型部分的測試。 + +如果它的 URI 與篩選器中的 URI 相符,或如果它有 +{@code content:} 或 {@code file:} URI,而且篩選器未指定 URI 時,就會通過 URI 部分的測試。換句話說,如果它的篩選器「只」列出 MIME 類型,就會假設元件支援 {@code content:} 與 {@code file:} 資料。 + + +

      8. +
      + +

      +最後一項規則 (規則 (d)) 可反映出希望元件能夠從檔案或內容供應程式取得本機資料的期望。 + +因此,其篩選器可以只列出資料類型,同時不需要明確命名 +{@code content:} 與 {@code file:} 配置。 +此為典型案例。例如,如下所示的 {@code <data>} 元素可以告訴 Android,元件能夠從內容供應程式取得影像資料並加以顯示: + + +

      + +
      +<intent-filter>
      +    <data android:mimeType="image/*" />
      +    ...
      +</intent-filter>
      + +

      +由於大部分可用資料都是由內容供應程式分配,因此指定資料類型但未指定 URI 的篩選器也許會最常見。 + +

      + +

      +另一個常見的設定是含有配置與資料類型的篩選器。例如,如下所示的 +{@code <data>}元素可以告訴 Android,元件能夠從網路擷取影片資料以執行動作: + + +

      + +
      +<intent-filter>
      +    <data android:scheme="http" android:type="video/*" />
      +    ...
      +</intent-filter>
      + + + +

      意圖比對

      + +

      和意圖篩選器比對的意圖不只可探尋要啟動的目標元件,還能探尋裝置上元件集合的相關資料。 + +例如,主畫面應用程式會利用指定 +{@link android.content.Intent#ACTION_MAIN} 動作與 +{@link android.content.Intent#CATEGORY_LAUNCHER} 類別的意圖篩選器來尋找所有 Activity,以填入應用程式啟動器。 +

      + +

      您的應用程式能以類似的方式使用意圖比對。 +{@link android.content.pm.PackageManager} 有一組 {@code query...()}方法可將接受特定意圖的所有元件傳回,還有一系列類似的 +{@code resolve...()} 方法可決定回應意圖的最佳元件。 + +例如, +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} 會傳回所有 Activity 清單,上述的 Activity 都可以執行當成引數傳送的意圖,以及 +{@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} 可傳回類似的服務清單。 + +這些方法不會啟動元件,只會列出可以回應的元件。類似的方法 +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()} 可用於廣播接收器。 +

      + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/loaders.jd b/docs/html-intl/intl/zh-tw/guide/components/loaders.jd new file mode 100644 index 0000000000000000000000000000000000000000..89bfc80744d9605aae4f8a5e9ff2b21a6fcea079 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=載入器 +parent.title=Activity +parent.link=activities.html +@jd:body +
      +
      +

      本文件內容

      +
        +
      1. 載入器 API 摘要
      2. +
      3. 在應用程式中使用載入器 +
          +
        1. +
        2. 啟動載入器
        3. +
        4. 重新啟動載入器
        5. +
        6. 使用 LoaderManager 回呼
        7. +
        +
      4. +
      5. 範例說明 +
          +
        1. 其他範例
        2. +
        +
      6. +
      + +

      重要類別

      +
        +
      1. {@link android.app.LoaderManager}
      2. +
      3. {@link android.content.Loader}
      4. + +
      + +

      相關範例

      +
        +
      1. LoaderCursor +
      2. +
      3. LoaderThrottle +
      4. +
      +
      +
      + +

      在 Android 3.0 導入的載入器可輕鬆在 Activity 或片段中,以非同步方式載入資料。 +載入器的特性如下:

      +
        +
      • 適用於所有 {@link android.app.Activity} 和 {@link +android.app.Fragment} 。
      • +
      • 提供以非同步方式載入資料。
      • +
      • 監視其資料來源,並在內容變更時傳送新的結果。 +
      • +
      • 可在設定變更後重新建立時,自動重新連接到上次載入器的游標, +因此不需要重新查詢資料。 +
      • +
      + +

      載入器 API 摘要

      + +

      有多個類別和介面可能與在應用程式中使用載入器有關。 +請參閱下表的摘要說明:

      + + + + + + + + + + + + + + + + + + + + + + + + + + +
      類別/介面說明
      {@link android.app.LoaderManager}與 {@link android.app.Activity} 或 +{@link android.app.Fragment} 相關聯的抽象類別,可用於管理一或多個 {@link +android.content.Loader} 執行個體。這可以協助應用程式管理所需執行時間較長的操作與 {@link android.app.Activity} 或 {@link android.app.Fragment} 生命週期搭配使用,這最常與 +{@link android.content.CursorLoader} 搭配使用,不過應用程式能夠撰寫自己的載入器來載入其他類型的資料。 + + + +
      +
      + 每個 Activity 或片段只能有一個 {@link android.app.LoaderManager},但 {@link android.app.LoaderManager} 可以有多個載入器。 +
      {@link android.app.LoaderManager.LoaderCallbacks}可供用戶端與 {@link +android.app.LoaderManager} 互動的回呼介面。例如,您使用 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +回呼方法來建立新的載入器。
      {@link android.content.Loader}以非同步方式載入資料的抽象類別。這是載入器的基本類別。 +您通常會使用 {@link +android.content.CursorLoader},但您也可以實作自己的子類別。載入器處於使用中時,應會監視其資料來源,並在內容有變更時傳送新的結果。 + +
      {@link android.content.AsyncTaskLoader}提供 {@link android.os.AsyncTask} 以執行工作的抽象載入器。
      {@link android.content.CursorLoader}查詢 +{@link android.content.ContentResolver} 並傳回 {@link +android.database.Cursor} 的 {@link android.content.AsyncTaskLoader} 子類別。此類別會以標準方式實作 {@link +android.content.Loader} 通訊協定,用來查詢建置於 {@link android.content.AsyncTaskLoader} 的游標,以便在背景執行緒中執行游標查詢,藉此避免封鎖應用程式的 UI。 + + +以非同步方式從 {@link +android.content.ContentProvider} 載入資料,而不是透過片段或 Activity 的 API 來管理查詢,最好的方法就是使用此載入器。 +
      + +

      上表中的類別和介面就是您將用來在應用程式中實作載入器的基本元件。 +上述元件不需要在您建立載入器時全部使用,但您必須一律參照到 {@link +android.app.LoaderManager},才能初始化載入器並實作 +{@link android.content.Loader} 類別,例如 {@link +android.content.CursorLoader}。 +以下各節說明如何在應用程式中使用這些類別和介面。 +

      + +

      在應用程式中使用載入器

      +

      本節說明如何在 Android 應用程式中使用載入器。使用載入器的應用程式通常包括下列物件: +

      +
        +
      • {@link android.app.Activity} 或 {@link android.app.Fragment}。
      • +
      • {@link android.app.LoaderManager} 的執行個體。
      • +
      • 可載入 {@link +android.content.ContentProvider} 所備份資料的 {@link android.content.CursorLoader}。或者,您可以實作自己的 +{@link android.content.Loader} 子類別或 {@link android.content.AsyncTaskLoader},從其他來源載入資料。 +
      • +
      • {@link android.app.LoaderManager.LoaderCallbacks} 的實作。 +您可以在這裡建立新的載入器和管理對現有載入器的參照。 +
      • +
      • 顯示載入器資料的一種方式,例如 {@link +android.widget.SimpleCursorAdapter}。
      • +
      • 使用 {@link android.content.CursorLoader} 時的資料來源,例如 {@link android.content.ContentProvider}。 +
      • +
      +

      啟動載入器

      + +

      {@link android.app.LoaderManager} 可在 {@link android.app.Activity} 或 {@link android.app.Fragment} 內管理一或多個 {@link +android.content.Loader} 執行個體。 +每個 Activity 或片段只能有一個 {@link +android.app.LoaderManager}。

      + +

      您通常會在 Activity 的 {@link +android.app.Activity#onCreate onCreate()} 方法內或在片段的 {@link android.app.Fragment#onActivityCreated onActivityCreated()} 方法內,初始化 {@link android.content.Loader}, + +如下所示: +

      + +
      // Prepare the loader.  Either re-connect with an existing one,
      +// or start a new one.
      +getLoaderManager().initLoader(0, null, this);
      + +

      {@link android.app.LoaderManager#initLoader initLoader()} 方法採用下列參數: +

      +
        +
      • 可識別載入器的不重複 ID。在本範例中,此 ID 為 0。
      • +
      • 可在建構時提供給載入器的選用引數 (在本範例中為null)。 +
      • + +
      • {@link android.app.LoaderManager.LoaderCallbacks} 實作, +{@link android.app.LoaderManager} 會呼叫此實作來回報載入器事件。在本範例中,本機類別會實作 {@link +android.app.LoaderManager.LoaderCallbacks} 執行個體,以將參照傳送給它自己 ({@code this})。 + +
      • +
      +

      {@link android.app.LoaderManager#initLoader initLoader()} 呼叫可確保載入器已初始化且處於有效狀態。 +可能會有兩種結果:

      +
        +
      • 如果 ID 所指定的載入器已經存在,就會重複使用上次建立的載入器。 +
      • +
      • 如果 ID 所指定的載入器「不存在」, +{@link android.app.LoaderManager#initLoader initLoader()} 會觸發 +{@link android.app.LoaderManager.LoaderCallbacks} 方法 {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。 +您可以在這裡實作程式碼以具現化及傳回新的載入器。 +如需詳細資訊,請參閱 onCreateLoader
      • +
      +

      在任一情況下,指定的 {@link android.app.LoaderManager.LoaderCallbacks}實作會與載入器建立關聯且會在載入器狀態變更時呼叫。 + +如果進行此呼叫時,呼叫器處於已啟動狀態,而要求的載入器已經存在並產生自己的資料,那麼系統會立即呼叫 {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} (在 {@link android.app.LoaderManager#initLoader initLoader()} 期間),請務必做好發生這種情況的準備。 + + + +如要進一步瞭解此回呼,請參閱 onLoadFinished +

      + +

      請注意,{@link android.app.LoaderManager#initLoader initLoader()}方法會傳回建立的 {@link android.content.Loader},但您不需要擷取它的參照。 + +{@link android.app.LoaderManager} 會自動管理載入器的生命週期。 +{@link android.app.LoaderManager} 會在必要時啟動及停止載入,並維護載入器的狀態與其相關內容。 + +顧名思義,您鮮少會直接與載入器互動 (但如需使用載入器方法微調載入器行為的範例,請參閱 LoaderThrottle 範例)。 + +當發生特定事件時,您最常會使用 {@link +android.app.LoaderManager.LoaderCallbacks} 方法來介入載入程序。 + +如要進一步瞭解此主題,請參閱使用 LoaderManager 回呼

      + +

      重新啟動載入器

      + +

      當您使用 {@link android.app.LoaderManager#initLoader initLoader()} (如上所示),它會使用已指定 ID 的現有載入器 (如果有的話)。 + +如果沒有,就會自行建立載入器。不過,有時候您會想捨棄舊資料並從頭開始。 +

      + +

      如要捨棄舊資料,請使用 {@link +android.app.LoaderManager#restartLoader restartLoader()}。例如,當使用者的查詢改變時,實作 {@link android.widget.SearchView.OnQueryTextListener} 會重新啟動載入器。 + +您必須重新啟動載入器,才能使用修訂後的搜尋篩選器執行新的查詢: +

      + +
      +public boolean onQueryTextChanged(String newText) {
      +    // Called when the action bar search text has changed.  Update
      +    // the search filter, and restart the loader to do a new query
      +    // with this filter.
      +    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
      +    getLoaderManager().restartLoader(0, null, this);
      +    return true;
      +}
      + +

      使用 LoaderManager 回呼

      + +

      {@link android.app.LoaderManager.LoaderCallbacks} 是回呼介面,可讓用戶端與 {@link android.app.LoaderManager} 互動。 +

      +

      載入器 (特別是 {@link android.content.CursorLoader}) 在停止之後,應該會保留它們的資料。 +這可讓應用程式保留其在 Activity 或片段的 {@link android.app.Activity#onStop +onStop()} 與 {@link android.app.Activity#onStart onStart()} 方法間的資料,好讓使用者回到應用程式時,不必枯等資料重新載入。 + + +您可以使用 {@link android.app.LoaderManager.LoaderCallbacks} 方法,藉以得知何時該建立新的載入器,以及指示應用程式何時該停止使用載入器的資料。 + +

      + +

      {@link android.app.LoaderManager.LoaderCallbacks} 包含以下方法: +

      +
        +
      • {@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — 具現化及傳回指定 ID 的新 {@link android.content.Loader}。 + +
      +
        +
      • {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} — 當先前建立的載入器已完成其載入工作時呼叫。 + +
      +
        +
      • {@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} — 正要重設先前建立的載入器時呼叫,使其資料無法使用。 + + +
      • +
      +

      以上方法在下列幾節有更詳細的說明。

      + +

      onCreateLoader

      + +

      當您嘗試存取載入器 (例如,透過 {@link +android.app.LoaderManager#initLoader initLoader()}) 時,系統會檢查根據 ID 指定的載入器是否已存在。 +如果不存在,就會觸發 {@link +android.app.LoaderManager.LoaderCallbacks} 方法 {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}。您可以在這裡建立新的載入器。 +這通常會是 {@link +android.content.CursorLoader},但您也可以實作自己的 {@link +android.content.Loader} 子類別。

      + +

      在此範例中,{@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} 回呼方法會建立 {@link android.content.CursorLoader}。 +您必須使用其建構函式方法來建置 +{@link android.content.CursorLoader},其需要一組完整的資訊才能對 {@link +android.content.ContentProvider} 執行查詢。 +具體來說,建構函式方法需要以下項目:

      +
        +
      • uri - 要擷取內容的 URI。
      • +
      • projection - 要傳回的欄清單。傳送 +null 將會傳回無效的所有欄。
      • +
      • selection - 篩選器會採用 SQL WHERE 子句的格式 (WHERE 本身除外) 宣告要傳回的列。 +傳送 +null 將會傳回指定 URI 的所有列。
      • +
      • selectionArgs - 您可能會在選取項目中包含 ?s,而其會由來自 selectionArgs 的值按照其出現在選取項目中的順序所取代。 + +值將會繫結為字串。
      • +
      • sortOrder - 如何採用 SQL ORDER BY 子句的格式 (ORDER BY 本身除外) 來排列各列的順序。 +傳遞 null 將會使用預設的排序順序,其可能是無排序順序。 +
      • +
      +

      例如:

      +
      + // If non-null, this is the current filter the user has provided.
      +String mCurFilter;
      +...
      +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
      +    // This is called when a new Loader needs to be created.  This
      +    // sample only has one Loader, so we don't care about the ID.
      +    // First, pick the base URI to use depending on whether we are
      +    // currently filtering.
      +    Uri baseUri;
      +    if (mCurFilter != null) {
      +        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
      +                  Uri.encode(mCurFilter));
      +    } else {
      +        baseUri = Contacts.CONTENT_URI;
      +    }
      +
      +    // Now create and return a CursorLoader that will take care of
      +    // creating a Cursor for the data being displayed.
      +    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
      +            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
      +            + Contacts.DISPLAY_NAME + " != '' ))";
      +    return new CursorLoader(getActivity(), baseUri,
      +            CONTACTS_SUMMARY_PROJECTION, select, null,
      +            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
      +}
      +

      onLoadFinished

      + +

      這個方法是在先前建立的載入器已完成其載入工作時呼叫。 +此方法一律是在提供給此載入器的最後資料發佈之前呼叫。 +此時,您應要移除所有使用的舊資料 (由於資料即將發佈),但不應自行發佈,因為載入器擁有該資料且將會負責處理。 + +

      + + +

      一旦應用程式不再使用資料時,載入器就會立即發佈資料。 +例如,如果資料是來自 {@link +android.content.CursorLoader} 的游標,您不應該自行對它呼叫 {@link +android.database.Cursor#close close()}。如果正要將該游標放入 +{@link android.widget.CursorAdapter},您應該使用 {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} 方法,如此才不會關閉舊的 +{@link android.database.Cursor}。例如:

      + +
      +// This is the Adapter being used to display the list's data.
      SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in.  (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}
      + +

      onLoaderReset

      + +

      這個方法是在正要重設先前建立的載入器時呼叫,以便使其資料無法使用。 +此回呼方法可讓您知道即將要發佈資料,而能先行將其參照移除。 +  

      +

      此實作會以 null 的值呼叫 +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()}: +

      + +
      +// This is the Adapter being used to display the list's data.
      +SimpleCursorAdapter mAdapter;
      +...
      +
      +public void onLoaderReset(Loader<Cursor> loader) {
      +    // This is called when the last Cursor provided to onLoadFinished()
      +    // above is about to be closed.  We need to make sure we are no
      +    // longer using it.
      +    mAdapter.swapCursor(null);
      +}
      + + +

      範例說明

      + +

      例如,以下是 {@link +android.app.Fragment} 的完整實作, +其顯示的 {@link android.widget.ListView} 包含聯絡人內容供應程式的查詢結果。它使用 {@link +android.content.CursorLoader} 管理對供應程式的查詢。

      + +

      如本範例所示,針對要存取使用者聯絡人的應用程式,它的宣示說明必須包含 {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} 權限。 + +

      + +
      +public static class CursorLoaderListFragment extends ListFragment
      +        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {
      +
      +    // This is the Adapter being used to display the list's data.
      +    SimpleCursorAdapter mAdapter;
      +
      +    // If non-null, this is the current filter the user has provided.
      +    String mCurFilter;
      +
      +    @Override public void onActivityCreated(Bundle savedInstanceState) {
      +        super.onActivityCreated(savedInstanceState);
      +
      +        // Give some text to display if there is no data.  In a real
      +        // application this would come from a resource.
      +        setEmptyText("No phone numbers");
      +
      +        // We have a menu item to show in action bar.
      +        setHasOptionsMenu(true);
      +
      +        // Create an empty adapter we will use to display the loaded data.
      +        mAdapter = new SimpleCursorAdapter(getActivity(),
      +                android.R.layout.simple_list_item_2, null,
      +                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
      +                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
      +        setListAdapter(mAdapter);
      +
      +        // Prepare the loader.  Either re-connect with an existing one,
      +        // or start a new one.
      +        getLoaderManager().initLoader(0, null, this);
      +    }
      +
      +    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
      +        // Place an action bar item for searching.
      +        MenuItem item = menu.add("Search");
      +        item.setIcon(android.R.drawable.ic_menu_search);
      +        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
      +        SearchView sv = new SearchView(getActivity());
      +        sv.setOnQueryTextListener(this);
      +        item.setActionView(sv);
      +    }
      +
      +    public boolean onQueryTextChange(String newText) {
      +        // Called when the action bar search text has changed.  Update
      +        // the search filter, and restart the loader to do a new query
      +        // with this filter.
      +        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
      +        getLoaderManager().restartLoader(0, null, this);
      +        return true;
      +    }
      +
      +    @Override public boolean onQueryTextSubmit(String query) {
      +        // Don't care about this.
      +        return true;
      +    }
      +
      +    @Override public void onListItemClick(ListView l, View v, int position, long id) {
      +        // Insert desired behavior here.
      +        Log.i("FragmentComplexList", "Item clicked: " + id);
      +    }
      +
      +    // These are the Contacts rows that we will retrieve.
      +    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
      +        Contacts._ID,
      +        Contacts.DISPLAY_NAME,
      +        Contacts.CONTACT_STATUS,
      +        Contacts.CONTACT_PRESENCE,
      +        Contacts.PHOTO_ID,
      +        Contacts.LOOKUP_KEY,
      +    };
      +    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
      +        // This is called when a new Loader needs to be created.  This
      +        // sample only has one Loader, so we don't care about the ID.
      +        // First, pick the base URI to use depending on whether we are
      +        // currently filtering.
      +        Uri baseUri;
      +        if (mCurFilter != null) {
      +            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
      +                    Uri.encode(mCurFilter));
      +        } else {
      +            baseUri = Contacts.CONTENT_URI;
      +        }
      +
      +        // Now create and return a CursorLoader that will take care of
      +        // creating a Cursor for the data being displayed.
      +        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
      +                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
      +                + Contacts.DISPLAY_NAME + " != '' ))";
      +        return new CursorLoader(getActivity(), baseUri,
      +                CONTACTS_SUMMARY_PROJECTION, select, null,
      +                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
      +    }
      +
      +    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
      +        // Swap the new cursor in.  (The framework will take care of closing the
      +        // old cursor once we return.)
      +        mAdapter.swapCursor(data);
      +    }
      +
      +    public void onLoaderReset(Loader<Cursor> loader) {
      +        // This is called when the last Cursor provided to onLoadFinished()
      +        // above is about to be closed.  We need to make sure we are no
      +        // longer using it.
      +        mAdapter.swapCursor(null);
      +    }
      +}
      +

      其他範例

      + +

      ApiDemos 中有數個不同的範例,示範如何使用載入器: +

      +
        +
      • LoaderCursor - 上述程式碼片段的完整版本在此。 + +
      • +
      • LoaderThrottle - 以範例說明如何使用節流功能在其資料變更時降低內容供應程式執行的查詢數目。 +
      • +
      + +

      如要進一步瞭解如何下載及安裝 SDK 範例,請參閱取得範例。 +

      + diff --git a/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd b/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd new file mode 100644 index 0000000000000000000000000000000000000000..74dbb8ebb3f151dfa70b65a11e0bd869c9058077 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=處理程序和執行緒 +page.tags=生命週期、背景 + +@jd:body + + + +

      當應用程式元件啟動且該應用程式未執行任何其他元件時,Android 系統會以執行單一執行緒的方式,為該應用程式啟動新的 Linux 處理程序。 + +預設會以相同的處理程序和執行緒 (稱為「主要」執行緒) 執行相同應用程式的所有元件。 +如果應用程式元件啟動且已有該應用程式的處理程序存在 (由於應用程式還有另一個元件存在),那麼元件會在該處理程序中啟動,並使用相同的執行緒執行。 + +不過,您可以安排應用程式中的不同元件以個別處理程序執行,還可以為任何處理程序建立額外的執行緒。 + +

      + +

      本文件說明處理程序和執行緒如何在 Android 應用程式中運作。

      + + +

      處理程序

      + +

      在預設情況下,系統會以相同的處理程序執行相同應用程式的所有元件,而且大部分應用程式都是如此。 +不過,如果您需要控制特定元件所屬的處理程序,可以在宣示說明檔案中這麼做。 +

      + +

      每種元件元素 — {@code +<activity>}{@code +<service>}{@code +<receiver>}{@code +<provider>} — 的宣示說明項目都支援 {@code android:process} 屬性,這項屬性能指定元件應在哪個處理程序執行。 +您可以設定此屬性讓每個元件都以自己的處理程序執行,或只讓當中的部分元件共用同一處理程序。 +您也可以設定 +{@code android:process},讓不同應用程式的元件以相同的處理程序執行,只要這些應用程式分享相同的 Linux 使用者 ID 並以相同的憑證簽署。 + +

      + +

      {@code +<application>} 元素也支援 {@code android:process} 屬性,以設定要套用到所有元件的預設值。 +

      + +

      Android 可能會在記憶體不足且需要其他處理程序更立即為使用者提供服務時,決定關閉處理程序。 +使用已終止的處理程序執行的應用程式元件會因此而終結。 +當再次有工作需要執行時,就會為這些元件再次啟動處理程序。 +

      + +

      Android 系統會將處理程序對使用者的相對重要性加權,以決定要終止的處理程序。 +例如,相較於代管可見 Activity 的處理程序,系統較容易關閉代管已不在螢幕上顯示的 Activity 的處理程序。 +因此,是否要終止處理程序,取決於以該處理程序執行中的元件狀態。 +如要瞭解用於決定是否終止處理程序的規則,請參閱下文。 +

      + + +

      處理程序生命週期

      + +

      Android 系統會儘可能持續維護處理程序,但最終仍必須移除舊的處理程序,以便回收記憶體供新的或更重要的處理程序使用。 +系統會根據以該處理程序執行的元件和那些元件的狀態,將每個處理程序放入「重要性階層」,藉此決定要保留以及要終止的處理程序。 + + +重要性最低的處理程序會最先遭到終止,接著是重要性次低的處理程序,依此類推,視需要收回系統資源。 + +

      + +

      重要性階層共有五個層級。下方清單依照重要性的順序列出不同類型的處理程序 (第一個處理程序為「最重要」且會「最後終止」): + +

      + +
        +
      1. 前景處理程序 +

        這種處理程序是指使用者目前執行的工作所需的處理程序。針對下列任一情況,系統或將處理程序視為位於前景中: +

        + +
          +
        • 使用者正與其代管的 {@link android.app.Activity} 互動 (已呼叫 {@link +android.app.Activity} 的 {@link android.app.Activity#onResume onResume()} 方法)。 +
        • + +
        • 其代管的 {@link android.app.Service} 已繫結至正在與使用者互動的 Activity。 +
        • + +
        • 其代管的 {@link android.app.Service} 正「在前景」執行中 (服務已呼叫 {@link android.app.Service#startForeground startForeground()})。 + + +
        • 其代管的 {@link android.app.Service} 正在執行本身的其中一個生命週期回呼 ({@link android.app.Service#onCreate onCreate()}、{@link android.app.Service#onStart onStart()} 或 {@link android.app.Service#onDestroy onDestroy()})。 + +
        • + +
        • 其代管的 {@link android.content.BroadcastReceiver} 正在執行本身的 {@link +android.content.BroadcastReceiver#onReceive onReceive()} 方法。
        • +
        + +

        一般來說,在任何指定時間內只會有幾個前景處理程序存在。只有在記憶體過低而全都無法繼續執行時,才會採取這最後的手段來終止它們。 +在這種情況下,裝置通常已達到記憶體分頁處理狀態,因此必須終止一些前景處理程序,才能讓使用者介面保持回應。 + +

      2. + +
      3. 可見處理程序 +

        這種處理程序是指沒有任何前景元件的處理程序,但仍會影響使用者在螢幕上看見的內容。 +針對下列任一情況,系統會將處理程序視為可見: +

        + +
          +
        • 其代管的 {@link android.app.Activity} 不在前景中,但使用者仍可看見 (已呼叫它的 {@link android.app.Activity#onPause onPause()} 方法)。 +例如,如果前景 Activity 啟動的對話方塊允許在它身後看見先前的 Activity,就會發生這種情況。 + +
        • + +
        • 其代管的 {@link android.app.Service} 已繫結至可見 (或前景) Activity。 +
        • +
        + +

        可見處理程序相當重要而且不會遭到終止,除非系統為了讓所有前景處理程序維持執行而必須終止這類處理程序。 +

        +
      4. + +
      5. 服務處理程序 +

        這種處理程序是指正在執行已使用 {@link +android.content.Context#startService startService()} 方法啟動的服務的處理程序;此處理程序不屬於上述兩種較重要的類別。 +雖然服務處理程序是間接繫結至使用者所見內容,但通常會執行使用者重視的工作 (例如,在背景中播放音樂,或下載網路上的資料),因此除非記憶體不足,無法讓所有前景與可見處理程序保持執行,否則系統會讓這類處理程序繼續執行。 + + +

        +
      6. + +
      7. 背景處理程序 +

        這種處理程序會保留使用者目前看不見的 Activity (已呼叫 Activity 的 +{@link android.app.Activity#onStop onStop()} 方法)。這些處理程序會間接影響使用者體驗,且系統能隨時將其終止,藉此回收記憶體以供前景、可見或服務處理程序使用。 + + +通常會有許多背景處理程序處於執行中,因此會將它們放在 LRU (最近最少使用) 清單中,以確保在最後才將包含使用者最近最常見 Activity 的處理程序終止。 + +如果 Activity 正確實作其生命週期方法並儲存其目前狀態,終止其處理程序不會對使用者體驗造成任何可察覺的影響,原因是當使用者瀏覽回 Activity 時,該 Activity 會還原它的所有可見狀態。 + + +如要進一步瞭解如何儲存及還原狀態,請參閱 Activity。 +

        +
      8. + +
      9. 空白處理程序 +

        這種處理程序是指未保留任何使用中應用程式元件的處理程序。讓這類處理程序保持有效的唯一目的是將其用於快取,以改善元件下次執行時所需的啟動時間。 + +系統通常會終止這些處理程序,以平衡處理程序快取與底層核心快取之間的整體系統資源。 +

        +
      10. +
      + + +

      Android 會根據在處理程序中目前處於使用中的元件重要性,將處理程序盡量排在最高層級。 +例如,如果處理程序代管一項服務和一個可見 Activity,此處理程序的會排為可見處理程序,而不是服務處理程序。 +

      + +

      此外,還可能因為它有其他相依處理程序,而導致處理程序的排名提升:為另一個處理程序提供服務的處理程序,其排名絕不能低於為其提供服務的處理程序。 + +例如,如果處理程序 A 的內容供應程式為處理程序 B 中的用戶端提供服務,或處理程序 A 中的服務繫結至處理程序 B 中的元件,則系統至少會將處理程序 A 視為和處理程序 B 一樣重要。 + +

      + +

      由於執行服務的處理程序排名會比包含背景 Activity 的處理程序排名高,因此初始化長時間執行操作的 Activity 可能適合啟動該操作的服務,而不只是建立工作者執行緒 — 特別是該操作可能會比 Activity 持久。例如,將圖片上傳至網站 Activity 應該啟動要執行上傳的服務,如果使用者離開 Activity,上傳處理程序也能在背景中繼續進行。如果使用服務,不論 Activity 發生什麼情況,都可保證該操作的優先順序至少會是「服務處理程序」。 + + + + + +廣播接收器應該採用服務,而不是只在執行緒中放置時間耗用操作,也是相同的理由。 +

      + + + + +

      執行緒

      + +

      當應用程式啟動時,系統會為執行該應用程式建立一個稱為「主要」的執行緒。 +這個執行緒非常重要,原因是它負責將事件分配給適當的使用者介面小工具,包括繪製事件。 +您的應用程式與 Android UI 工具組中的元件 ({@link +android.widget} 和 {@link android.view} 中的元件) 互動時,也需要使用這個執行緒。 +因此,主要執行緒有時也稱為 UI 執行緒。 +

      + +

      系統「不會」為每個元件執行個體建立個別的執行緒。以相同處理程序執行的所有元件都是利用 UI 執行緒來具現化,而且都是由該執行緒分配系統呼叫的每個元件。 + +因此,回應系統回呼的方法 (例如報告使用者動作的 {@link android.view.View#onKeyDown onKeyDown()} 或生命週期回呼方法) 一律會以該處理程序的 UI 執行緒執行。 + +

      + +

      例如,當使用者輕觸畫面上的按鈕時,您應用程式的 UI 執行緒會將輕觸事件分配給小工具,接著設定其按下狀態並對事件佇列張貼失效要求。 + +UI 執行緒會將要求從佇列中移除,然後通知小工具應重新繪製本身。 +

      + +

      當您的應用程式密集執行作業以回應使用者互動時,除非您適當實作應用程式,否則這種單一執行緒模型會降低效能。 +具體來說,假設一切都在 UI 執行緒中進行,如果執行像是網路存取或資料庫查詢的長時間操作,將會封鎖整個 UI。當執行緒遭到封鎖時,會無法分配任何事件 (包括繪製事件)。 + + +從使用者的觀點來看,應用程式似乎閒置不動。 +更糟的是,如果 UI 執行緒遭到封鎖長達數秒 (目前約為 5 秒),就會向使用者顯示的「應用程式沒有回應」(ANR) 對話方塊。 + +使用者接著會決定結束您的應用程式,並可能因感到不滿而將其解除安裝。 +

      + +

      此外,Andoid UI 工具組「並非」安全執行緒,因此請勿透過工作者執行緒操縱 UI — 使用者介面的所有操縱作業都必須從 UI 執行緒來執行。 + +基於上述原因,Android 的單一執行緒模型只有兩項簡單規則:

      + +
        +
      1. 不要封鎖 UI 執行緒 +
      2. 不要從 UI 執行緒以外的位置存取 Android UI 工具組 +
      + +

      工作者執行緒

      + +

      因為上述的單一執行緒模型,所以不封鎖 UI 執行緒對於應用程式 UI 的回應能力至關重要。 +如果您有不會立即執行的操作,請務必以不同的執行緒 (「背景」或「工作者」執行緒) 執行這類操作。 + +

      + +

      例如,以下是從個別執行緒下載圖片並顯示在 {@link android.widget.ImageView} 的部分點擊接聽器程式碼: +

      + +
      +public void onClick(View v) {
      +    new Thread(new Runnable() {
      +        public void run() {
      +            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
      +            mImageView.setImageBitmap(b);
      +        }
      +    }).start();
      +}
      +
      + +

      首先,由於這會建立新的執行緒來處理網路操作,所以看起來似乎可以正常運作。 +不過,它違反單一執行緒模型的第二項規則:「不要從 UI 執行緒以外的位置存取 Android UI 工具組」— 這個範例修改工作者執行緒中的 {@link +android.widget.ImageView},而不是 UI 執行緒。 +這樣會產生未定義且預期外的行為,不但難以追蹤且耗費時間。 +

      + +

      為修正這個問題,Android 提供數種可從其他執行緒存取 UI 執行緒的方法。 +以下是可協助修正此問題的方法清單:

      + +
        +
      • {@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}
      • +
      • {@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}
      • +
      • {@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}
      • +
      + +

      例如,您可以使用 {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)} 方法來修正上述程式碼:

      + +
      +public void onClick(View v) {
      +    new Thread(new Runnable() {
      +        public void run() {
      +            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
      +            mImageView.post(new Runnable() {
      +                public void run() {
      +                    mImageView.setImageBitmap(bitmap);
      +                }
      +            });
      +        }
      +    }).start();
      +}
      +
      + +

      現在這個實作才是安全執行緒:雖然從不同的執行緒完成網路操作,但卻是從 UI 執行緒操縱 {@link android.widget.ImageView}。 +

      + +

      不過,隨著操作複雜度日益增加,這種程式碼也會變得複雜且難以維護。 +如要利用工作者執行緒處理更複雜的互動,您可能要考慮在工作者執行緒中使用 {@link android.os.Handler},以處理從 UI 執行緒傳送的訊息。 + +最佳解決方案也許是擴充 {@link android.os.AsyncTask} 類別,將必須與 UI 互動的工作者執行緒工作執行簡化。 +

      + + +

      使用 AsyncTask

      + +

      {@link android.os.AsyncTask} 可讓您透過使用者介面執行非同步工作。 +它會以工作者執行緒執行封鎖操作,然後將結果發行在 UI 執行緒,完全不需要您自行處理執行緒和/或處理常式。 +

      + +

      使用方法是您必須要有子類別 {@link android.os.AsyncTask},並實作以背景執行緒集區執行的 {@link +android.os.AsyncTask#doInBackground doInBackground()} 回呼方法。 +如要更新您的 UI,請實作 {@link +android.os.AsyncTask#onPostExecute onPostExecute()} 來傳送 {@link +android.os.AsyncTask#doInBackground doInBackground()} 的結果,然後以 UI 執行緒執行,如此您才能安全地更新 UI。 +接著,您可以從 UI 執行緒呼叫 {@link android.os.AsyncTask#execute execute()} 來執行該工作。 +

      + +

      例如,您可以使用 {@link android.os.AsyncTask} 這種方法實作先前的範例: +

      + +
      +public void onClick(View v) {
      +    new DownloadImageTask().execute("http://example.com/image.png");
      +}
      +
      +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
      +    /** The system calls this to perform work in a worker thread and
      +      * delivers it the parameters given to AsyncTask.execute() */
      +    protected Bitmap doInBackground(String... urls) {
      +        return loadImageFromNetwork(urls[0]);
      +    }
      +    
      +    /** The system calls this to perform work in the UI thread and delivers
      +      * the result from doInBackground() */
      +    protected void onPostExecute(Bitmap result) {
      +        mImageView.setImageBitmap(result);
      +    }
      +}
      +
      + +

      現在 UI 很安全,而且程式碼變得更簡單,這是因為它將工作分成兩部分,一部分應在工作者執行緒上完成,而另一部分應在 UI 執行緒上完成。 +

      + +

      建議您參閱 {@link android.os.AsyncTask} 參考資料,以全面瞭解如何使用此類別;以下是其如何運作的快速總覽: +

      + +
        +
      • 您可以使用泛型來指定參數類型、進度值以及工作的最終值 +
      • +
      • {@link android.os.AsyncTask#doInBackground doInBackground()} 方法會在工作者執行緒中自動執行 +
      • +
      • {@link android.os.AsyncTask#onPreExecute onPreExecute()}、{@link +android.os.AsyncTask#onPostExecute onPostExecute()} 和 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} 全都是透過 UI 執行緒呼叫。
      • +
      • {@link android.os.AsyncTask#doInBackground doInBackground()} 傳回的值會傳送至 +{@link android.os.AsyncTask#onPostExecute onPostExecute()}
      • +
      • 您隨時都可用 {@link +android.os.AsyncTask#doInBackground doInBackground()} 呼叫 {@link android.os.AsyncTask#publishProgress publishProgress()},以便透過 UI 執行緒執行 {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()}。
      • +
      • 您可以從任何執行緒隨時取消工作
      • +
      + +

      注意:使用工作者執行緒可能會遇到的另一個問題是,由於執行階段設定變更 (例如使用者變更螢幕方向時) 使您的 Activity 意外重新啟動,而可能會終止您的工作者站執行緒。 + +請參閱 Shelves 範例應用程式的原始程式碼,以瞭解如何在遇到其中一種重新啟動情況時保留您的工作,以及如何在 Activity 遭到終止時適當取消該工作。 + +

      + + +

      安全執行緒方法

      + +

      在某些情況下,您實作的方法可能是從多個執行緒呼叫,因此務必要撰寫成安全執行緒。 +

      + +

      這種情況主要發生在可從遠端呼叫的方法,例如已繫結服務中的方法。當在源自執行 {@link android.os.IBinder IBinder} 的相同處理程序中實作 {@link android.os.IBinder} 方法上的呼叫時,該方法是以呼叫端的執行緒執行。 + + + +不過,當呼叫源自另一個處理程序時,會從系統在相同處理程序中當成 {@link android.os.IBinder +IBinder} (未以處理程序的 UI 執行緒執行) 維護的執行緒集區,以選擇的執行緒來執行該方法。例如,雖然服務的 +{@link android.app.Service#onBind onBind()} 方法可從服務處理程序的 UI 執行緒呼叫,但以 {@link android.app.Service#onBind +onBind()} 傳回的物件實作的方法會從集區中的執行緒呼叫。 + +由於服務能有多個用戶端,同時也能有多個集區執行緒採用相同的 +{@link android.os.IBinder IBinder} 方法。因此 {@link android.os.IBinder +IBinder} 方法必須實作為安全執行緒。

      + +

      同樣地,內容供應程式能接收源自其他處理程序的資料要求。 +雖然 {@link android.content.ContentResolver} 與 {@link android.content.ContentProvider} +類別會隱藏如何管理處理程序間通訊的詳細資料,但回應這些要求的 {@link +android.content.ContentProvider} 方法 — {@link +android.content.ContentProvider#query query()}、{@link android.content.ContentProvider#insert +insert()}、{@link android.content.ContentProvider#delete delete()}、{@link +android.content.ContentProvider#update update()} 和 {@link android.content.ContentProvider#getType +getType()} — 是在內容供應程式的處理程序中從執行緒集區呼叫,而不是該處理程序的 UI 執行緒。 +由於可能會同時有任意數目的執行緒呼叫這些方法,因此它們也要實作為安全執行緒。 +

      + + +

      處理程序間通訊

      + +

      Android 提供一項使用遠端程序呼叫 (RPC) 進行處理程序間通訊 (IPC) 的機制,RPC 是指 (以另一個處理程序) 從遠端執行由 Activity 或其他應用程式元件呼叫的方法,再加上要傳回給呼叫端的任何結果。 + + +這需要將方法呼叫與其資料分解成作業系統能夠瞭解的程度,將它從本機處理程序與位址空間傳輸到遠端處理程序與位址空間,然後再重新組合和重新實作呼叫。 + +接著,再以相反的方向傳輸傳回值。 +Android 提供進行這些 IPC 交易的所有程式碼,讓您可以專心定義及實作 RPC 程式設計介面。 +

      + +

      如要執行 IPC,您的應用程式必須使用 {@link +android.content.Context#bindService bindService()} 來繫結至服務。如需詳細資訊,請參閱服務開發人員指南。

      + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/recents.jd b/docs/html-intl/intl/zh-tw/guide/components/recents.jd new file mode 100644 index 0000000000000000000000000000000000000000..d56c12c0e87bbc8a00d91f4b1a243337a3511e04 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=總覽畫面 +page.tags="recents","overview" + +@jd:body + +
      +
      + +

      本文件內容

      +
        +
      1. 新增工作到總覽畫面 +
          +
        1. 使用意圖旗標來新增工作
        2. +
        3. 使用 Activity 屬性來新增工作
        4. +
        +
      2. +
      3. 移除工作 +
          +
        1. 使用 AppTask 類別來移除工作
        2. +
        3. 保留結束的工作
        4. +
        +
      4. +
      + +

      重要類別

      +
        +
      1. {@link android.app.ActivityManager.AppTask}
      2. +
      3. {@link android.content.Intent}
      4. +
      + +

      程式碼範例

      +
        +
      1. 以文件為中心的應用程式
      2. +
      + +
      +
      + +

      總覽畫面 (也被稱為最近畫面、最近工作清單,或最近的應用程式) 是系統層級的 UI,可以列出最近存取的 Activity工作。 + +使用者可以透過清單導覽並選擇要繼續的工作,或是使用者可以滑動的方式從清單移除工作。 + +使用 Android 5.0 版本 (API 級別 21),相同 Activity (包含不同文件) 的多個執行個體可以在總覽畫面中顯示為工作。 +例如:對多份 Google 文件,Google 雲端硬碟能讓每份文件都對應一個工作。 +每份文件在總覽畫面中都會顯示為工作。 +

      + + +

      圖 1.總覽畫面會顯示三份 Google 雲端硬碟文件,每份都分別顯示為一項工作。 +

      + +

      一般而言,您應該允許系統定義如何在總覽畫面中呈現工作與 Activity,而且不需要修改此行為。 +然而,您的應用程式可以決定要如何與在何時於總覽畫面中顯示 Activity。 + +{@link android.app.ActivityManager.AppTask} 類別讓您可以管理工作,而 {@link android.content.Intent} 類別的 Activity 旗標可以讓您指定何時從總覽畫面新增或移除 Activity。 + +此外, +<activity> 屬性可以讓您設定宣示說明中的行為。

      + +

      新增工作到總覽畫面

      + +

      使用 {@link android.content.Intent} 類別的旗標可以新增工作,該工作針對何時與如何在總覽畫面中開啟或重新開啟文件,可給予更多控制權。 +當您使用 +<activity>屬性時,您可以選擇永遠在新工作開啟文件或對文件重複使用現有工作。 + +

      + +

      使用意圖旗標來新增工作

      + +

      當您建立 Activity 的新文件時,您可以呼叫 + {@link android.app.ActivityManager.AppTask} 類別的 {@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()}方法,傳送啟動 Activity 的意圖至新文件。 + +如要插入邏輯中斷,讓系統可以將您的 Activity 當作總覽視窗中的新工作,傳送啟動 Activity 的 {@link android.content.Intent}其 {@link android.content.Intent#addFlags(int) addFlags()} 方法中的 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標。 + + +

      + +

      注意:{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標會取代 {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET} 旗標,後者已從 Android 5.0 (API 級別 21) 起失效。 + +

      + +

      如果您在建立新文件時設定 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,則系統永遠會在建立新工作時也在根目錄建立目標 Activity。此設定允許可以在一個以上的工作中開啟相同的文件。 + +下列程式碼示範主要 Activity 如何處理: +

      + +

      +DocumentCentricActivity.java

      +
      +public void createNewDocument(View view) {
      +      final Intent newDocumentIntent = newDocumentIntent();
      +      if (useMultipleTasks) {
      +          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
      +      }
      +      startActivity(newDocumentIntent);
      +  }
      +
      +  private Intent newDocumentIntent() {
      +      boolean useMultipleTasks = mCheckbox.isChecked();
      +      final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
      +      newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
      +      newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
      +      return newDocumentIntent;
      +  }
      +
      +  private static int incrementAndGet() {
      +      Log.d(TAG, "incrementAndGet(): " + mDocumentCounter);
      +      return mDocumentCounter++;
      +  }
      +}
      +
      + +

      注意:與 {@code FLAG_ACTIVITY_NEW_DOCUMENT} 旗標一起啟動的 Activity 務必要在宣示說明中設定 {@code android:launchMode="standard"} 屬性值 (預設)。 + +

      + +

      當主要 Activity 啟動新的 Activity 時,系統會透過現有工作搜尋其意圖和 Activity 意圖元件名稱及意圖資料相同的 Activity。 +如果找不到工作,或意圖已包含 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,則會建立新的工作並使用 Activity 做為其根目錄。 + +如果找到工作,則會將該工作帶到前面並傳送新的意圖到 {@link android.app.Activity#onNewIntent onNewIntent()}。 +新的 Activity 會取得意圖並在總覽視窗中建立新的文件,如下列範例所示: + +

      + +

      NewDocumentActivity.java +

      +
      +@Override
      +protected void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +    setContentView(R.layout.activity_new_document);
      +    mDocumentCount = getIntent()
      +            .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
      +    mDocumentCounterTextView = (TextView) findViewById(
      +            R.id.hello_new_document_text_view);
      +    setDocumentCounterText(R.string.hello_new_document_counter);
      +}
      +
      +@Override
      +protected void onNewIntent(Intent intent) {
      +    super.onNewIntent(intent);
      +    /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
      +    is reused to create a new document.
      +     */
      +    setDocumentCounterText(R.string.reusing_document_counter);
      +}
      +
      + + +

      使用 Activity 屬性來新增工作

      + +

      Activity 也可以在宣示說明中指定為永遠啟動新工作,這可透過使用<activity> 屬性的 +{@code android:documentLaunchMode} 達成。 + +此屬性有四個值,在使用者使用應用程式開啟文件時,會產生下列效果: +

      + +
      +
      "{@code intoExisting}"
      +
      Activity 會對文件重複使用現有的工作。設定 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 旗標,但「不」設定 +{@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標,會與上述使用意圖旗標來新增工作達到相同效果。 +
      + +
      "{@code always}"
      +
      Activity 會為文件建立新的工作,就算文件已開始也會建立新的工作。使用此值與設定 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 與 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標會達到相同效果。 + +
      + +
      "{@code none”}"
      +
      Activity 不會為文件建立新的工作。總覽視窗會將 Activity 當作預設:會顯示應用程式的單一工作,該工作會從使用者最後呼叫的 Activity 繼續。 + +
      + +
      "{@code never}"
      +
      Activity 不會為文件建立新的工作。設定此值會覆寫 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} 與 {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} 旗標的行為,如果任一個已於意圖中設定,總覽視窗會顯示應用程式的單一工作,該工作會從使用者最後呼叫的 Activity 繼續。 + + + +
      +
      + +

      注意:如果值不是 {@code none} 與 {@code never},則 Activity 必須使用 {@code launchMode="standard"} 定義。 +如果沒有指定此屬性,則會使用 +{@code documentLaunchMode="none"}。

      + +

      移除工作

      + +

      依照預設,當 Activity 結束時,會自動將文件工作從總覽畫面移除。 +您可以使用 {@link android.app.ActivityManager.AppTask} 類別,搭配 {@link android.content.Intent} 旗標或 +<activity> 屬性,來覆寫此行為。 +

      + +

      您可以完全從總覽畫面排除工作,設定方式為將 +<activity> 屬性的 +{@code android:excludeFromRecents} 設為 {@code true}。 +

      + +

      您可以設定應用程式可以納入總覽視窗的最大工作數目,方法是對 <activity> 屬性 {@code android:maxRecents} 設定整數值。 + + +預設值為 16。當達到工作的最大值時,會從總覽視窗移除最近最少使用的工作。 +{@code android:maxRecents} 的最大值是 50 (在低記憶體裝置上是 25);數值不可以小於 1。 +

      + +

      使用 AppTask 類別來移除工作

      + +

      在總覽畫面建立新工作的 Activity 中,您可以指定何時移除工作與結束所有相關 Activity,方法為呼叫{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 方法。 + +

      + +

      NewDocumentActivity.java +

      +
      +public void onRemoveFromRecents(View view) {
      +    // The document is no longer needed; remove its task.
      +    finishAndRemoveTask();
      +}
      +
      + +

      注意:使用 +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} 方法覆寫 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 標籤的使用,會在下面討論。 + +

      + +

      保留結束的工作

      + +

      如果您要保留總覽畫面中的工作 (就算其 Activity 已結束), 方法為傳送啟動 Activity 的意圖其 {@link android.content.Intent#addFlags(int) addFlags()} 方法中的 +{@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 旗標。 +

      + +

      DocumentCentricActivity.java +

      +
      +private Intent newDocumentIntent() {
      +    final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
      +    newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
      +      android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
      +    newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet());
      +    return newDocumentIntent;
      +}
      +
      + +

      如要達到相同效果,可以設定 +<activity> 屬性的 +{@code android:autoRemoveFromRecents} 為 {@code false}。 +對文件 Activity 的預設值為 {@code true},對定期 Activity 的預設值則為 {@code false}。 +如同之前的討論,使用此屬性可以覆寫 {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} 旗標。 +

      + + + + + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/services.jd b/docs/html-intl/intl/zh-tw/guide/components/services.jd new file mode 100644 index 0000000000000000000000000000000000000000..d6a440d88dd6c8e63b794cc7d7d7ee7acc015b2d --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=服務 +@jd:body + + + + +

      {@link android.app.Service} 是可以在背景中長時間執行操作的應用程式元件,且不提供使用者介面。 +也是另一個可以啟動服務的應用程式元件,就算使用者切換至其他應用程式,也會繼續在背景中執行。 + +此外,元件也可以繫結至服務,以便與其互動,甚至執行處理程序間通訊 (IPC)。 +例如,服務可能處理網路交易、播放音樂、執行檔案輸入/輸出或與內容供應程式互動,這些都可以從背景執行。 + +

      + +

      服務基本上可以採取兩種形式:

      + +
      +
      啟動
      +
      服務「啟動」表示應用程式元件 (例如 Activity) 透過呼叫 +{@link android.content.Context#startService startService()} 來啟動服務。一旦啟動,服務可以無限次數地在背景中執行,就算啟動服務的元件已終結也不會影響。 +通常,已啟動的服務會執行單一操作且不會將結果傳回呼叫端。例如,服務可能會透過網路下載或上傳檔案。 + +當操作完成時,服務應該會自行終結。 +
      +
      繫結
      +
      服務「繫結」表示應用程式元件透過呼叫 {@link +android.content.Context#bindService bindService()} 來繫結至服務。已繫結的服務提供主從式介面,讓元件可以與服務互動、傳送要求、取得結果,甚至可以使用處理程序間通訊 (IPC) 來跨程序達到目的。 + +已繫結的服務伴隨另一個繫結至服務的應用程式元件執行。 +多重元件可以一次繫結至服務,但是當所有元件都取消繫結時,服務就會被終結。 +
      +
      + +

      雖然此文件通常分別討論服務的這兩種類別,但您的服務兩種方式都可以執行 — 可以啟動服務 (無限次數地執行) 也允許繫結。 +這僅與您是否實作兩種回呼方法而定:{@link +android.app.Service#onStartCommand onStartCommand()} 允許元件啟動服務,而 {@link +android.app.Service#onBind onBind()} 則允許繫結。 +

      + +

      不論您的應用程式是否啟動、繫結或兩者都有,任何應用程式元件可以使用服務 (就算來自不同的應用程式),與任何元件可以使用 Activity 的方式相同 — 使用 {@link android.content.Intent} 啟動服務。 + +然而,您可以宣告服務為私用、位於宣示說明檔案之中,與封鎖從其他應用程式存取。 +如需更多討論資訊,請詳見在宣示說明中宣告服務。 + +

      + +

      注意:服務會在其託管程序的主執行緒中執行 — 服務不會建立自己的執行緒且不會在另外的程序中執行 (除非您另行指定)。 + +這代表如果您的服務即將從事任何 CPU 密集的作業或封鎖操作 (如播放 MP3 或連線網路),您應該在服務中建立新的執行緒來執行這些工作。 + +透過使用另外的執行緒,您會降低應用程式不回應 (ANR) 錯誤的風險,且應用程式的主執行緒仍可以專務於使用者與您的 Activity 互動。 + +

      + + +

      基本概念

      + + + +

      如要建立服務,您必須建立 {@link android.app.Service} 的子類別 (或是其現有的子類別之一)。 +在您的實作中,如果可以,您必須覆寫某些回呼方法,這些方法負責處理服務生命週期的關鍵層面,並提供元件繫結至服務的機制。 + +您應該覆寫的最重要回呼方法為:

      + +
      +
      {@link android.app.Service#onStartCommand onStartCommand()}
      +
      當另一個元件 (如 Activity) 透過呼叫 {@link android.content.Context#startService +startService()} 來要求啟動服務時,系統會呼叫此方法。 +一旦執行此方法,服務會被啟動且可以無限次數地在背景中執行。 +如果您實作此方法,當作業完成時,您必須負責停止服務,方式為呼叫 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()}。 +(如果您只想要提供繫結功能,您不需要實作此方法)。 +
      +
      {@link android.app.Service#onBind onBind()}
      +
      當其他元件想要與服務 (如執行 RPC) 繫結時,系統會透過呼叫 {@link android.content.Context#bindService +bindService()} 來呼叫此方法。 +在您實作此方法時,透過傳回 {@link android.os.IBinder},您必須提供用戶端可用來與服務通訊的介面。 +您必須永遠實作此方法,但如果您不想允許繫結,則您應該傳回 null。 +
      +
      {@link android.app.Service#onCreate()}
      +
      當第一次建立服務時,系統會呼叫此方法來執行一次性的設定程序 (在其呼叫 {@link android.app.Service#onStartCommand onStartCommand()} 或 +{@link android.app.Service#onBind onBind()} 之前)。 +如果已經執行服務,則不會呼叫此方法。 +
      +
      {@link android.app.Service#onDestroy()}
      +
      當不再使用服務且正在終結服務時,系統會呼叫此方法。您的服務應該實作此方法來清除任何如執行緒、註冊的接聽器,接收器等資源。 + +這會是服務接收到的最後呼叫。
      +
      + +

      如果透過呼叫 {@link +android.content.Context#startService startService()} (這是呼叫至 {@link +android.app.Service#onStartCommand onStartCommand()} 的結果) 讓元件啟動服務,則服務仍會保持執行狀態,直到使用 {@link android.app.Service#stopSelf()} 讓服務自行停止或透過呼叫 +{@link android.content.Context#stopService stopService()} 由其他元件停止服務。 +

      + +

      如果元件呼叫 +{@link android.content.Context#bindService bindService()} 來建立服務 (且「沒有」呼叫 {@link +android.app.Service#onStartCommand onStartCommand()}),則服務會伴隨繫結至服務的元件執行。 +一旦服務與所有用戶端解除繫結時,系統會終結服務。 +

      + +

      只有在記憶體不足且必須復原擁有使用者焦點的 Activity 其系統資源時,Android 系統才會強制停止服務。 +如果服務已繫結至擁有使用者焦點的 Activity,則該服務不太容易被終止,且如果服務被宣告為在前景中執行 (稍後會討論),則該服務幾乎不會被終止。 +否則,如果長時間執行於前景中啟動的服務,則系統會隨時間降低該服務在背景工作清單中的位置,且該服務會很容易被終止 — 如果已啟動您的服務,則您必須設計讓系統可以完美地處理重新啟動。 + + + +如果系統終止服務,則系統會在可以再度取得資源時立即重新啟動服務 (雖然這也會依據您從 {@link +android.app.Service#onStartCommand onStartCommand()} 傳回的值而有所不同,稍後會討論)。 +如需更多有關系統何時可能終結服務的資訊,請參閱程序與執行緒文件。 + +

      + +

      在下列小節,您將看到您如何可以建立每種類型的服務與如何從不同應用程式元件使用。 +

      + + + +

      在宣示說明中宣告服務

      + +

      就如同 Activity (與其他元件),您必須在您應用程式的宣示說明檔案中宣告所有服務。 +

      + +

      如要宣告您的服務,可以新增 {@code <service>} 元素為 {@code <application>}元素的子項。 + +例如:

      + +
      +<manifest ... >
      +  ...
      +  <application ... >
      +      <service android:name=".ExampleService" />
      +      ...
      +  </application>
      +</manifest>
      +
      + +

      如需更多有關在宣示說明中宣告您服務的行系資訊,請參閱 {@code <service>} 元素。 +

      + +

      您還可以在 {@code <service>} 元素中包含其他屬性,用來定義如啟動服務所需權限的屬性,與服務應該執行的程序。 + +{@code android:name} 屬性是唯一必備的屬性 — 用來指定服務的類別名稱。 +一旦您發佈應用程式,您就不應該變更此名稱,因為如果這樣做,會由於依賴明確的意圖來啟動或繫結服務 (閱讀部落格貼文、不能變更的事項),造成程式碼中斷的風險。 + + + + +

      如要確認您的應用程式是安全的,當啟動或繫結您的 {@link android.app.Service} 時,永遠使用明確意圖,且不要宣告服務的意圖篩選器。 +如果允許一定程度的模糊來決定啟動的服務是重要的,您可以提供您的服務意圖篩選器,並從 {@link +android.content.Intent} 排除元件名稱,但您仍需使用 {@link +android.content.Intent#setPackage setPackage()} 設定意圖的封裝,這會提供目標服務足夠明確、避免含糊的指示。 + + +

      + +

      此外,您可以確保只有您的應用程式可以使用您的服務,方法為包含 {@code android:exported} 屬性並將其設定為 {@code "false"}。 + +這可以有效地避免其他應用程式啟動您的服務,就算使用明確意圖時也是如此。 +

      + + + + +

      建立已啟動的服務

      + +

      已啟動的服務是由其他應用程式呼叫 {@link +android.content.Context#startService startService()} 所啟動的,由呼叫至服務的 +{@link android.app.Service#onStartCommand onStartCommand()} 方法所導致。

      + +

      當服務啟動,其生命週期與啟動服務的元件無關,且服務可以無限次數地在背景中執行,就算啟動服務的元件已終結也不會影響。 + +因此,當服務的工作藉由呼叫 {@link android.app.Service#stopSelf stopSelf()} 而完成時,服務應該會自行停止,或藉由呼叫 {@link android.content.Context#stopService stopService()},其他元件也可以停止服務。 + +

      + +

      如 Activity 等應用程式元件可以透過呼叫 {@link +android.content.Context#startService startService()} 與傳送用來指定服務及包含服務所要使用任何資料的 +{@link android.content.Intent},來啟動服務。服務會接收 {@link android.app.Service#onStartCommand +onStartCommand()} 方法中的此 {@link android.content.Intent}。 +

      + +

      例如,假設 Activity 需要一些資料到線上資料庫。藉由傳送意圖至 {@link +android.content.Context#startService startService()},Activity 可以啟動伴隨服務並傳送要儲存的資料。 +服務會接收 {@link +android.app.Service#onStartCommand onStartCommand()} 中的意圖,連線到網際網路,並執行資料庫交易。 +當操作完成時,服務應該會自行終結。 +

      + +

      注意:服務會在與應用程式相同的程序中執行,服務會在該程序中被宣告,且依照預設,會位於該應用程式的主執行緒之中。 +所以,在使用者與來自相同應用程式的 Activity 互動時,如果您的服務執行密集的操作或封鎖操作,則該服務將會降低 Activity 的效能。 + +要避免影響應用程式的效能,您應該在服務之中啟動新的執行緒。 +

      + +

      傳統上,有兩種類別可以延伸為建立已啟動的服務:

      +
      +
      {@link android.app.Service}
      +
      這是所有服務的基本類別。當您延伸此類別時,建立用來執行所有服務工作的新執行緒是非常重要的,因為依照預設,服務會使用您應用程式的主執行緒,這會降低任何您應用程式所執行 Activity 的效能。 + + +
      +
      {@link android.app.IntentService}
      +
      這是 {@link android.app.Service} 的子類別,會使用 worker 執行緒來處理所有的啟動要求,一次一個。 +如果您不需要您的服務同時處理多個要求,則這是最佳的選項。 +您唯一要做的就是實作 {@link +android.app.IntentService#onHandleIntent onHandleIntent()},這會接收每個起始要求的意圖 ,所以您可以在背景執行。 +
      +
      + +

      下列小節說明如何可以使用這些類別之一來實作您的服務。 +

      + + +

      延伸 IntentService 類別

      + +

      因為大多數的已啟動服務不需要同時處理多個要求(多重執行緒策略事實上是危險的),如果您使用 {@link android.app.IntentService} 類別來實作您的服務可能是最佳的方法。 + +

      + +

      {@link android.app.IntentService} 會達到下列目的:

      + +
        +
      • 建立預設的 worker 執行緒,該執行緒會執行所有傳送至 {@link +android.app.Service#onStartCommand onStartCommand()} 的意圖,並與您應用程式的主執行緒有所分別。 +
      • +
      • 建立工作佇列,該佇列會一次傳送一個意圖到您的 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 實作,所以您絕對不需要擔心多重執行緒的問題。 +
      • +
      • 在所有啟動要求都已處理後,停止服務,這樣您永遠不需要呼叫 +{@link android.app.Service#stopSelf}。
      • +
      • 提供傳回 null 的 {@link android.app.IntentService#onBind onBind()} 其預設實作。 +
      • +
      • 提供傳送意圖至工作佇列的 {@link android.app.IntentService#onStartCommand +onStartCommand()} 其預設實作,然後傳送至您的 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 實作。
      • +
      + +

      所有這一切都說明了您要做的就是實作 {@link +android.app.IntentService#onHandleIntent onHandleIntent()} 來完成用戶端提供的工作。 +(雖然,您也需要提供小型建構函式給服務)。

      + +

      下列是 {@link android.app.IntentService} 實作的範例:

      + +
      +public class HelloIntentService extends IntentService {
      +
      +  /**
      +   * A constructor is required, and must call the super {@link android.app.IntentService#IntentService}
      +   * constructor with a name for the worker thread.
      +   */
      +  public HelloIntentService() {
      +      super("HelloIntentService");
      +  }
      +
      +  /**
      +   * The IntentService calls this method from the default worker thread with
      +   * the intent that started the service. When this method returns, IntentService
      +   * stops the service, as appropriate.
      +   */
      +  @Override
      +  protected void onHandleIntent(Intent intent) {
      +      // Normally we would do some work here, like download a file.
      +      // For our sample, we just sleep for 5 seconds.
      +      long endTime = System.currentTimeMillis() + 5*1000;
      +      while (System.currentTimeMillis() < endTime) {
      +          synchronized (this) {
      +              try {
      +                  wait(endTime - System.currentTimeMillis());
      +              } catch (Exception e) {
      +              }
      +          }
      +      }
      +  }
      +}
      +
      + +

      您需要的是:{@link +android.app.IntentService#onHandleIntent onHandleIntent()} 的建構函式與實作。

      + +

      如果您決定也要覆寫其他回呼方法,如 {@link +android.app.IntentService#onCreate onCreate()}、{@link +android.app.IntentService#onStartCommand onStartCommand()} 或 {@link +android.app.IntentService#onDestroy onDestroy()},請確認呼叫超級實作,這樣 +{@link android.app.IntentService} 才可以適當處理 worker 執行緒的生命。

      + +

      例如,{@link android.app.IntentService#onStartCommand onStartCommand()} 必須傳回預設的實作 (這就是意圖如何被傳送至 {@link +android.app.IntentService#onHandleIntent onHandleIntent()}): +

      + +
      +@Override
      +public int onStartCommand(Intent intent, int flags, int startId) {
      +    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
      +    return super.onStartCommand(intent,flags,startId);
      +}
      +
      + +

      除了 {@link android.app.IntentService#onHandleIntent onHandleIntent()},您不需要呼叫超級類別的唯一方法就是 {@link android.app.IntentService#onBind +onBind()} (但只有在您的服務允許繫結時才需要實作)。 +

      + +

      在下一節,您會看到在延伸基本 {@link android.app.Service} 類別時,如何實作同類的服務,這需要寫更多程式碼,但是如果您需要同時處理多個啟動要求時,這可能是比較合適的處理方式。 + +

      + + +

      延伸服務類別

      + +

      如同您在前一節看到的,使用 {@link android.app.IntentService} 可讓您已啟動服務的實作變得非常容易。 +然而,如果您要求您的服務執行多重執行緒 (取代透過工作佇列處理啟動要求),則您可以延伸 {@link android.app.Service} 類別來處理每個意圖。 + +

      + +

      為了對比之用,下列程式碼範例是 {@link +android.app.Service} 類別的實作,該類別執行與上述使用 {@link +android.app.IntentService} 範例相同的工作。也就是,對每個啟動要求,會使用 worker 執行緒來執行工作,且一次只處理一個要求。 +

      + +
      +public class HelloService extends Service {
      +  private Looper mServiceLooper;
      +  private ServiceHandler mServiceHandler;
      +
      +  // Handler that receives messages from the thread
      +  private final class ServiceHandler extends Handler {
      +      public ServiceHandler(Looper looper) {
      +          super(looper);
      +      }
      +      @Override
      +      public void handleMessage(Message msg) {
      +          // Normally we would do some work here, like download a file.
      +          // For our sample, we just sleep for 5 seconds.
      +          long endTime = System.currentTimeMillis() + 5*1000;
      +          while (System.currentTimeMillis() < endTime) {
      +              synchronized (this) {
      +                  try {
      +                      wait(endTime - System.currentTimeMillis());
      +                  } catch (Exception e) {
      +                  }
      +              }
      +          }
      +          // Stop the service using the startId, so that we don't stop
      +          // the service in the middle of handling another job
      +          stopSelf(msg.arg1);
      +      }
      +  }
      +
      +  @Override
      +  public void onCreate() {
      +    // Start up the thread running the service.  Note that we create a
      +    // separate thread because the service normally runs in the process's
      +    // main thread, which we don't want to block.  We also make it
      +    // background priority so CPU-intensive work will not disrupt our UI.
      +    HandlerThread thread = new HandlerThread("ServiceStartArguments",
      +            Process.THREAD_PRIORITY_BACKGROUND);
      +    thread.start();
      +
      +    // Get the HandlerThread's Looper and use it for our Handler
      +    mServiceLooper = thread.getLooper();
      +    mServiceHandler = new ServiceHandler(mServiceLooper);
      +  }
      +
      +  @Override
      +  public int onStartCommand(Intent intent, int flags, int startId) {
      +      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
      +
      +      // For each start request, send a message to start a job and deliver the
      +      // start ID so we know which request we're stopping when we finish the job
      +      Message msg = mServiceHandler.obtainMessage();
      +      msg.arg1 = startId;
      +      mServiceHandler.sendMessage(msg);
      +
      +      // If we get killed, after returning from here, restart
      +      return START_STICKY;
      +  }
      +
      +  @Override
      +  public IBinder onBind(Intent intent) {
      +      // We don't provide binding, so return null
      +      return null;
      +  }
      +
      +  @Override
      +  public void onDestroy() {
      +    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
      +  }
      +}
      +
      + +

      如同您看到的,比起使用 {@link android.app.IntentService},這需要花更多功夫。

      + +

      然而,因為您自行處理每個對 {@link android.app.Service#onStartCommand +onStartCommand()} 的呼叫,您可以同時執行多個要求。那不是這個範例要做的,但如果那是您想要的,那麼您可以針對每個要求建立新的執行緒,並立即執行 (代替等待之前的要求結束)。 + +

      + +

      請注意:{@link android.app.Service#onStartCommand onStartCommand()} 方法必須傳回整數。 +整數是一個數值,說明在系統終止服務的事件中,系統該如何繼續服務 (如上述的討論,雖然您可以修改,但 {@link +android.app.IntentService} 的預設實作會替您處理)。 +從 +{@link android.app.Service#onStartCommand onStartCommand()} 傳回的值必須是下列常數之一: +

      + +
      +
      {@link android.app.Service#START_NOT_STICKY}
      +
      如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,除非有待決的意圖要傳送,否則請「不要」建立服務。 +在非必要時與應用程式可以簡單地重新啟動任何未完成的工作時,這是避免執行服務的最安全方式。 +
      +
      {@link android.app.Service#START_STICKY}
      +
      如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,請重新建立服務並呼叫 {@link +android.app.Service#onStartCommand onStartCommand()},但「不要」重新傳送最後的意圖。 +相反地,除非有待決的意圖要啟動服務傳送,否則系統會使用 null 意圖呼叫 {@link android.app.Service#onStartCommand onStartCommand()},如果有待決的意圖要啟動服務,則會傳送那些意圖。 + +這適用於媒體播放程式 (或類似服務) 這類不執行命令,但可以無次數限制執行與等待工作的服務。 +
      +
      {@link android.app.Service#START_REDELIVER_INTENT}
      +
      如果系統在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後終止服務,請重新建立服務並使用傳送至服務的最後意圖呼叫 {@link +android.app.Service#onStartCommand onStartCommand()}。 +任何待決的意圖會反過來由後往前傳送。這適用的服務為主動執行如下載檔案等應該立即繼續的工作。 +
      +
      +

      如需有關這些傳回值的詳細資訊,請參閱每個常數的連結參考文件。 +

      + + + +

      啟動服務

      + +

      您可以透過傳送 +{@link android.content.Intent} (指定要啟動的服務) 到 {@link +android.content.Context#startService startService()},從 Activity 或其他應用程式元件來啟動服務。Android 系統會呼叫服務的 {@link +android.app.Service#onStartCommand onStartCommand()} 方法並傳送 {@link +android.content.Intent} 給該方法。(絕對不要直接呼叫 {@link android.app.Service#onStartCommand +onStartCommand()})。

      + +

      例如,使用明確意圖並搭配 {@link android.content.Context#startService +startService()},Activity 可以啟動前小節範例中的服務 ({@code +HelloSevice}):

      + +
      +Intent intent = new Intent(this, HelloService.class);
      +startService(intent);
      +
      + +

      {@link android.content.Context#startService startService()} 方法會立即回傳且Android 系統會呼叫服務的 {@link android.app.Service#onStartCommand +onStartCommand()} 方法。 +如果尚未開始執行服務,系統會先呼叫 {@link +android.app.Service#onCreate onCreate()},然後呼叫 {@link android.app.Service#onStartCommand +onStartCommand()}。

      + +

      如果服務也不提供繫結,則與意圖一同傳送的 {@link +android.content.Context#startService startService()} 是在應用程式元件與服務間通訊的唯一模式。 +然而,如果您想要服務將結果送回,則啟動服務的用戶端會針對廣播建立 {@link android.app.PendingIntent}(搭配 {@link android.app.PendingIntent#getBroadcast getBroadcast()}) 並將其傳送至服務,該服務在啟動服務的 {@link android.content.Intent} 之中。 + + +然後服務就可使用廣播來傳送結果。 +

      + +

      多個啟動服務的要求會導致多個相關呼叫至服務的 +{@link android.app.Service#onStartCommand onStartCommand()}。然而,只允許一個要求可以停止服務 (使用 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()})。 +

      + + +

      停止服務

      + +

      已啟動的服務必須管理本身的生命週期。也就是,系統不會停止或終結服務,除非系統必須復原系統記憶體,而服務會在 {@link android.app.Service#onStartCommand onStartCommand()} 回傳後繼續執行。 + +所以,服務務必自我停止,可透過呼叫 {@link android.app.Service#stopSelf stopSelf()} 達成,或透過呼叫 {@link android.content.Context#stopService stopService()} 讓其他元件可以停止服務。 + +

      + +

      一旦要求使用 {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 停止,系統會盡快終結服務。 +

      + +

      然而,如果您的服務同時處理多個對 {@link +android.app.Service#onStartCommand onStartCommand()} 的要求,則在您開始處理啟動要求時,您不應該停止服務,因為您仍可能會收到新的啟動要求 (在第一個要求結束時停止可能會終止第二個要求)。 + +如要避免發生此問題,您可以使用 {@link android.app.Service#stopSelf(int)} 來確保您停止服務的要求會總是基於最新的啟動要求。 + +也就是,當您呼叫 {@link +android.app.Service#stopSelf(int)} 時,會傳送啟動要求的 ID (startId 已傳送至 {@link android.app.Service#onStartCommand onStartCommand()}) 至您相關的停止要求。 + +然而,如果服務在您可以呼叫 {@link +android.app.Service#stopSelf(int)} 之前,就收到新的啟動要求,則 ID 將不會相符,服務也不會停止。

      + +

      注意:在您的應用程式完成工作時,應用程式停止其服務是非常重要的,這可以避免浪費系統資源與消耗電池電力。 +如果必要,透過呼叫 {@link +android.content.Context#stopService stopService()},其他元件可以停止服務。 +就算您啟動服務的繫結,如果曾接收對 {@link +android.app.Service#onStartCommand onStartCommand()} 的呼叫,您永遠仍必須自行停止服務。 +

      + +

      如需有關服務生命週期的詳細資訊,請參閱下節管理服務的生命週期

      + + + +

      建立已繫結的服務

      + +

      已繫結的服務是允許應用程式元件透過呼叫 {@link +android.content.Context#bindService bindService()} 來建立繫結的服務,這是為了建立長期的連線 (一般而言,並不允許元件透過呼叫 {@link +android.content.Context#startService startService()} 來「啟動」 )。 +

      + +

      當您想要透過處理程序間通訊 (IPC),與來自 Activity 的服務及您應用程式中的其他元件互動時,或是想要揭露您應用程式的特定功能給其他應用程式時,您應該建立已繫結的服務。 + +

      + +

      如要建立已繫結的服務,您必須實作 {@link +android.app.Service#onBind onBind()} 回呼方法並傳回 +{@link android.os.IBinder} (這用來定義與服務溝通的介面)。接著其他應用程式元件可以呼叫 +{@link android.content.Context#bindService bindService()} 來擷取介面並開始服務上的呼叫方法。 +服務只會為了服務所繫結應用程式元件才存在, +所以當沒有繫結服務的元件時,系統會終結服務 (當服務透過 +{@link android.app.Service#onStartCommand onStartCommand()} 啟動時,您「不」需要停止已繫結的服務)。 +

      + +

      如要建立已繫結的服務,您必須做的第一件事就是定義用來指定用戶端如何與服務通訊的介面。 +介於服務與用戶端間的介面必須是 {@link android.os.IBinder} 的實作,也是您服務必須從 {@link android.app.Service#onBind +onBind()} 回呼方法傳回的。 + +一旦用戶端收到 {@link android.os.IBinder},可以透過介面開始與服務互動。 +

      + +

      服務一次可以繫結多個用戶端。當用戶端完成與服務互動時,會呼叫 {@link android.content.Context#unbindService unbindService()} 來取消繫結。 +一旦沒有用戶端與服務繫結,系統就會終結服務。 +

      + +

      有多個方法可以實作已繫結的服務,且實作比已啟動服務更加複雜,所以會在另一份有關已繫結的服務文件中討論已繫結的服務。 + +

      + + + +

      傳送通知給使用者

      + +

      一旦執行,服務可以使用快顯通知狀態列通知來通知事件的使用者。

      + +

      快顯通知是一個會出現在目前視窗表面上短暫時間然後消失的訊息,此時狀態列通知會在狀態列提供一個圖示與訊息,使用者可以選擇然後採取行動 (例如啟動 Activity)。 + +

      + +

      通常,當已完成某些背景作業(如完成檔案下載) 且使用者現在可以採取行動時,狀態列通知是最佳的方法。 + +當使用者從擴展的檢視選取通知時,通知可以啟動 Activity (如檢視已下載檔案)。 +

      + +

      如需更多資訊,請參閱快顯通知狀態列通知開發人員指南。 +

      + + + +

      在前景中執行服務

      + +

      前景服務是被認為使用者已主動意識到、就算在記憶體不足時系統也不會終結的服務。 +前景服務必須提供通知給狀態列,狀態列會放置在「進行中」標題之下,這表示除非服務已被停止或從前景移除,否則無法解除通知。 + + +

      + +

      例如,播放來自某服務音樂的音樂播放器應該被設為在前景中執行,因為使用者明確意識到這項操作。 + +狀態列中的通知可能會指出目前播放的歌曲,並允許使用者啟動 Activity 來與音樂播放器互動。 +

      + +

      如要要求您的服務在前景中執行,可以呼叫 {@link +android.app.Service#startForeground startForeground()}。此方法有兩個參數:一個整數用來唯一識別通知,以及 {@link +android.app.Notification} 供狀態列使用。 +例如:

      + +
      +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
      +        System.currentTimeMillis());
      +Intent notificationIntent = new Intent(this, ExampleActivity.class);
      +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
      +notification.setLatestEventInfo(this, getText(R.string.notification_title),
      +        getText(R.string.notification_message), pendingIntent);
      +startForeground(ONGOING_NOTIFICATION_ID, notification);
      +
      + +

      注意:您給 {@link +android.app.Service#startForeground startForeground()} 的整數 ID 不能為 0。

      + + +

      如要從前景移除服務,可以呼叫 {@link +android.app.Service#stopForeground stopForeground()}。此方法有一個布林數,表示是否同時移除狀態列通知。 +此方法「不會」停止服務。 +然而,如果您在服務仍於前景執行時停止服務,則也會移除通知。 +

      + +

      如需更多有關通知的資訊,請參閱建立狀態列通知。 +

      + + + +

      管理服務的生命週期

      + +

      服務的生命週期比 Activity 的生命週期要簡單多了。然而,密切關注如何建立與終結服務就更重要了,因為在使用者沒有意識到的狀況下可在背景中執行服務。 + +

      + +

      服務生命週期 — 從何時建立到何時終結 — 可以遵循兩種不同的路徑: +

      + +
        +
      • 已啟動的服務 +

        當其他元件呼叫 {@link +android.content.Context#startService startService()} 時,會建立服務。然後服務可無限次數執行,且必須自行停止,方法是呼叫 {@link +android.app.Service#stopSelf() stopSelf()}。 +透過呼叫 {@link android.content.Context#stopService +stopService()},其他元件可以停止服務。 +當服務停止時,系統會終結服務。

      • + +
      • 已繫結的服務 +

        當其他元件 (一個用戶端) 呼叫 {@link +android.content.Context#bindService bindService()} 時,會建立服務。然後用戶端會透過 +{@link android.os.IBinder} 介面與服務通訊。用戶端也可以呼叫 +{@link android.content.Context#unbindService unbindService()} 來切斷連線。多重用戶端可以繫結至相同的服務,但是當所有用戶端都取消繫結時,系統就會終結服務。 +(服務「不」需要自行停止)。 +

      • +
      + +

      這兩種路徑不是完全獨立的。也就是,您可以繫結至已經使用 +{@link android.content.Context#startService startService()} 啟動的服務。例如,可以啟動背景音樂服務,方法是呼叫 {@link android.content.Context#startService +startService()} 並搭配可識別要播放音樂的 {@link android.content.Intent}。 +之後,使用者可能會想要透過播放器試試某些控制或取得關於目前歌曲的資訊,可以將 Activity 繫結至服務,方法為呼叫 {@link +android.content.Context#bindService bindService()}。 + +就這個的案子而言,除非所有的用戶端都取消繫結,否則 {@link +android.content.Context#stopService stopService()} 或 {@link android.app.Service#stopSelf +stopSelf()} 不會實際上停止服務。

      + + +

      實作生命週期回呼

      + +

      就如同 Activity,服務有您可以實作來監控服務狀態變更與在適合時段執行作業的生命週期回呼方法。 +下列服務會示範每個生命週期方法: +

      + +
      +public class ExampleService extends Service {
      +    int mStartMode;       // indicates how to behave if the service is killed
      +    IBinder mBinder;      // interface for clients that bind
      +    boolean mAllowRebind; // indicates whether onRebind should be used
      +
      +    @Override
      +    public void {@link android.app.Service#onCreate onCreate}() {
      +        // The service is being created
      +    }
      +    @Override
      +    public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) {
      +        // The service is starting, due to a call to {@link android.content.Context#startService startService()}
      +        return mStartMode;
      +    }
      +    @Override
      +    public IBinder {@link android.app.Service#onBind onBind}(Intent intent) {
      +        // A client is binding to the service with {@link android.content.Context#bindService bindService()}
      +        return mBinder;
      +    }
      +    @Override
      +    public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) {
      +        // All clients have unbound with {@link android.content.Context#unbindService unbindService()}
      +        return mAllowRebind;
      +    }
      +    @Override
      +    public void {@link android.app.Service#onRebind onRebind}(Intent intent) {
      +        // A client is binding to the service with {@link android.content.Context#bindService bindService()},
      +        // after onUnbind() has already been called
      +    }
      +    @Override
      +    public void {@link android.app.Service#onDestroy onDestroy}() {
      +        // The service is no longer used and is being destroyed
      +    }
      +}
      +
      + +

      注意:不像 Activity 生命週期回呼方法,您「不」需要呼叫這些回呼方法的超級類別實作。 +

      + + +

      圖 2.服務生命週期。左圖顯示使用 {@link android.content.Context#startService +startService()} 建立服務的生命週期,右圖顯示使用 +{@link android.content.Context#bindService bindService()} 建立服務的生命週期。 +

      + +

      透過實作這些方法,您可以監控服務生命週期中的兩個巢狀迴圈:

      + +
        +
      • 服務的整個生命發生在 {@link +android.app.Service#onCreate onCreate()} 被呼叫的時間與 {@link +android.app.Service#onDestroy} 回傳的時間之間。就像 Activity,服務於 +{@link android.app.Service#onCreate onCreate()} 中開始其初始設定,並於 {@link +android.app.Service#onDestroy onDestroy()} 中釋出所有剩餘的資訊。例如,音樂播放服務可以建立執行緒,且音樂會於 {@link +android.app.Service#onCreate onCreate()} 中播放,然後在 {@link +android.app.Service#onDestroy onDestroy()} 中停止執行緒。 + + +

        {@link android.app.Service#onCreate onCreate()} 與 {@link android.app.Service#onDestroy +onDestroy()} 方法可供所有服務呼叫,不論服務是否由 {@link android.content.Context#startService startService()} 或 {@link +android.content.Context#bindService bindService()} 所建立。 +

      • + +
      • 服務的使用生命從對 {@link +android.app.Service#onStartCommand onStartCommand()} 或 {@link android.app.Service#onBind onBind()} 的呼叫開始。 +每個方法都被遞交 {@link +android.content.Intent},也會被分別傳送至 {@link android.content.Context#startService +startService()} 或 {@link android.content.Context#bindService bindService()}。 +

        如果服務已啟動,使用生命的結束時間與整個生命結束的時間相同 (就算在 {@link android.app.Service#onStartCommand +onStartCommand()} 回傳後,服務仍在作用中)。 +如果服務已繫結,使用生命的會於 {@link +android.app.Service#onUnbind onUnbind()} 回傳時結束。

        +
      • +
      + +

      注意:雖然已啟動的服務可藉由對 + {@link android.app.Service#stopSelf stopSelf()} 或 {@link +android.content.Context#stopService stopService()} 的呼叫來停止,但沒有服務的相對應回呼 (沒有 {@code onStop()} 回呼)。 +所有,除非服務已繫結至用戶端,系統會在服務停止時終結服務 — {@link +android.app.Service#onDestroy onDestroy()} 是唯一收到的回呼。 +

      + +

      圖 2 說明服務的回呼方法。雖然圖示區隔由 +{@link android.content.Context#startService startService()} 所建立的服務與由 +{@link android.content.Context#bindService bindService()} 所建立的服務,但請記住,任何服務不論是如何建立的,都可能可以允許使用者繫結。 + +所以,一開始使用 {@link android.app.Service#onStartCommand +onStartCommand()} 啟動的服務 (由用戶端呼叫 {@link android.content.Context#startService startService()})仍可以接收對 {@link android.app.Service#onBind onBind()} 的呼叫 (當用戶端呼叫 +{@link android.content.Context#bindService bindService()} 時)。 +

      + +

      針對建立提供已繫結的服務,如需更多詳細資訊,請參閱已繫結的服務文件,其中在管理已繫結服務的生命週期包含更多有關 {@link android.app.Service#onRebind onRebind()}回呼方法的詳細資訊。 + + +

      + + + diff --git a/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd new file mode 100644 index 0000000000000000000000000000000000000000..e23301d641bcf28fa13f02f5bdc077352e599436 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=工作和返回堆疊 +parent.title=Activity +parent.link=activities.html +@jd:body + + + + +

      一個應用程式通常包含多個 Activity。每個 Activity 應根據使用者可執行的特定動作類型加以設計,且要能啟動其他 Activity。 + +例如,電子郵件應用程式可能會有一個可顯示新訊息清單的 Activity。 +當使用者選擇一則訊息,會開啟新的 Activity 以檢視該訊息。

      + +

      Activity 甚至可啟動裝置上其他應用程式的 Activity。例如,如果您的應用程式想要傳送一封電子郵件訊息,您可以定義一個意圖以執行「傳送」動作並包含一些資料,像是電子郵件地址和訊息。 + +其他應用程式中宣告處理此意圖類型的 Activity 就會開啟。 +在這種情況下,意圖就是要傳送電子郵件,因此電子郵件應用程式會啟動「撰寫」Activity (如果有多個 Activity 支援相同的意圖,則系統會讓使用者選擇要使用的 Activity)。 + +電子郵件傳送後,您的 Activity 就會繼續,並將電子郵件 Activity 視為您應用程式的一部分。 +雖然 Activity 可能來自不同的應用程式,但 Android 會將兩個 Activity 放在相同的工作中,以維護使用者體驗的流暢性。 + +

      + +

      工作是執行特定工作時,與使用者互動的 Activity 集合。 +Activity 會在堆疊 (返回堆疊) 中依照每個 Activity 開啟的順序加以排列。 +

      + + + +

      裝置主螢幕是大多數工作開始的地方。當使用者輕觸應用程式啟動組件上的某個圖示 (或主螢幕上的捷徑 ) 時,應用程式工作會移到前景。 + +如果應用程式沒有工作 (最近未使用應用程式),則會建立新的工作,且該應用程式的「主要」Activity 會以堆疊中的根 Activity 形式開啟。 + +

      + +

      當目前的 Activity 啟動另一個 Activity 時,會將新的 Activity 推到堆疊的頂端並取得焦點。 +之前的 Activity 會留在堆疊中,但已停止。Activity 停止後,系統會保留其使用者介面的目前狀態。 +當使用者按下 [返回] 按鈕,會將目前的 Activity 從堆疊頂端推出 (Activity 已終結),並繼續進行之前的 Activity (還原其 UI 之前的狀態)。 + + +堆疊中的 Activity 不會重新整理,只會從堆疊推入和推出 — 由目前 Activity 啟動時推入堆疊,而當使用者使用 [返回] 按鈕離開時推出堆疊。 + +因此,返回堆疊會以「後進先出」的物件結構進行運作。 + +圖 1 透過時間軸將此行為視覺化,以時間軸顯示 Activity 間的進度以及每個時間點的目前返回堆疊。 + +

      + + +

      圖 1.顯示工作中每個新 Activity 如何將項目新增到返回堆疊。 +當使用者按下 [返回] 按鈕,目前的 Activity 將會終結,而之前的 Activity 則會繼續進行。 + +

      + + +

      如果使用者持續按下 [返回],則會持續推出堆疊中的每個 Activity 以顯示之前的 Activity,直到使用者返回主螢幕 (或到工作開始時執行的 Activity)。 + + +當堆疊中的 Activity 全部移除後,工作將不再存在。

      + +
      +

      圖 2.兩個工作:工作 B 在前景收到使用者互動,而工作 A 在背景等待繼續。 +

      +
      +
      +

      圖 3.單一 Activity 會具現化很多次。

      +
      + +

      工作是一個緊密結合的單位,當使用者開始新的工作時可以移到「背景」,或透過 [首頁] 按鈕前往主螢幕。 +在背景時,工作中的所有 Activity 都會停止,但該工作的返回堆疊會保留下來 — 該工作純粹失去焦點,由另一個工作取而代之,如圖 2 所示。 + + +之後,工作可以返回「前景」,讓使用者繼續未完成的工作。 +例如,假設目前的工作 (工作 A) 的堆疊中有三個 Activity — 兩個位於目前的 Activity 下。 +使用者按下 [首頁] 按鈕,然後從應用程式啟動新的應用程式。 + +當主螢幕出現時,工作 A 會移到背景。 +新的應用程式啟動時,系統會啟動該應用程式的工作 (工作 B),該應用程式會有自己的 Activity 堆疊。 +與該應用程式互動之後,使用者會再次回到首頁,並選取原來啟動工作 A 的應用程式。現在,工作 A 移到了前景 — 堆疊中的三個 Activity 全部保持不變,而堆疊中最頂端的 Activity 則會繼續進行。 + + + +此時,使用者也能切換回工作 B,前往首頁並選取啟動該工作的應用程式圖示 (或從總覽畫面選取應用程式工作)。這是在 Android 執行多工作業的範例。 + + + +

      + +

      注意:背景可以一次執行多個工作。 +不過,如果使用者同時執行多個背景工作,系統可能會開始終結背景 Activity 以復原記憶體,導致 Activity 狀態遺失。 +請參閱下列有關 Activity 狀態的章節。 +

      + +

      由於返回堆疊中的 Activity 不會重新整理,如果您的應用程式允許使用者從一個以上的 Activity 中啟動特定 Activity,則會建立該 Activity 的新執行個體並推入堆疊 (而不會將 Activity 任何之前的執行個體移到最頂端)。 + + +因此,您應用程式中的一個 Activity 可能會具現化很多次 (甚至來自不同的工作),如圖 3 所示。 +也因為這樣,如果使用者使用 [返回] 按鈕瀏覽之前的資訊,Activity 的每個執行個體會依開啟的順序顯示 (每個會有自己的 UI 狀態)。 + + +不過,如果您不希望 Activity 具現化一次以上,則可以修改這個行為。 +如需詳細步驟,請參閱下文的管理工作

      + + +

      摘要說明 Activity 和工作的預設行為:

      + +
        +
      • 當 Activity A 啟動 Activity B,Activity A 會停止,但系統會保留其狀態(例如捲軸位置和輸入表單的文字)。 + +如果使用者在 Activity B 按下 [返回] 按鈕,Activity A 的狀態會復原並繼續執行。 +
      • +
      • 當使用者按下 [首頁] 按鈕離開工作,目前的 Activity 會停止且其工作會移到背景。 + +系統會保留工作中所有 Activity 的狀態。如果使用者稍後選取啟動工作的啟動組件圖示繼續執行工作,工作會移到前景並在堆疊頂端繼續執行 Activity。 + +
      • +
      • 如果使用者按下 [返回] 按鈕,會將目前的 Activity 從堆疊推出並終結。 + +堆疊中之前的 Activity 會繼續進行。Activity 終結後,系統將不會保留 Activity 的狀態。 +
      • +
      • Activity 可以具現化很多次,即使來自其他工作也一樣。
      • +
      + + +
      +

      導覽設計

      +

      如需應用程式導覽如何在 Android 運作的詳細資訊,請參閱 Android 設計的導覽指南。

      +
      + + +

      儲存 Activity 狀態

      + +

      如上所述,系統的預設行為會在 Activity 停止時保留 Activity 的狀態。 +如此一來,當使用者瀏覽之前的 Activity 時,其使用者介面的顯示方式將與離開時一樣。 +不過,您可以 — 也應該 — 使用回呼方法主動保留 Activity 的狀態,以防止 Activity 遭到終結且必須重新建立的情況。 + +

      + +

      如果系統停止您其中一個 Activity (例如,當新的 Activity 開始或工作移到背景),當系統需要復原系統記憶體時,可能會完全終結該 Activity。 + +發生這種情況時,與 Activity 狀態相關的資訊都將遺失。如果發生這種情況,系統仍然知道該 Activity 位於返回堆疊中,但是當 Activity 移到堆疊頂端時,系統必須重新建立該 Activity (而不是繼續執行)。 + + +如果不想讓使用者的工作遺失,您應該在 Activity 中實作 {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} 回呼方法,主動保留該工作。 + + +

      + +

      如需儲存 Activity 狀態的詳細資訊,請參閱 Activity 文件。 +

      + + + +

      管理工作

      + +

      如上所述,Android 管理工作和返回堆疊的方式 — 將連續啟動的所有 Activity 放在相同的工作及「後進先出」堆疊中 — 對大多數應用程式而言非常好用,而且您不需擔心 Activity 與工作關聯的方式或它們如何存在於返回堆疊中。 + + +不過,您也許會想中斷一般的行為。 +您或許會希望應用程式的 Activity 可以在啟動時開始一個新的工作 (而不是放入目前的工作中);或者當您啟動一個 Activity 時,您可能會想使用其現有的執行個體 (而不是在返回堆疊頂端建立新的執行個體);又或者當使用者離開工作時,您想要清除返回堆疊中的所有 Activity,只留下根 Activity。 + + + +

      + +

      如要執行這些工作及更多工作,您可以使用 {@code <activity>} 宣示說明元素中的屬性,以及您傳送到 {@link android.app.Activity#startActivity startActivity()} 之意圖中的旗標。 + + +

      + +

      就這個情況而言,您可以使用的主要 +{@code <activity>} 屬性包括:

      + + + +

      而您可以使用的主要意圖旗標包括:

      + +
        +
      • {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
      • +
      • {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
      • +
      • {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
      • +
      + +

      在以下各節中,您將瞭解如何使用這些宣示說明屬性和意圖旗標,定義 Activity 與工作關聯的方式以及它們在返回堆疊中的行為。 +

      + +

      同時,還會分開討論工作與 Activity 如何在總覽畫面表示和進行管理。 +請參閱總覽畫面以取得詳細資訊。 +一般而言,您應該允許系統定義如何在總覽畫面中呈現工作與 Activity,而且不需要修改此行為。 +

      + +

      注意:大多數應用程式不應中斷 Activity 和工作的預設行為: +如果您判斷修改 Activity 的預設行為是必要的,請謹慎小心並記得測試啟動期間 Activity 的可用性,以及使用 [返回] 按鈕從其他 Activity 和工作瀏覽到此 Activity 的情況。請記得測試可能會與使用者預期的行為衝突的瀏覽行為。 + + +

      + + +

      定義啟動模式

      + +

      啟動模式可讓您定義 Activity 的新執行個體與目前工作關聯的方式。 +您可用兩種方法定義不同的啟動模式:

      +
        +
      • 使用宣示說明檔案 +

        當您在宣示說明檔案中宣告 Activity 時,您可以指定 Activity 啟動時該如何與工作關聯。 +

      • +
      • 使用意圖旗標 +

        當您呼叫 {@link android.app.Activity#startActivity startActivity()} 時,您可以在 {@link android.content.Intent} 包含一個旗標,宣告新 Activity 應如何 (或是否) 與目前的工作關聯。 + +

      • +
      + +

      因此,如果 Activity A 啟動 Activity B,Activity B 可以在其宣示說明中定義它應如何與目前的工作 (如果有) 關聯,而且 Activity A 也能要求 Activity B 應如何與目前的工作關聯。 + +如果這兩個 Activity 皆定義 Activity B 應如何與工作關聯,相較於 Activity B 的要求 (如宣示說明中所定義),會優先採用 Activity A 的要求 (如意圖中所定義)。 + +

      + +

      注意:某些宣示說明檔案中提供的啟動模式可能沒有對應的意圖旗標,同樣地,某些意圖旗標提供的啟動模式無法在宣示說明中定義。 + +

      + + +

      使用宣示說明檔案

      + +

      當您在宣示說明檔案中宣告 Activity 時,您可以使用 {@code <activity>} 元素的 {@code +launchMode} 屬性,指定 Activity 應如何與工作關聯。 + +

      + +

      {@code +launchMode} 屬性可指定應如何將 Activity 啟動至工作內的指示。 +您可以為 +launchMode 屬性指定四種不同的啟動模式: +

      + +
      +
      {@code "standard"} (預設模式)
      +
      預設。系統在啟動 Activity 的工作中建立新的執行個體,並將意圖路由至該處。 +Activity 可以具現化很多次,每個執行個體可屬於不同的工作,而且一個工作可以有多個執行個體。 +
      +
      {@code "singleTop"}
      +
      如果 Activity 的執行個體已經出現在目前工作的頂端,系統會透過呼叫其 {@link +android.app.Activity#onNewIntent onNewIntent()} 方法,將意圖路由至該執行個體,而不是建立新的 Activity 執行個體。 + +Activity 可以具現化很多次,每個執行個體可屬於不同的工作,而且一個工作可以有多個執行個體 (但僅限於返回堆疊頂端的 Activity 不是現有的 Activity 執行個體時)。 + + +

      例如,假設工作的返回堆疊包含根 Activity A 及 Activity B、C 及在最頂端的 D (堆疊為 A-B-C-D;D 在最頂端)。 +類型 D Activity 的意圖抵達。 +如果 D 有預設的 {@code "standard"} 啟動模式,則會啟動新的類別執行個體,且堆疊會變成 A-B-C-D-D。不過,如果 D 啟動模式為 {@code "singleTop"},D 的現有執行個體會透過 {@link +android.app.Activity#onNewIntent onNewIntent()} 接收意圖,這是因為它位於堆疊的最頂端 — 堆疊會維持 A-B-C-D。不過,如果類型 B Activity 的意圖抵達,則 B 的新執行個體會新增到堆疊中,即使其啟動模式為 {@code "singleTop"} 也是如此。 + + + +

      +

      注意:建立新的 Activity 執行個體之後,使用者可按下 [返回] 按鈕,返回之前的 Activity。 +但是,如果處理新意圖的是現有的 Activity 執行個體,則使用者無法按下 [返回] 按鈕回到新意圖抵達 {@link android.app.Activity#onNewIntent +onNewIntent()} 之前的 Activity 狀態。 + + + +

      +
      + +
      {@code "singleTask"}
      +
      系統會建立新的工作並在新工作的根目錄將 Activity 具現化。不過,如果 Activity 的執行個體已經出現在其他工作中,系統會透過呼叫其 {@link +android.app.Activity#onNewIntent onNewIntent()} 方法,將意圖路由至現有的執行個體,而不是建立新的執行個體。 + +一次只能有一個 Activity 執行個體。 + +

      注意:雖然 Activity 是在新工作中啟動,使用者仍可使用 [返回] 按鈕返回之前的 Activity。 +

      +
      {@code "singleInstance"}。
      +
      與 {@code "singleTask"} 一樣,差別在於系統不會將任何其他 Activity 啟動至保留執行個體的工作中。 +Activity 一律是其工作的唯一成員;使用此項目啟動的任何 Activity 會在個別的工作中開啟。 +
      +
      + + +

      另外一個例子,Android 瀏覽器應用程式宣告網頁瀏覽器 Activity 應永遠在自己的工作中開啟 — 透過在 {@code <activity>} 元素指定 {@code singleTask} 啟動模式。 +這表示如果您的應用程式發出開啟 Android 瀏覽器的意圖,其 Activity 不會與您的應用程式放在同一個工作中。 + + +而是會為瀏覽器啟動新的工作,或者如果瀏覽器已經有工作在背景執行,會將該工作帶出來處理新的意圖。 + +

      + +

      無論 Activity 在新工作啟動或與啟動該 Activity 之 Activity 的相同工作中啟動,使用者都能使用 [返回] 按鈕返回之前的 Activity。 +不過,如果您啟動指定 {@code singleTask} 啟動模式的 Activity,如果該 Activity 的執行個體存在於背景工作中,則該工作會整個移到前景。 + +此時,返回堆疊現在包含已帶出且位於堆疊頂端之工作的所有 Activity。 + +圖 4 說明這種類型的情況。

      + + +

      圖 4.顯示含有啟動模式 "singleTask" 的 Activity 如何新增到返回堆疊。 +如果 Activity 已經是背景工作的一部份且有自己的返回堆疊,則整個返回堆疊都會帶出來,位於目前工作的最頂端。 + +

      + +

      如要進一步瞭解如何使用宣示說明檔案中的啟動模式,請參閱 <activity> 元素,其中會有 {@code launchMode} 屬性和可接受值的詳細說明。 + + +

      + +

      注意:您使用 {@code launchMode} 屬性指定的 Activity 行為可被啟動 Activity 之意圖所含的旗標所覆寫,如下一節所述。 + +

      + + + +

      使用意圖旗標

      + +

      啟動 Activity 時,您可以在傳送到 {@link +android.app.Activity#startActivity startActivity()} 的意圖中包含旗標,以修改 Activity 及其工作的預設關聯。 +您可以用來修改預設行為的旗標包括: +

      + +

      +

      {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}
      +
      在新工作中啟動 Activity。如果工作已為您目前啟動的 Activity 執行,該工作會移到前景並復原至上個狀態,而且 Activity 會在 {@link android.app.Activity#onNewIntent onNewIntent()} 收到新的意圖。 + + +

      這會產生與 {@code "singleTask"} {@code launchMode} 值相同的行為,如上節所述。 +

      +
      {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
      +
      如果現在正在啟動的 Activity 是目前的 Activity (位於返回堆疊的頂端),則現有執行個體會收到 {@link android.app.Activity#onNewIntent onNewIntent()} 呼叫,而不會建立新的 Activity 執行個體。 + + +

      這會產生與 {@code "singleTop"} {@code launchMode} 值相同的行為,如上節所述。 +

      +
      {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}
      +
      如果正在啟動的 Activity 已在目前的工作中執行,則不會啟動新的 Activity 執行個體,而是會終結位於其上方的所有其他 Activity,且此意圖會透過 {@link android.app.Activity#onNewIntent onNewIntent()} 傳送到繼續執行的 Activity 執行個體 (現在位於頂端)。 + + + +

      沒有任何 {@code launchMode} 屬性值可以產生此行為。 +

      +

      {@code FLAG_ACTIVITY_CLEAR_TOP} 最常與 {@code FLAG_ACTIVITY_NEW_TASK} 搭配使用。 +一起使用時,這些旗標可以找出位於其他工作中的現有 Activity,然後將它放置於可以回應意圖的地方。 + +

      +

      注意:如果指定 Activity 的啟動模式為 {@code "standard"},它也會從堆疊中移除,改為啟動新的執行個體處理傳入的意圖。 + + +這是因為當啟動模式為 {@code "standard"} 時,一律會為新的意圖建立新的執行個體。 +

      +
      + + + + + + +

      處理親和性

      + +

      親和性可指出 Activity 偏好屬於哪個工作。根據預設,相同應用程式的所有 Activity 間互相都有親和性。 +因此,根據預設,相同應用程式的所有 Activity 都偏好位於相同的工作。 +不過,您可以修改 Activity 的預設親和性。 +不同應用程式中定義的 Activity 可以共用親和性,或者相同應用程式中定義的 Activity 可以指派不同的工作親和性。 + +

      + +

      您可以使用 {@code <activity>} 元素的 {@code taskAffinity} 屬性修改任何指定 Activity 的親和性。 + +

      + +

      {@code taskAffinity} 屬性使用字串值,但必須與 +{@code <manifest>} 元素中宣告的預設封裝名稱不同,因為系統使用該名稱來識別應用程式的預設工作親和性。 + + + +

      + +

      在兩種情況下會用到親和性:

      +
        +
      • 當啟動 Activity 的意圖包含 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} 旗標。 + + +

        根據預設,新的 Activity 會啟動至 Activity (名為 {@link android.app.Activity#startActivity startActivity()}) 的工作中。 +系統會將它推入至與呼叫端相同的返回堆疊。 +不過,如果傳送至 +{@link android.app.Activity#startActivity startActivity()} 的意圖包含 {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} 旗標,則系統會找尋不同的工作來放置新的 Activity。 + +這通常是新工作。 +不過,它不一定要是新工作。如果現有工作中有與新 Activity 相同的親和性,Activity 會啟動至該工作中。 +如果沒有,會開始新的工作。

        + +

        如果此旗標導致 Activity 開始新的工作,而使用者按 [首頁] 按鈕離開它,必須要有方法可以讓使用者回來瀏覽這個工作。 + +有些實體 (例如,通知管理員) 總是從外部工作開始 Activity,從來不使用自己的工作,因此他們都會將 {@code FLAG_ACTIVITY_NEW_TASK} 放入傳送到 +{@link android.app.Activity#startActivity startActivity()} 的意圖。 + +如果您的 Activity 可以由外部實體呼叫且可能使用此旗標,記得要提供使用者獨立的方法回到啟動的工作,例如,透過啟動組件圖示 (工作的根 Activity 有一個 {@link android.content.Intent#CATEGORY_LAUNCHER} 意圖篩選器;請參閱下方的開始工作)。 + + + +

        +
      • + +
      • 當 Activity 的 +{@code allowTaskReparenting} 屬性設為 {@code "true"}。 +

        在這種情況下,當工作移到前景時,Activity 可以從其啟動的工作移到與其有親和性的工作。 +

        +

        例如,假設將報告所選城市天氣狀況的 Activity 定義為旅遊應用程式的一部份。 +它與相同應用程式中的其他 Activity 有相同的親和性 (預設的應用程式親和性),而且它允許與此屬性重設父代。 +當您的其中一個 Activity 開始氣象報告程式 Activity,它一開始屬於與您 Activity 相同的工作。 + +不過,當旅遊應用程式工作移到前景,氣象報告程式 Activity 就會重新指派給該工作,並在其中顯示。 +

        +
      • +
      + +

      提示:如果從使用者的角度來看 {@code .apk} 檔案包含一個以上的「應用程式」,您可能會想要使用 {@code taskAffinity} 屬性對與每個「應用程式」關聯的 Activity 指派不同的親和性。 + +

      + + + +

      清除返回堆疊

      + +

      如果使用者離開工作一段很長的時間,系統會清除根 Activity 以外所有 Activity 的工作 +。當使用者再次回到工作,只會復原根 Activity。 +系統會有這樣的行為是因為在一段很長的時間後,使用者很可能會放棄他們之前在做的工作,並回到工作開始其他新的工作。 +

      + +

      您可以使用下列一些 Activity 屬性來修改這個行為:

      + +
      +
      alwaysRetainTaskState +
      +
      如果這項屬性在工作的根 Activity 中設為 {@code "true"},則剛描述的預設行為不會發生。 +即使過了很長的一段時間,工作仍然會在堆疊保留所有的 Activity。 +
      + +
      clearTaskOnLaunch
      +
      如果這項屬性在工作的根 Activity 中設為 {@code "true"},則剛描述的預設行為不會發生。 +即使過了很長的一段時間,工作仍然會在堆疊保留所有的 Activity。 +換句話說,它與 + +{@code alwaysRetainTaskState} 相反。即便使用者只離開工作一小段時間,使用者還是會回到工作的起始狀態。 +
      + +
      finishOnTaskLaunch +
      +
      這項屬性與 {@code clearTaskOnLaunch} 相似,但它在單一 Activity 上作業,而不是在整個工作。 + +它也會導致任何 Activity 離開,包含根 Activity。 +如果設成 {@code "true"},Activity 只會在目前的工作階段留在此工作中。 +如果使用者離開後再回到工作,該工作將不再存在。 +
      +
      + + + + +

      開始工作

      + +

      您可以給予 Activity 一個意圖篩選器,將 +{@code "android.intent.action.MAIN"} 設定為指定的動作, +{@code "android.intent.category.LAUNCHER"} 設定為指定的類別,以便將該 Activity 設定為工作的進入點。 +例如:

      + +
      +<activity ... >
      +    <intent-filter ... >
      +        <action android:name="android.intent.action.MAIN" />
      +        <category android:name="android.intent.category.LAUNCHER" />
      +    </intent-filter>
      +    ...
      +</activity>
      +
      + +

      這類意圖篩選器可在應用程式啟動組件顯示 Activity 的圖示和標籤,讓使用者啟動 Activity 並回到 Activity 啟動後任何時間建立的工作。 + + +

      + +

      第二項功能很重要:使用者必須能夠在離開工作後,使用此 Activity 啟動組件回到此工作。 +由於這個原因,兩個將 Activity 標示為一律啟動工作的啟動模式 {@code "singleTask"} 和 +{@code "singleInstance"},應只能在 Activity 有 {@link android.content.Intent#ACTION_MAIN} 和 {@link android.content.Intent#CATEGORY_LAUNCHER} 篩選器時才能使用。 + + +例如,試想如果缺少篩選器會發生什麼情況: +意圖會啟動 {@code "singleTask"} Activity、起始新工作,然後使用者會花一些時間在該工作進行作業。 +之後,使用者按下 [首頁] 按鈕。 +此工作現在會傳送到背景而且不會顯示。現在,使用者沒有辦法回到工作,這是因為應用程式啟動組件沒有代表此工作的項目。 +

      + +

      在您不希望使用者返回 Activity 的情況下,將 +<activity> 元素的 +{@code finishOnTaskLaunch} 設定為 {@code "true"} (請參閱清除堆疊)。 + +

      + +

      如要進一步瞭解工作和 Activity 在總覽畫面中的顯示及管理方式,請參閱總覽畫面。 + +

      + + diff --git a/docs/html-intl/intl/zh-tw/guide/index.jd b/docs/html-intl/intl/zh-tw/guide/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..f7ad966d73fa68d344efed515c155f442d47f273 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/index.jd @@ -0,0 +1,74 @@ +page.title=Android 簡介 + +@jd:body + + + + +

      Android 提供內容豐富的應用程式架構,可讓您在 Java 語言環境中建置適用於行動裝置的新穎應用程式和遊戲。 +您可以參閱左側導覽區所列的文件,進一步瞭解如何使用 Android 的各種 API 建置應用程式。 +

      + +

      如果您是剛開始接觸 Android 開發環境,請務必詳閱下列有關 Android 應用程式架構的基本概念: +

      + + +
      + +
      + +

      應用程式可提供多個進入點

      + +

      Android 應用程式是由許多不同元件建置而成,應用程式可個別呼叫每個元件。 +例如,「Activity」可在單一畫面中顯示使用者介面,而「服務」則個別可在背景中執行作業。 + +

      + +

      您可以透過某個元件使用「意圖」啟動另一個元件。您甚至可以啟動其他應用程式中的元件,例如啟動地圖應用程式的 Activity 來顯示地址。 +這個模型可為單一應用程式提供多個進入點,還能讓任何應用程式針對其他應用程式可能呼叫的動作,以使用者設定的「預設值」運作。 + +

      + + +

      瞭解詳情:

      + + +
      + + +
      + +

      應用程式會針對不同裝置進行調整

      + +

      Android 提供的應用程式架構可視情況進行調整,讓您能夠針對不同的裝置設定提供專屬資源。 +例如,您可以針對不同的螢幕大小建立各種 XML 版面配置檔案,藉此讓系統根據目前裝置的螢幕大小決定要套用的版面配置設定。 + +

      + +

      如果有應用程式功能需要特定硬體 (例如相機) 才能運作,您可以在執行階段查詢裝置功能的可用性。 +此外,您還可以視需要宣告您的應用程式所需的功能,以便讓 Google Play 商店等應用程式市集禁止使用者在不支援相關功能的裝置上安裝您的應用程式。 + +

      + + +

      瞭解詳情:

      + + +
      + +
      + + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd b/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd new file mode 100644 index 0000000000000000000000000000000000000000..5e42e37d91fc54598dae72ab85ec940a46f89fc8 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/manifest/manifest-intro.jd @@ -0,0 +1,517 @@ +page.title=應用程式宣示說明 +@jd:body + + + +

      + 每個應用程式的根目錄都必須包含 AndroidManifest.xml 檔案 (名稱要一字不差)。 + 宣示說明檔案可向 Android 系統顯示應用程式的基本資訊,也就是系統在執行該應用程式的任何程式碼之前必須具備的資訊。 + + + 宣示說明可執行下列動作: +

      + +
        +
      • 為應用程式的 Java 封裝命名。 +封裝名稱可當成應用程式的唯一識別碼使用。
      • + +
      • 描述應用程式的元件 — 組成應用程式的 Activity、服務、廣播接收器和內容供應程式。 + +為實作每個元件的類別命名以及發佈類別的功能 (例如,類別可處理的 {@link android.content.Intent +Intent} 訊息)。 +這些宣告可讓 Android 系統瞭解元件為何以及可在哪些情況下啟動。 +
      • + +
      • 決定代管應用程式元件的程序。
      • + +
      • 宣告應用程式必須擁有哪些權限,才能存取 API 受保護的部分以及與其他應用程式互動。 +
      • + +
      • 宣示說明亦可宣告其他項目必須擁有哪些權限,才能與應用程式的元件互動。 +
      • + +
      • 列出可在應用程式執行時提供分析和其他資訊的 {@link android.app.Instrumentation} 類別。 +只有在應用程式開發及測試完成的情況下,宣示說明中才會顯示這些宣告;這些宣告會在應用程式發佈之前移除。 + +
      • + +
      • 宣告應用程式要求的最低 Android API 級別。 +
      • + +
      • 列出應用程式必須連結的程式庫。
      • +
      + + +

      宣示說明檔案結構

      + +

      +下圖顯示宣示說明檔案的一般結構和可納入其中的元素。 +每個元素和其所有屬性都會完全記錄在個別檔案中。 +如要查看任一元素的詳細資訊,只要按一下圖表中的元素名稱、圖表後方按字母順序列出的元素清單,或在他處提及的任何元素名稱。 + + + +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +
      +<manifest>
      +
      +    <uses-permission />
      +    <permission />
      +    <permission-tree />
      +    <permission-group />
      +    <instrumentation />
      +    <uses-sdk />
      +    <uses-configuration />  
      +    <uses-feature />  
      +    <supports-screens />  
      +    <compatible-screens />  
      +    <supports-gl-texture />  
      +
      +    <application>
      +
      +        <activity>
      +            <intent-filter>
      +                <action />
      +                <category />
      +                <data />
      +            </intent-filter>
      +            <meta-data />
      +        </activity>
      +
      +        <activity-alias>
      +            <intent-filter> . . . </intent-filter>
      +            <meta-data />
      +        </activity-alias>
      +
      +        <service>
      +            <intent-filter> . . . </intent-filter>
      +            <meta-data/>
      +        </service>
      +
      +        <receiver>
      +            <intent-filter> . . . </intent-filter>
      +            <meta-data />
      +        </receiver>
      +
      +        <provider>
      +            <grant-uri-permission />
      +            <meta-data />
      +            <path-permission />
      +        </provider>
      +
      +        <uses-library />
      +
      +    </application>
      +
      +</manifest>
      +
      + +

      +下方按字母順序列出可出現在宣示說明檔案中的所有元素。 +只有這些才是符合資格的元素,您無法新增自己的元素或屬性。 + +

      + +

      +<action> +
      <activity> +
      <activity-alias> +
      <application> +
      <category> +
      <data> +
      <grant-uri-permission> +
      <instrumentation> +
      <intent-filter> +
      <manifest> +
      <meta-data> +
      <permission> +
      <permission-group> +
      <permission-tree> +
      <provider> +
      <receiver> +
      <service> +
      <supports-screens> +
      <uses-configuration> +
      <uses-feature> +
      <uses-library> +
      <uses-permission> +
      <uses-sdk> +

      + + + + +

      檔案轉換

      + +

      +某些轉換和規則通常適用於宣示說明中的所有元素與屬性: + +

      + +
      +
      元素
      +
      只有 +<manifest> 與 +<application> 是必要元素,務必顯示兩者且這些元素只能出現一次。 +雖然您至少必須顯示當中的一些元素,才能完成有意義的作業,但大部分其他元素可以出現數次或完全不出現。 + + + + +

      +如果可以的話,元素還可以包含其他元素。所有值並非當成元素內的字元資料使用,而是透過屬性設定。 + +

      + +

      +系統通常不會將位於相同層級的元素排序。例如, +<activity>、 +<provider> 和 +<service> 元素能以任何順序排列組合。 +( +<activity-alias> 元素是這項規則的例外狀況: +由於它是 +<activity> 的別名,因此必須跟在該元素的後面)。 + +

      + +
      屬性
      +
      形式上而言,所有屬性均為選用性質。不過,您必須為元素指定某些屬性,才能達到其目的。 +請使用本文件當成參考指南。 +真正的選用屬性會提及在缺少規格時要使用的預設值或狀態。 + + +

      除了 +<manifest> 根元素的某些屬性以外,所有屬性名稱都是以前置詞 {@code android:}為開頭,例如 {@code android:alwaysRetainTaskState}。 + +由於前置詞是通用的,因此按名稱參照屬性時,文件通常會加以省略。 + +

      + +
      宣告類別名稱
      +
      許多元素都會對應到 Java 物件,包括應用程式本身的元素 ( +<application> 元素) 與其主要元件:Activity (<activity>)、服務 (<service>)、廣播接收器 (<receiver>) 以及內容供應程式 (<provider>)。 + + + + + + + + + + +

      +如果您一如往常定義元件類別 ({@link android.app.Activity}、{@link android.app.Service}、 +{@link android.content.BroadcastReceiver} 及 {@link android.content.ContentProvider}) 般定義子類別,就會透過 {@code name} 屬性宣告子類別。 + +該名稱必須包含完整的封裝指定名稱。 +例如,{@link android.app.Service} 子類別可能會以下列格式宣告: + +

      + +
      <manifest . . . >
      +    <application . . . >
      +        <service android:name="com.example.project.SecretService" . . . >
      +            . . .
      +        </service>
      +        . . .
      +    </application>
      +</manifest>
      + +

      +不過,採用速記法時,如果字串的第一個字元是句點,就會將字串附加到應用程式的封裝名稱 (如同由 +<manifest> 元素的 +package 屬性指定)。 + + +下列的指派結果會和上述相同: +

      + +
      <manifest package="com.example.project" . . . >
      +    <application . . . >
      +        <service android:name=".SecretService" . . . >
      +            . . .
      +        </service>
      +        . . .
      +    </application>
      +</manifest>
      + +

      +啟動元件時,Android 會建立具名子類別的執行個體。如果未指定子類別,就會建立基本類別的執行個體。 + +

      + +
      多個值
      +
      如果可以指定多個值,該元素幾乎會一直重複,而不是在單一元素內列出多個值。 +例如,意圖篩選器能列出數種動作: + + +
      <intent-filter . . . >
      +    <action android:name="android.intent.action.EDIT" />
      +    <action android:name="android.intent.action.INSERT" />
      +    <action android:name="android.intent.action.DELETE" />
      +    . . .
      +</intent-filter>
      + +
      資源值
      +
      有些屬性的值可以供使用者查看 — 例如 Activity 的標籤和圖示。 +您必須將這些屬性的值本地化,以便從資源或主題設定這些值。 +資源值是採用下列格式表示: +

      + +

      {@code @[package:]type:name}

      + +

      +其中的package 名稱可以省略 (如果資源所在的封裝和應用程式相同的話), + type 是指資源類型 — 例如「字串」或「可繪項目」 — 而 + name 則是可識別特定資源的名稱。範例: + +

      + +
      <activity android:icon="@drawable/smallPic" . . . >
      + +

      +主題中的值會以類似的方式表示,但字首會是 '{@code ?}',而不是 '{@code @}': + +

      + +

      {@code ?[package:]type:name} +

      + +
      字串值
      +
      如果屬性值為字串,必須使用雙反斜線 ('{@code \\}')來溢出字元,例如 '{@code \\n}'表示換行字元,或 '{@code \\uxxxx}' 表示 Unicode 字元。 + +
      +
      + + +

      檔案功能

      + +

      +下列各節說明如何在宣示說明檔案中反映部分 Android 功能。 + +

      + + +

      意圖篩選器

      + +

      +應用程式的核心元件 (即應用程式的 Activity、服務和廣播接收器) 是由 + 意圖啟動。意圖是一組資訊組合 ({@link android.content.Intent} 物件),用於說明要採取的動作 — 包括執行依據的資料、應執行動作的元件類別,以及其他相關的指示。 + + +Android 會找出適當的元件來回應意圖、視需要啟動元件的新執行個體,以及將意圖物件傳送給它。 + + + +

      + +

      +元件會通知其功能 (元件可回應的意圖類型),而通知途徑是 + 意圖篩選器。由於 Android 系統必須先瞭解元件能夠處理哪些意圖,才能啟動該元件,因此意圖篩選器在宣示說明中會指定為 +<intent-filter> 元素。 + + +元件可包含的篩選器數目不拘,每個篩選器描述的功能各不相同。 + +

      + +

      +明確命名目標元件的意圖會啟動該元件,而不必使用篩選器。 +但未指定目標名稱的意圖,只有在其通過其中一個元件的篩選器後,才能啟動元件。 + + +

      + +

      +如要瞭解意圖物件測試意圖篩選器的方式,請參閱意圖和意圖篩選器。 + + + +

      + + +

      圖示和標籤

      + +

      +許多元素都有可供小型圖示與文字標籤使用的 {@code icon} 和 {@code label} 屬性,而使用者可看到這些圖示和標籤。 +有些元素還包含可供較長說明文字使用的 +{@code description} 屬性,這個說明文字亦可顯示在螢幕上。 +例如,假設 +<permission> 元素含有上述三種屬性,當系統詢問使用者是否將權限授予發出要求的應用程式時,可以將代表權限的圖示、該權限的名稱以及所需的描述全都向使用者顯示。 + + + + +

      + +

      +在各種情況下,元件中設定的圖示和標籤會成為所有容器下層元素的預設 +{@code icon} 與 {@code label} 設定因此, +<application> 元素中設定的圖示和標籤會是應用程式各元件的預設圖示和標籤。 + +同樣地,為元件 (例如 +<activity> 元素) 設定的圖示和標籤會是元件的 +<intent-filter> 元素預設值。 + + +如果 +<application> 元素設有一個標籤,但 Activity 與其意圖篩選器並未設定該標籤,系統會將應用程式標籤視為 Activity 和意圖篩選器的標籤。 + + + +

      + +

      +每當執行篩選器通告的功能,要向使用者顯示元件時,就會將為意圖篩選器設定的圖示和標籤用來代表元件。 + +例如,包含 +"{@code android.intent.action.MAIN}" 與 +"{@code android.intent.category.LAUNCHER}" 設定的篩選器會將某 Activity 宣告為啟動應用程式的 Activity,也就是應顯示在應用程式啟動器中的 Activity。 + +因此,顯示在啟動器中的會是篩選器中設定的圖示和標籤。 + +

      + + +

      權限

      + +

      +單一 權限 是指一項限制,可限制某部分程式碼或裝置上資料的存取權。 + 系統會強制實施這項限制,以保護會遭到誤用而扭曲或損害使用者體驗的重要資料與程式碼。 + +

      + +

      +各項權限都是用不重複的標籤加以辨識。該標籤通常會指出受到限制的動作。 +例如,以下是 Android 定義的一些權限: + +

      + +

      {@code android.permission.CALL_EMERGENCY_NUMBERS} +
      {@code android.permission.READ_OWNER_DATA} +
      {@code android.permission.SET_WALLPAPER} +
      {@code android.permission.DEVICE_POWER}

      + +

      +單一功能最多只能利用一項權限來加以保護。 +

      + +

      +如果應用程式需要存取受權限保護的功能,它必須在宣示說明中利用 +<uses-permission> 元素來宣告其需要該項權限。 + +接著,要在裝置上安裝該應用程式時,安裝程式會檢查簽署該應用程式憑證的授權單位 (在某些情況下還會詢問使用者),然後決定是否授予要求的權限。 + + +如果授予權限,該應用程式就能夠使用受保護的功能。 + +如果不授予權限,存取相關功能的嘗試就會失敗,但使用者不會收到任何通知。 + +

      + +

      +應用程式也能利用權限來保護自己的元件 (Activity、服務、廣播接收器和內容供應程式)。 +它能使用 Android 定義的任何權限 (列於 +{@link android.Manifest.permission android.Manifest.permission}) 或其他應用程式宣告的任何權限。 + +此外,應用程式也能自行定義權限。新的權限是以 +<permission> 元素宣告。 + +例如,您可以利用下列權限保護 Activity: +

      + +
      +<manifest . . . >
      +    <permission android:name="com.example.project.DEBIT_ACCT" . . . />
      +    <uses-permission android:name="com.example.project.DEBIT_ACCT" />
      +    . . .
      +    <application . . .>
      +        <activity android:name="com.example.project.FreneticActivity"
      +                  android:permission="com.example.project.DEBIT_ACCT"
      +                  . . . >
      +            . . .
      +        </activity>
      +    </application>
      +</manifest>
      +
      + +

      +請注意,在本範例中,不只以 +<permission> 元素宣告 {@code DEBIT_ACCT} 權限,還使用 +<uses-permission> 元素來要求使用此權限。 + + +即使保護是由應用程式本身強制施行,還是必須要求使用該權限,應用程式的其他元件才能啟動受保護的 Activity。 + + +

      + +

      +在相同的範例中,如果 {@code permission} 屬性設定為在別處宣告的權限 (例如 {@code android.permission.CALL_EMERGENCY_NUMBERS}),就不必再次使用 +<permission> 元素來宣告。 + + + +不過,還是必須利用 +<uses-permission> 來要求使用。 +

      + +

      + +<permission-tree> 元素可為程式碼將定義的一組權限宣告命名空間。 + +此外, +<permission-group> 可為一組權限定義標籤 (以 +<permission> 元素在宣示說明中宣告的權限或在別處宣告的權限)。 + +它只會影響在向使用者呈現權限時的分組方式。 + +<permission-group> 元素不會指定各權限所屬的群組,只會指定群組的名稱。 + +只要將群組名稱指派給 +<permission> 元素 +permissionGroup 的屬性,就能將權限放入群組中。 + + + +

      + + +

      程式庫

      + +

      +每款應用程式都會與預設的 Android 程式庫連結,該程式庫中包含的基本封裝可用於建置應用程式 (使用 Activity、Service、Intent、View、Button、Application、ContentProvider 等一般類別)。 + + + +

      + +

      +不過,有些封裝是存放在其專屬的程式庫中。如果您的應用程式使用來自這類封裝的程式碼,您必須明確要求與其建立連結。 + +宣示說明必須包含個別的 +<uses-library> 元素,才能命名各個程式庫。 +(您可以在封裝的說明文件中找到程式庫名稱)。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..42434e4b30e8a393571004d564ca507210e44920 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/calendar-provider.jd @@ -0,0 +1,1184 @@ +page.title=日曆供應程式 +@jd:body + +
      +
      +

      本文件內容

      +
        +
      1. 基本概念
      2. +
      3. 使用者權限
      4. +
      5. 日曆表格 +
          +
        1. 查詢日曆
        2. +
        3. 修改日曆
        4. +
        5. 插入日曆
        6. +
        +
      6. +
      7. 活動表格 +
          +
        1. 新增活動
        2. +
        3. 更新活動
        4. +
        5. 刪除活動
        6. +
        +
      8. +
      9. 參與者表格 +
          +
        1. 新增參與者
        2. +
        +
      10. +
      11. 提醒表格 +
          +
        1. 新增提醒
        2. +
        +
      12. +
      13. 執行個體表格 +
          +
        1. 查詢執行個體表格
        2. +
      14. +
      15. 日曆意圖 +
          +
        1. 使用意圖插入活動
        2. +
        3. 使用意圖編輯活動
        4. +
        5. 使用意圖檢視日曆資料
        6. +
        +
      16. + +
      17. 同步配接器
      18. +
      + +

      重要類別

      +
        +
      1. {@link android.provider.CalendarContract.Calendars}
      2. +
      3. {@link android.provider.CalendarContract.Events}
      4. +
      5. {@link android.provider.CalendarContract.Attendees}
      6. +
      7. {@link android.provider.CalendarContract.Reminders}
      8. +
      +
      +
      + +

      「日曆供應程式」是使用者日曆活動的存放庫。「日曆供應程式」API 允許您在日曆、活動、參與者、提醒等等項目中執行查詢、插入、更新以及刪除操作。 + +

      + + +

      「日曆供應程式」API 可以由應用程式和同步配接器使用。規則取決於發出呼叫的程式類型。 +本文件主要著重於將「日曆供應程式」API 作為應用程式使用。 +如要瞭解同步配接器不一樣的資訊,請參閱同步配接器。 + +

      + + +

      一般而言,如要讀取或寫入日曆資料,應用程式的宣示說明必須含有適當的權限 (於使用者權限中描述)。 + +如要更輕鬆執行常見操作,「日曆供應程式」提供一組意圖 (於日曆意圖中描述)。 + +這些意圖會將使用者帶往「日曆」應用程式,以插入、檢視以及編輯活動。 +使用者與「日曆」應用程式互動後,回到原來的應用程式。 +因此,您的應用程式不需要要求權限,也不需要提供檢視或建立活動的使用者介面。 +

      + +

      基本概念

      + +

      內容供應程式會儲存資料,讓應用程式可以存取這些資料。 +內容供應程式是由 Android 平台所提供 (包括「日曆供應程式」),通常會根據關聯式資料庫模型,以一組表格的形式公開資料。表格中的每一列都是一筆記錄,而每一欄則是特定類型和意義的資料。 + +透過「日曆供應程式」API,應用程式和同步配接器可以取得資料庫表格 (此資料庫表格含有使用者日曆資料) 的讀取/寫入存取權。 + +

      + +

      每個內容供應程式都會公開一個公用 URI (包裝為 +{@link android.net.Uri} 物件),可唯一識別其資料集。 +控制多個資料集 (多個表格) 的內容供應程式會為每個資料集公開個別的 URI。 +供應程式所有 URI 的開頭字串為「content://」。 +這表示資料是受到內容供應程式的控制。 +「日曆供應程式」會為每個類別 (表格) 的 URI 定義常數。 +這些 URI 的格式為 <class>.CONTENT_URI。 +例如,{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI}。 +

      + +

      「圖 1」顯示「日曆供應程式」資料模型的圖形表示。它顯示主要表格以及將每個表格連接在一起的欄位。 +

      + +Calendar Provider Data Model +

      圖 1.「日曆供應程式」資料模型。

      + +

      使用者可以有多個日曆,而且不同的日曆可以與不同類型的帳戶 (Google 日曆、Exchange 等等) 關聯。

      + +

      {@link android.provider.CalendarContract} 會定義日曆的資料模型和活動相關資訊。此資料儲存於一些表格,列示如下。

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      表格 (類別)描述

      {@link android.provider.CalendarContract.Calendars}

      此表格內含日曆特定的資訊。 +此表格中的每一列包含單一日曆的詳細資訊,例如名稱、色彩、同步資訊等等。 +
      {@link android.provider.CalendarContract.Events}此表格內含活動特定的資訊。 +此表格中的每一列包含單一活動的資訊 — 例如,活動標題、位置、開始時間、結束時間等等。 + +活動可以只發生一次或發生多次。參與者、提醒以及延伸屬性儲存於個別的表格。 +它們每一個都有 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID},會參照「活動」表格中的 {@link android.provider.BaseColumns#_ID}。 + +
      {@link android.provider.CalendarContract.Instances}此表格內含活動每次發生的開始和結束時間。 +此表格的每一列代表單一活動發生。 +單次活動執行個體和活動的對應為 1:1。 +對於週期性活動,則會自動產生多個列,對應到該活動的多次發生。 +
      {@link android.provider.CalendarContract.Attendees}此表格內含活動參與者 (邀請對象) 的資訊。 +每一列代表一個活動的單一邀請對象。 +其中會指出邀請對象類型,以及邀請對象是否出席該活動的回應。 +
      {@link android.provider.CalendarContract.Reminders}此表格內含警示/通知資訊。 +每一列代表一個活動的單一警示。一個活動可以設定多個提醒。 +每個活動的最大數量提醒指定於 +{@link android.provider.CalendarContract.CalendarColumns#MAX_REMINDERS},此項是由擁有指定日曆的同步配接器所設定。 + + +提醒是以分鐘數指定活動發生前的時間,而且有一個方法用於決定通知使用者的方式。 +
      + +

      「日曆供應程式」API 的設計具備彈性且功能強大。提供良好的使用者體驗,以及保護日曆及其資料的完整性,兩者一樣重要。 + +關於這一點,使用此 API 時,請記得以下事項: +

      + +
        + +
      • 插入、更新以及檢視日曆活動。如要從「日曆供應程式」直接插入、修改以及讀取活動,您需要具備適當的權限。然而,如果您不是要建置功能豐富的日曆應用程式,則不需要要求這些權限。您可以改用 Android「日曆」應用程式支援的意圖,將讀取和寫入操作交給您的應用程式。使用意圖時,您的應用程式可以將使用者傳送到「日曆」應用程式,在預先填好的表單中執行想要的操作。 +完成之後,使用者會回到您的應用程式。將應用程式設計成透過「日曆」來執行常見的操作,即可為使用者提供一致且完整的使用者介面。 + +我們建議您採用此方式。 +如需詳細資訊,請參閱日曆意圖。 +

        + + +
      • 同步配接器。同步配接器會將使用者裝置的日曆資料與另一台伺服器或資料來源進行同步。 +在 +{@link android.provider.CalendarContract.Calendars} 和 +{@link android.provider.CalendarContract.Events} 表格中,會保留某些欄讓同步配接器使用。供應程式和應用程式不應加以修改。 + +事實上,除非以同步配接器進行存取,否則看不到這些保留的欄。 +如需關於同步配接器的詳細資訊,請參閱同步配接器。 +
      • + +
      + + +

      使用者權限

      + +

      如要讀取日曆資料,應用程式必須在其宣示說明檔案中包含 {@link +android.Manifest.permission#READ_CALENDAR} 權限。宣示說明檔案必須包含 {@link android.Manifest.permission#WRITE_CALENDAR} 權限,才能刪除、插入或更新日曆資料: + +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
      +    <uses-sdk android:minSdkVersion="14" />
      +    <uses-permission android:name="android.permission.READ_CALENDAR" />
      +    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
      +    ...
      +</manifest>
      +
      + + +

      日曆表格

      + +

      {@link android.provider.CalendarContract.Calendars} 表格包含個別日曆的詳細資訊。 +下列「日曆」欄可以讓應用程式和同步配接器寫入。 +如需關於支援欄位的完整清單,請參閱 +{@link android.provider.CalendarContract.Calendars} 參照。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      常數描述
      {@link android.provider.CalendarContract.Calendars#NAME}日曆的名稱。
      {@link android.provider.CalendarContract.Calendars#CALENDAR_DISPLAY_NAME}使用者看到此日曆的名稱。
      {@link android.provider.CalendarContract.Calendars#VISIBLE}指出是否選擇要顯示日曆的布林值。值 0 表示與此日曆關聯的活動不應顯示。 + +值 1 表示與此日曆關聯的活動應顯示。 +此值會影響 {@link +android.provider.CalendarContract.Instances} 表格中列的產生。
      {@link android.provider.CalendarContract.CalendarColumns#SYNC_EVENTS}指出日曆是否應同步,並且讓日曆的活動儲存在裝置上的布林值。 +值 0 表示不同步此日曆,並且不要在裝置上儲存其活動。 +值 1 表示同步此日曆的活動,並且在裝置上儲存其活動。 +
      + +

      查詢日曆

      + +

      以下的範例顯示如何取得特定使用者擁有的日曆。 +為了簡化起見,本範例中的查詢操作顯示於使用者介面執行緒 (即「主要執行緒」) 中。 +實務上,應該以非同步執行緒來完成,而不是使用主要執行緒。 +如需更多討論,請參閱載入器。 +如果您不只要讀取資料,還要加以修改,請參閱 {@link android.content.AsyncQueryHandler}。 + +

      + + +
      +// Projection array. Creating indices for this array instead of doing
      +// dynamic lookups improves performance.
      +public static final String[] EVENT_PROJECTION = new String[] {
      +    Calendars._ID,                           // 0
      +    Calendars.ACCOUNT_NAME,                  // 1
      +    Calendars.CALENDAR_DISPLAY_NAME,         // 2
      +    Calendars.OWNER_ACCOUNT                  // 3
      +};
      +  
      +// The indices for the projection array above.
      +private static final int PROJECTION_ID_INDEX = 0;
      +private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
      +private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
      +private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
      + + + + + +

      在範例的下一個部分,您將建構查詢。選項會指定查詢的條件。 +在此範例中,查詢會尋找日曆中有 ACCOUNT_NAME "sampleuser@google.com"、ACCOUNT_TYPE "com.google" 以及 OWNER_ACCOUNT "sampleuser@google.com" 的內容。 + + + +如果您要查看使用者可以檢視的所有日曆,不是只查看使用者擁有的日曆,請略過 OWNER_ACCOUNT。此查詢會傳回 {@link android.database.Cursor} 物件。您可以使用此物件周遊資料庫查詢傳回的結果集。 + + + +如需關於在內容供應程式中使用查詢的詳細討論,請參閱內容供應程式。 +

      + + +
      // Run query
      +Cursor cur = null;
      +ContentResolver cr = getContentResolver();
      +Uri uri = Calendars.CONTENT_URI;   
      +String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND (" 
      +                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
      +                        + Calendars.OWNER_ACCOUNT + " = ?))";
      +String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
      +        "sampleuser@gmail.com"}; 
      +// Submit the query and get a Cursor object back. 
      +cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
      + +

      下一節會使用游標逐步檢視結果集。它會使用範例一開始設定好的常數,傳回每個欄位的值。 + +

      + +
      // Use the cursor to step through the returned records
      +while (cur.moveToNext()) {
      +    long calID = 0;
      +    String displayName = null;
      +    String accountName = null;
      +    String ownerName = null;
      +      
      +    // Get the field values
      +    calID = cur.getLong(PROJECTION_ID_INDEX);
      +    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
      +    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
      +    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);
      +              
      +    // Do something with the values...
      +
      +   ...
      +}
      +
      + +

      修改日曆

      + +

      如要執行日曆的更新,您可以提供日曆的 {@link +android.provider.BaseColumns#_ID},可以是 URI 的附加 ID +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 或以第一個選擇項目方式提供。 + + +選項的開頭應該是 "_id=?",而且第一個 +selectionArg 應該是日曆的 {@link +android.provider.BaseColumns#_ID}。 +您也可以透過將 ID 編碼在 URI 中,以執行更新。此範例會使用 +({@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 方式變更日曆的顯示名稱: + + +

      + +
      private static final String DEBUG_TAG = "MyActivity";
      +...
      +long calID = 2;
      +ContentValues values = new ContentValues();
      +// The new display name for the calendar
      +values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
      +Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
      +int rows = getContentResolver().update(updateUri, values, null, null);
      +Log.i(DEBUG_TAG, "Rows updated: " + rows);
      + +

      插入日曆

      + +

      日曆的設計主要是由同步配接器進行管理,因此您只能以同步配接器的方式插入新日曆。 +在大多數情況下,應用程式只能對日曆進行與外觀相關的變更,例如變更顯示名稱 +。如果應用程式需要建立本機日曆,請以同步配接器的方式插入日曆,方法是使用 {@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} 的 {@link android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE}。 +{@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} 是特殊的日曆帳戶類型,它沒有與裝置帳戶關聯。 + + + + + +此類型的日曆不會同步至伺服器。如需關於同步配接器的相關討論,請參閱同步配接器。 +

      + +

      活動表格

      + +

      {@link android.provider.CalendarContract.Events} 表格包含個人活動的詳細資訊。 +如要新增、更新或刪除活動,應用程式必須在宣示說明檔案中包括 {@link android.Manifest.permission#WRITE_CALENDAR}權限。 + +

      + +

      下列「活動」欄可以讓應用程式和同步配接器寫入。 +如需關於支援欄位的完整清單,請參閱 {@link +android.provider.CalendarContract.Events} 參照。

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      常數描述
      {@link android.provider.CalendarContract.EventsColumns#CALENDAR_ID}活動所屬日曆的 {@link android.provider.BaseColumns#_ID}。
      {@link android.provider.CalendarContract.EventsColumns#ORGANIZER}活動主辦人 (擁有者) 的電子郵件。
      {@link android.provider.CalendarContract.EventsColumns#TITLE}活動的標題。
      {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION}舉辦活動的地點。
      {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION}活動的描述。
      {@link android.provider.CalendarContract.EventsColumns#DTSTART}活動開始的時間,以紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。
      {@link android.provider.CalendarContract.EventsColumns#DTEND}活動結束的時間,以紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。
      {@link android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}活動的時區。
      {@link android.provider.CalendarContract.EventsColumns#EVENT_END_TIMEZONE}活動結束時間的時區。
      {@link android.provider.CalendarContract.EventsColumns#DURATION}活動的持續期間,以 RFC5545 格式表示。例如,值 "PT1H" 表示活動持續一小時,而值 "P2W" 指出持續 2 週。 + + +
      {@link android.provider.CalendarContract.EventsColumns#ALL_DAY}值 1 表示此活動需要整天,如同當地時區所定義。 +值 0 表示定期活動,會在一天中的任何時間開始和結束。 +
      {@link android.provider.CalendarContract.EventsColumns#RRULE}活動的週期規則。例如,"FREQ=WEEKLY;COUNT=10;WKST=SU"。 +您可以在這裡查看更多範例。 +
      {@link android.provider.CalendarContract.EventsColumns#RDATE}活動重複發生的日期。您通常會將 {@link android.provider.CalendarContract.EventsColumns#RDATE} 和 {@link android.provider.CalendarContract.EventsColumns#RRULE} 一起使用,以定義週期性活動的彙總集合。 + + + +如需更多討論,請參閱 RFC5545 規格
      {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY}活動要視為忙碌或有空 (可以安排其他活動) 的時間。 +
      {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_MODIFY}邀請對象是否可以修改活動。
      {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_INVITE_OTHERS}邀請對象是否可以邀請其他人。
      {@link android.provider.CalendarContract.EventsColumns#GUESTS_CAN_SEE_GUESTS}邀請對象是否可以看到參與者清單。
      + +

      新增活動

      + +

      您的應用程式插入新活動時,我們建議您使用 +{@link android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同使用意圖插入活動所述)。不過,如果需要,您也可以直接插入活動。 +本節將描述如何執行此動作。 +

      + + +

      以下是插入新活動的規則:

      +
        + +
      • 您必須包括 {@link +android.provider.CalendarContract.EventsColumns#CALENDAR_ID} 和 {@link +android.provider.CalendarContract.EventsColumns#DTSTART}。
      • + +
      • 您必須包括 {@link +android.provider.CalendarContract.EventsColumns#EVENT_TIMEZONE}。如要取得系統已安裝時區 ID 的清單,請使用 {@link +java.util.TimeZone#getAvailableIDs()}。 +請注意,如果您是透過 {@link +android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同使用意圖插入活動 — 所描述的案例) 插入活動,則不適用此規則,將會提供預設時區。 + +
      • + +
      • 對於非週期性活動,您必須包括 {@link +android.provider.CalendarContract.EventsColumns#DTEND}。
      • + + +
      • 對於週期性活動,您必須包括 {@link +android.provider.CalendarContract.EventsColumns#DURATION},以及 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 或 {@link +android.provider.CalendarContract.EventsColumns#RDATE}。請注意,如果您是透過 {@link +android.content.Intent#ACTION_INSERT INSERT} 意圖 (如同使用意圖插入活動 — 所描述的案例) 插入活動,則不適用此規則。您可以使用 {@link +android.provider.CalendarContract.EventsColumns#RRULE} 搭配 {@link android.provider.CalendarContract.EventsColumns#DTSTART} 和 {@link android.provider.CalendarContract.EventsColumns#DTEND},然後「日曆」應用程式就會自動將它轉換為一段持續時間。 + + +
      • + +
      + +

      以下是插入活動的範例。為了簡化,會在 UI 執行緒中執行此示範。 +實際運作時,插入和更新應該在非同步執行緒中完成,以便將動作移至背景執行緒。 +如需詳細資訊,請參閱 {@link android.content.AsyncQueryHandler}。 +

      + + +
      +long calID = 3;
      +long startMillis = 0; 
      +long endMillis = 0;     
      +Calendar beginTime = Calendar.getInstance();
      +beginTime.set(2012, 9, 14, 7, 30);
      +startMillis = beginTime.getTimeInMillis();
      +Calendar endTime = Calendar.getInstance();
      +endTime.set(2012, 9, 14, 8, 45);
      +endMillis = endTime.getTimeInMillis();
      +...
      +
      +ContentResolver cr = getContentResolver();
      +ContentValues values = new ContentValues();
      +values.put(Events.DTSTART, startMillis);
      +values.put(Events.DTEND, endMillis);
      +values.put(Events.TITLE, "Jazzercise");
      +values.put(Events.DESCRIPTION, "Group workout");
      +values.put(Events.CALENDAR_ID, calID);
      +values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
      +Uri uri = cr.insert(Events.CONTENT_URI, values);
      +
      +// get the event ID that is the last element in the Uri
      +long eventID = Long.parseLong(uri.getLastPathSegment());
      +// 
      +// ... do something with event ID
      +//
      +//
      + +

      注意:查看本範例如何在建立活動後擷取活動 ID。 +這是取得活動 ID 的最簡單方式。您經常需要活動 ID 來執行其他日曆操作 — 例如,在活動中新增參與者或提醒。 + +

      + + +

      更新活動

      + +

      您的應用程式允許使用者編輯活動時,我們建議您使用 {@link android.content.Intent#ACTION_EDIT EDIT} 意圖編輯活動 (如同使用意圖插入活動所述)。不過,如果需要,您也可以直接編輯活動。 + + +如要執行活動的更新,您要提供活動的 _ID,可以是 URI 的附加 ID ({@link +android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()}) 或以第一個選擇項目方式提供。 + + +選項的開頭應該是 "_id=?",而且第一個 +selectionArg 應該是活動的 _ID。 +您也可以使用不含 ID 的選項進行更新。以下是更新活動的範例。 + +它使用 +{@link android.content.ContentUris#withAppendedId(android.net.Uri,long) withAppendedId()} 方式變更活動的標題: +

      + + +
      private static final String DEBUG_TAG = "MyActivity";
      +...
      +long eventID = 188;
      +...
      +ContentResolver cr = getContentResolver();
      +ContentValues values = new ContentValues();
      +Uri updateUri = null;
      +// The new title for the event
      +values.put(Events.TITLE, "Kickboxing"); 
      +updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
      +int rows = getContentResolver().update(updateUri, values, null, null);
      +Log.i(DEBUG_TAG, "Rows updated: " + rows);  
      + +

      刪除活動

      + +

      您可以透過活動 URI 的附加 ID {@link +android.provider.BaseColumns#_ID} 或使用標準選擇方式來刪除活動。 +如果您使用附加 ID,就不能進行選擇。刪除有兩種方式:以應用程式和以同步配接器。 +應用程式刪除會將 deleted 欄設定為 1。 +此旗標會告訴同步配接器該列已刪除,而且此刪除應傳播到伺服器。 + +同步配接器會從資料庫刪除活動及其相關的所有資料。 +以下是應用程式透過活動的 {@link android.provider.BaseColumns#_ID}刪除活動的範例: +

      + + +
      private static final String DEBUG_TAG = "MyActivity";
      +...
      +long eventID = 201;
      +...
      +ContentResolver cr = getContentResolver();
      +ContentValues values = new ContentValues();
      +Uri deleteUri = null;
      +deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
      +int rows = getContentResolver().delete(deleteUri, null, null);
      +Log.i(DEBUG_TAG, "Rows deleted: " + rows);  
      +
      + +

      參與者表格

      + +

      {@link android.provider.CalendarContract.Attendees} 表格的每一列都代表活動的單一參與者或邀請對象。 +呼叫 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} 會傳回指定 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 活動的參與者清單。 + +此 {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 必須符合特定活動的 {@link +android.provider.BaseColumns#_ID}。 + +

      + +

      下表列出可寫入的欄位。 +插入新的參與者時,您必須包括 ATTENDEE_NAME 以外的所有項目。 + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      常數描述
      {@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID}活動的 ID。
      {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_NAME}參與者的名稱。
      {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_EMAIL}參與者的電子郵件地址。
      {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_RELATIONSHIP}

      參與者與活動的關係。可以是以下其中一種:

      +
        +
      • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ATTENDEE}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_NONE}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_ORGANIZER}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_PERFORMER}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#RELATIONSHIP_SPEAKER}
      • +
      +
      {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_TYPE}

      參與者的類型。可以是以下其中一種:

      +
        +
      • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_REQUIRED}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#TYPE_OPTIONAL}
      • +
      {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS}

      參與者的出席狀態。可以是以下其中一種:

      +
        +
      • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_ACCEPTED}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_DECLINED}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_INVITED}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_NONE}
      • +
      • {@link android.provider.CalendarContract.AttendeesColumns#ATTENDEE_STATUS_TENTATIVE}
      • +
      + +

      新增參與者

      + +

      以下是將單一參與者新增至活動的範例。請注意, +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 為必要的: +

      + +
      +long eventID = 202;
      +...
      +ContentResolver cr = getContentResolver();
      +ContentValues values = new ContentValues();
      +values.put(Attendees.ATTENDEE_NAME, "Trevor");
      +values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
      +values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
      +values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
      +values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
      +values.put(Attendees.EVENT_ID, eventID);
      +Uri uri = cr.insert(Attendees.CONTENT_URI, values);
      +
      + +

      提醒表格

      + +

      {@link android.provider.CalendarContract.Reminders} 表格的每一列都代表活動的單一提醒。 +呼叫 +{@link android.provider.CalendarContract.Reminders#query(android.content.ContentResolver, long, java.lang.String[]) query()} 會傳回指定 +{@link android.provider.CalendarContract.AttendeesColumns#EVENT_ID} 活動的提醒清單。 +

      + + +

      下表列出提醒可寫入的欄位。插入新的提醒時,必須包括所有項目。 +請注意,同步配接器會在 {@link +android.provider.CalendarContract.Calendars} 表格中指出同步配接器支援的提醒類型。 +如需詳細資料,請參閱 +{@link android.provider.CalendarContract.CalendarColumns#ALLOWED_REMINDERS}。 +

      + + + + + + + + + + + + + + + + + + + +
      常數描述
      {@link android.provider.CalendarContract.RemindersColumns#EVENT_ID}活動的 ID。
      {@link android.provider.CalendarContract.RemindersColumns#MINUTES}活動之前的幾分鐘要觸發提醒。
      {@link android.provider.CalendarContract.RemindersColumns#METHOD}

      鬧鐘方法 (如伺服器上所設定)。可以是以下其中一種:

      +
        +
      • {@link android.provider.CalendarContract.RemindersColumns#METHOD_ALERT}
      • +
      • {@link android.provider.CalendarContract.RemindersColumns#METHOD_DEFAULT}
      • +
      • {@link android.provider.CalendarContract.RemindersColumns#METHOD_EMAIL}
      • +
      • {@link android.provider.CalendarContract.RemindersColumns#METHOD_SMS}
      • +
      + +

      新增提醒

      + +

      此範例會在活動新增提醒。此提醒會在活動 15 分鐘之前觸發。 +

      +
      +long eventID = 221;
      +...
      +ContentResolver cr = getContentResolver();
      +ContentValues values = new ContentValues();
      +values.put(Reminders.MINUTES, 15);
      +values.put(Reminders.EVENT_ID, eventID);
      +values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
      +Uri uri = cr.insert(Reminders.CONTENT_URI, values);
      + +

      執行個體表格

      + +

      +{@link android.provider.CalendarContract.Instances} 表格內含活動每次發生的開始和結束時間。 +此表格的每一列代表單一活動發生。 +執行個體表格無法寫入,僅供查詢活動的發生。 +

      + +

      下表列出您可以針對執行個體查詢的欄位。請注意,時區是由 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_TYPE} 和 +{@link android.provider.CalendarContract.CalendarCache#KEY_TIMEZONE_INSTANCES} 所定義。 + +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      常數描述
      {@link android.provider.CalendarContract.Instances#BEGIN}執行個體的開始時間,以 UTC 毫秒數為單位。
      {@link android.provider.CalendarContract.Instances#END}執行個體的結束時間,以 UTC 毫秒數為單位。
      {@link android.provider.CalendarContract.Instances#END_DAY}執行個體的凱撒曆結束日,與「日曆」的時區相關。 + + +
      {@link android.provider.CalendarContract.Instances#END_MINUTE}執行個體的結束分鐘,從「日曆」時區的午夜開始計算。 +
      {@link android.provider.CalendarContract.Instances#EVENT_ID}此執行個體活動的 _ID
      {@link android.provider.CalendarContract.Instances#START_DAY}執行個體的凱撒曆開始日,與「日曆」的時區相關。 +
      {@link android.provider.CalendarContract.Instances#START_MINUTE}執行個體的開始分鐘,從午夜開始計算,與「日曆」的時區相關。 + +
      + +

      查詢執行個體表格

      + +

      如要查詢「執行個體」表格,您需要在 URI 中指定查詢的範圍時間。在本範例中,{@link android.provider.CalendarContract.Instances} 是透過 {@link android.provider.CalendarContract.EventsColumns} 介面的實作,得以存取 {@link +android.provider.CalendarContract.EventsColumns#TITLE} 欄位。 + + +換句話說,{@link +android.provider.CalendarContract.EventsColumns#TITLE} 是透過資料庫視觀表所傳回,而不是透過查詢原始 {@link +android.provider.CalendarContract.Instances} 表格。 + +

      + +
      +private static final String DEBUG_TAG = "MyActivity";
      +public static final String[] INSTANCE_PROJECTION = new String[] {
      +    Instances.EVENT_ID,      // 0
      +    Instances.BEGIN,         // 1
      +    Instances.TITLE          // 2
      +  };
      +  
      +// The indices for the projection array above.
      +private static final int PROJECTION_ID_INDEX = 0;
      +private static final int PROJECTION_BEGIN_INDEX = 1;
      +private static final int PROJECTION_TITLE_INDEX = 2;
      +...
      +
      +// Specify the date range you want to search for recurring
      +// event instances
      +Calendar beginTime = Calendar.getInstance();
      +beginTime.set(2011, 9, 23, 8, 0);
      +long startMillis = beginTime.getTimeInMillis();
      +Calendar endTime = Calendar.getInstance();
      +endTime.set(2011, 10, 24, 8, 0);
      +long endMillis = endTime.getTimeInMillis();
      +  
      +Cursor cur = null;
      +ContentResolver cr = getContentResolver();
      +
      +// The ID of the recurring event whose instances you are searching
      +// for in the Instances table
      +String selection = Instances.EVENT_ID + " = ?";
      +String[] selectionArgs = new String[] {"207"};
      +
      +// Construct the query with the desired date range.
      +Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
      +ContentUris.appendId(builder, startMillis);
      +ContentUris.appendId(builder, endMillis);
      +
      +// Submit the query
      +cur =  cr.query(builder.build(), 
      +    INSTANCE_PROJECTION, 
      +    selection, 
      +    selectionArgs, 
      +    null);
      +   
      +while (cur.moveToNext()) {
      +    String title = null;
      +    long eventID = 0;
      +    long beginVal = 0;    
      +    
      +    // Get the field values
      +    eventID = cur.getLong(PROJECTION_ID_INDEX);
      +    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
      +    title = cur.getString(PROJECTION_TITLE_INDEX);
      +              
      +    // Do something with the values. 
      +    Log.i(DEBUG_TAG, "Event:  " + title); 
      +    Calendar calendar = Calendar.getInstance();
      +    calendar.setTimeInMillis(beginVal);  
      +    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
      +    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));    
      +    }
      + }
      + +

      日曆意圖

      +

      您的應用程式不需要權限,即可讀取和寫入日曆資料。您可以改用 Android「日曆」應用程式支援的意圖,將讀取和寫入操作交給您的應用程式。下表列出「日曆供應程式」支援的意圖:

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      動作URI描述Extra

      + {@link android.content.Intent#ACTION_VIEW VIEW}

      content://com.android.calendar/time/<ms_since_epoch>

      + 您也可以使用 +{@link android.provider.CalendarContract#CONTENT_URI CalendarContract.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱使用意圖檢視日曆資料。 + + +
      開啟日曆到 <ms_since_epoch> 指定的時間。無。

      {@link android.content.Intent#ACTION_VIEW VIEW}

      + +

      content://com.android.calendar/events/<event_id>

      + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱使用意圖檢視日曆資料。 + + +
      檢視 <event_id> 指定的活動。{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
      +
      +
      + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
      {@link android.content.Intent#ACTION_EDIT EDIT}

      content://com.android.calendar/events/<event_id>

      + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱使用意圖編輯活動。 + + + +
      編輯 <event_id> 指定的活動。{@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_BEGIN_TIME}
      +
      +
      + {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME CalendarContract.EXTRA_EVENT_END_TIME}
      {@link android.content.Intent#ACTION_EDIT EDIT}
      +
      + {@link android.content.Intent#ACTION_INSERT INSERT}

      content://com.android.calendar/events

      + + 您也可以使用 +{@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 參照 URI。如需使用此意圖的範例,請參閱使用意圖插入活動。 + + +
      建立活動。Extra 列於下表。
      + +

      下表列出「日曆供應程式」支援的意圖 Extra: +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      意圖 Extra描述
      {@link android.provider.CalendarContract.EventsColumns#TITLE Events.TITLE}活動的名稱。
      {@link android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME}活動開始時間,以紀元元年 1 月 1 日零時起算經過的毫秒數為單位。
      {@link android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME}活動結束時間,以紀元元年 1 月 1 日零時起算經過的毫秒數為單位。
      {@link android.provider.CalendarContract#EXTRA_EVENT_ALL_DAY +CalendarContract.EXTRA_EVENT_ALL_DAY}指出活動為整天的布林值。值可以是 +truefalse
      {@link android.provider.CalendarContract.EventsColumns#EVENT_LOCATION +Events.EVENT_LOCATION}活動的地點。
      {@link android.provider.CalendarContract.EventsColumns#DESCRIPTION +Events.DESCRIPTION}活動描述。
      + {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL}邀請對象的電子郵件地址 (以逗號分隔的清單)。
      + {@link android.provider.CalendarContract.EventsColumns#RRULE Events.RRULE}活動的週期規則。
      + {@link android.provider.CalendarContract.EventsColumns#ACCESS_LEVEL +Events.ACCESS_LEVEL}活動為私人或公開性質。
      {@link android.provider.CalendarContract.EventsColumns#AVAILABILITY +Events.AVAILABILITY}活動要視為忙碌或有空 (可以安排其他活動) 的時間。
      +

      以下各節說明如何使用這些意圖。

      + + +

      使用意圖插入活動

      + +

      使用 {@link android.content.Intent#ACTION_INSERT INSERT} 意圖讓您的應用程式將活動插入工作交給「日曆」本身。使用此方式,您的應用程式就不需要將 {@link +android.Manifest.permission#WRITE_CALENDAR} 權限包括在其宣示說明檔案中。 + +

      + + +

      使用者執行採用此方式的應用程式時,此應用程式會將使用者 +傳送到「日曆」以完成新增活動的操作。{@link +android.content.Intent#ACTION_INSERT INSERT} 意圖會使用額外的欄位將「日曆」中活動的詳細資訊,預先填入表單。 +然後,使用者可以取消活動、視需要編輯表單或將活動儲存到其日曆。 + +

      + + + +

      以下的程式碼片段會在 2012 年 1 月 19 日安排活動,此活動的期間是從上午 7:30 到上午 8:30。 +請注意下列關於此程式碼片段的事項:

      + +
        +
      • 它指定 {@link android.provider.CalendarContract.Events#CONTENT_URI Events.CONTENT_URI} 作為 URI。 +
      • + +
      • 它使用 {@link +android.provider.CalendarContract#EXTRA_EVENT_BEGIN_TIME +CalendarContract.EXTRA_EVENT_BEGIN_TIME} 和 {@link +android.provider.CalendarContract#EXTRA_EVENT_END_TIME +CalendarContract.EXTRA_EVENT_END_TIME} 額外欄位,將活動的時間預先填入表單。 +這些時間值必須以自紀元元年 1 月 1 日零時起算經過的 UTC 毫秒數為單位。 +
      • + +
      • 它使用 {@link android.content.Intent#EXTRA_EMAIL Intent.EXTRA_EMAIL} 額外欄位提供逗號分隔的受邀者清單 (以電子郵件地址指定)。 +
      • + +
      +
      +Calendar beginTime = Calendar.getInstance();
      +beginTime.set(2012, 0, 19, 7, 30);
      +Calendar endTime = Calendar.getInstance();
      +endTime.set(2012, 0, 19, 8, 30);
      +Intent intent = new Intent(Intent.ACTION_INSERT)
      +        .setData(Events.CONTENT_URI)
      +        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
      +        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
      +        .putExtra(Events.TITLE, "Yoga")
      +        .putExtra(Events.DESCRIPTION, "Group class")
      +        .putExtra(Events.EVENT_LOCATION, "The gym")
      +        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
      +        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
      +startActivity(intent);
      +
      + +

      使用意圖編輯活動

      + +

      您可以直接更新活動,如同更新活動所述。不過,使用 {@link +android.content.Intent#ACTION_EDIT EDIT} 意圖可以讓不具備權限的應用程式將活動編輯操作交給「日曆」應用程式。使用者在「日曆」中完成活動的編輯操作時,使用者就會回到原來的應用程式。 + + +

      以下的意圖範例會為指定的活動設定名稱,然後讓使用者在「日曆」中編輯此活動。 +

      + + +
      long eventID = 208;
      +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
      +Intent intent = new Intent(Intent.ACTION_EDIT)
      +    .setData(uri)
      +    .putExtra(Events.TITLE, "My New Title");
      +startActivity(intent);
      + +

      使用意圖檢視日曆資料

      +

      「日曆供應程式」提供兩種不同的方式來使用 {@link android.content.Intent#ACTION_VIEW VIEW} 意圖:

      +
        +
      • 開啟「日曆」到特定的日期。
      • +
      • 檢視某個活動。
      • + +
      +

      以下的範例顯示如何開啟「日曆」到特定的日期:

      +
      // A date-time specified in milliseconds since the epoch.
      +long startMillis;
      +...
      +Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
      +builder.appendPath("time");
      +ContentUris.appendId(builder, startMillis);
      +Intent intent = new Intent(Intent.ACTION_VIEW)
      +    .setData(builder.build());
      +startActivity(intent);
      + +

      以下的範例顯示如何開啟某個活動以便檢視。

      +
      long eventID = 208;
      +...
      +Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
      +Intent intent = new Intent(Intent.ACTION_VIEW)
      +   .setData(uri);
      +startActivity(intent);
      +
      + + +

      同步配接器

      + + +

      應用程式和同步配接器存取「日曆供應程式」的方式只有些微差異: +

      + +
        +
      • 同步配接器需要透過將 {@link android.provider.CalendarContract#CALLER_IS_SYNCADAPTER} 設定為 true,以指出這是同步配接器。
      • + + +
      • 同步配接器需要在 URI 中提供 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_NAME} 和 {@link +android.provider.CalendarContract.SyncColumns#ACCOUNT_TYPE} 作為查詢參數。
      • + +
      • 同步配接器與應用程式或小工具相比,有更多欄的寫入權限。 + 例如,應用程式只能修改日曆的一些特性,例如名稱、顯示名稱、能見度設定,以及日曆是否同步。 + +相較之下,同步配接器不只能存取這些欄,還可以存取很多其他欄,例如日曆色彩、時區、存取級別、位置等等。然而,同步配接器受限於所指定的 ACCOUNT_NAME 和 +ACCOUNT_TYPE。 + +
      + +

      以下是您可用於傳回 URI 的協助程式方法,以便與同步配接器搭配使用:

      +
       static Uri asSyncAdapter(Uri uri, String account, String accountType) {
      +    return uri.buildUpon()
      +        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
      +        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
      +        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
      + }
      +
      +

      如需實作同步配接器的範例 (並非與「日曆」特別相關),請參閱 +SampleSyncAdapter。 diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..b5f888012eed280ec7edb763c824179e49512d33 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/contacts-provider.jd @@ -0,0 +1,2356 @@ +page.title=聯絡人供應程式 +@jd:body +

      +
      +

      快速檢視

      +
        +
      • Android 關於人員資訊的存放庫。
      • +
      • + 與網頁同步。 +
      • +
      • + 整合社交串流資料。 +
      • +
      +

      本文件內容

      +
        +
      1. + 聯絡人供應程式組織架構 +
      2. +
      3. + 原始聯絡人 +
      4. +
      5. + 資料 +
      6. +
      7. + 聯絡人 +
      8. +
      9. + 來自同步配接器的資料 +
      10. +
      11. + 必要權限 +
      12. +
      13. + 使用者設定檔 +
      14. +
      15. + 聯絡人供應程式中繼資料 +
      16. +
      17. + 聯絡人供應程式存取 +
      18. +
      19. +
      20. + 聯絡人供應程式同步配接器 +
      21. +
      22. + 社交串流資料 +
      23. +
      24. + 其他聯絡人供應程式功能 +
      25. +
      +

      重要類別

      +
        +
      1. {@link android.provider.ContactsContract.Contacts}
      2. +
      3. {@link android.provider.ContactsContract.RawContacts}
      4. +
      5. {@link android.provider.ContactsContract.Data}
      6. +
      7. {@code android.provider.ContactsContract.StreamItems}
      8. +
      +

      相關範例

      +
        +
      1. + 聯絡人管理員 + + +
      2. +
      3. + +範例同步配接器 +
      4. +
      +

      另請參閱

      +
        +
      1. + + 內容供應程式基本概念 + +
      2. +
      +
      +
      +

      + 聯絡人供應程式是強大且有彈性的 Android 元件,負責管理裝置上與人員相關的中央資料存放庫。 +聯絡人供應程式是裝置中聯絡人應用程式的資料來源,您也可以在自己的應用程式中存取其資料,並且在裝置和線上服務之間傳輸資料。 + +供應程式內含範圍寬廣的資料來源,而且嘗試管理每個人愈來愈多的資料,組織架構因而變得很複雜。 + +基於這個原因,供應程式的 API 包括豐富的合約類別和介面,可幫助資料的擷取和修改。 + + +

      +

      + 本指南描述以下各項: +

      +
        +
      • + 基本的供應程式結構。 +
      • +
      • + 如何從供應程式擷取資料。 +
      • +
      • + 如何修改供應程式中的資料。 +
      • +
      • + 如何針對從伺服器到聯絡人供應程式的資料同步編寫同步配接器。 + +
      • +
      +

      + 本指南假設您瞭解 Android 內容供應程式基本概念。如要更瞭解 Android 內容供應程式的詳細資訊,請閱讀內容供應程式基本概念指南。 + + +範例同步配接器範例應用程式是使用同步配接器在聯絡人供應程式和 Google 網路服務代管的範例應用程式之間傳輸資料的例子。 + + + +

      +

      聯絡人供應程式組織架構

      +

      + 聯絡人供應程式是 Android 內容供應程式元件。負責維護三種與人有關的資料類型,每一種類型都會對應到供應程式所提供的表格,如圖 1 所示: + + +

      + +

      + 圖 1.聯絡人供應程式表格結構。 +

      +

      + 這三個表格通常會以其合約類別的名稱來稱呼。類別會定義內容 URI 的常數、欄名稱以及表格所使用的欄值: + +

      +
      +
      + {@link android.provider.ContactsContract.Contacts} 表格 +
      +
      + 根據原始聯絡人列的彙總,代表不同人員的列。 +
      +
      + {@link android.provider.ContactsContract.RawContacts} 表格 +
      +
      + 內含人員資料摘要的列,針對使用者帳戶和類型。 +
      +
      + {@link android.provider.ContactsContract.Data} 表格 +
      +
      + 內含原始聯絡人詳細資料的列,例如電子郵件地址或電話號碼。 +
      +
      +

      + 合約類別在 {@link android.provider.ContactsContract} 中呈現的其他表格都是輔助表格。聯絡人供應程式使用輔助表格來管理其操作,或支援裝置中聯絡人或電話應用程式的特定功能。 + + +

      +

      原始聯絡人

      +

      + 原始聯絡人是來自單一帳戶類型和帳戶名稱的人員資料。 +因為聯絡人供應程式允許一個人有多種線上服務做為資料來源,所以聯絡人供應程式可以讓同一個人有多個原始聯絡人。 + + 多個原始聯絡人也可以讓使用者,將相同帳戶類型中多個帳戶的人員資料合併。 + +

      +

      + 原始聯絡人的大部分資料不是儲存在 +{@link android.provider.ContactsContract.RawContacts} 表格,而是儲存在 {@link android.provider.ContactsContract.Data} 表格中的一或多個列。 +每個資料列都有一欄 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID},當中包含其上層資料列 {@link android.provider.ContactsContract.RawContacts} 的 {@code android.provider.BaseColumns#_ID RawContacts._ID} 值。 + + +

      +

      重要的原始聯絡人欄

      +

      + {@link android.provider.ContactsContract.RawContacts} 表格中的重要欄列於表 1。 +請詳閱表格下方的注意事項: +

      +

      + 表 1.重要的原始聯絡人欄。 +

      + + + + + + + + + + + + + + + + + + + + +
      欄名稱用途備註
      + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} + + 帳戶類型 (此原始聯絡人的來源) 的帳戶名稱。 + 例如,Google 帳戶的帳戶名稱是裝置擁有者的其中一個 Gmail 地址。 +請參閱下一個項目,進一步瞭解 + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE}。 + + + 不同的帳戶類型的名稱格式各不相同。帳戶名稱不一定是電子郵件地址。 + +
      + {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} + + 帳戶類型是此原始聯絡人的來源。例如,Google 帳戶的帳戶類型為 com.google。 +一定要將帳戶類型加上您所擁有或控制網域的網域識別碼。 +這樣可以確認您的帳戶類型是唯一的。 + + + 提供聯絡人資料的帳戶類型通常有關聯的同步配接器,可以與聯絡人供應程式同步。 + +
      + {@link android.provider.ContactsContract.RawContactsColumns#DELETED} + + 原始聯絡人的「已刪除」旗標。 + + 此旗標讓聯絡人供應程式可以在內部維護列,直到同步配接器從其伺服器刪除該列,最後從存放庫刪除該列。 + + +
      +

      備註

      +

      + 以下是有關 + {@link android.provider.ContactsContract.RawContacts} 表格的注意事項: +

      +
        +
      • + 原始聯絡人的名稱不是儲存於本身在 +{@link android.provider.ContactsContract.RawContacts} 的列,而是儲存在 {@link android.provider.ContactsContract.Data} 表格的 + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列中。 +原始聯絡人在 {@link android.provider.ContactsContract.Data} 表格中只有一列這種類型。 + +
      • +
      • + 注意:如要使用您在原始聯絡人列中自己的帳戶資料,必須先向 {@link android.accounts.AccountManager} 註冊。 +如要註冊,請提示使用者將帳戶類型及其帳戶名稱新增至帳戶清單。 +如果沒有這麼做,聯絡人供應程式將自動刪除您的原始聯絡人列。 + +

        + 例如,如果您希望應用程式維護網域為 {@code com.example.dataservice} 網頁式服務的聯絡人資料,此服務的使用者帳戶是 {@code becky.sharp@dataservice.example.com},這名使用者必須先新增帳戶「類型」({@code com.example.dataservice}) 和帳戶「名稱」 +({@code becky.smart@dataservice.example.com}),您的應用程式才能新增原始聯絡人列。 + + + + 您可以在文件中向使用者說明此需求,或提示使用者同時新增類型和名稱。 +帳戶類型和帳戶名稱在下一節有更詳細的說明。 + +

      • +
      +

      原始聯絡人資料的來源

      +

      + 如要瞭解原始聯絡人的運作方式,假設一位使用者 Emily Dickinson 在其裝置上定義了下列三個使用者帳戶: + +

      +
        +
      • emily.dickinson@gmail.com
      • +
      • emilyd@gmail.com
      • +
      • Twitter 帳戶「belle_of_amherst」
      • +
      +

      + 此使用者在「帳戶」設定中為這三個帳戶啟用了「同步聯絡人」。 + +

      +

      + 假設 Emily Dickinson 開啟瀏覽器視窗,使用 + emily.dickinson@gmail.com登入 Gmail,開啟[聯絡人] 並新增「Thomas Higginson」。 +接著,她使用 + emilyd@gmail.com登入 Gmail,並寄送電子郵件給「Thomas Higginson」(系統已自動將他新增為聯絡人)。 +她也在 Twitter 上關注「colonel_tom」(Thomas Higginson 的 Twitter ID)。 + +

      +

      + 聯絡人供應程式建立三個原始聯絡人的方式如下: +

      +
        +
      1. + 「Thomas Higginson」的原始聯絡人與 emily.dickinson@gmail.com 相關聯。 + 該使用者帳戶類型為 Google。 +
      2. +
      3. + 「Thomas Higginson」的第二個原始聯絡人與 emilyd@gmail.com 相關聯。 + 該使用者帳戶類型也是 Google。儘管第二個原始聯絡人的名稱與前一個名稱完全相同,但此人是針對不同使用者帳戶所新增的。 + + +
      4. +
      5. + 「Thomas Higginson」的第三個原始聯絡人與「belle_of_amherst」相關聯。該使用者帳戶類型是 Twitter。 + +
      6. +
      +

      資料

      +

      + 如前所述,原始聯絡人的資料是儲存在 + {@link android.provider.ContactsContract.Data} 列,而此列會連結到原始聯絡人的 + _ID 值。這樣讓原始聯絡人的相同資料類型可以有多個執行個體,例如電子郵件地址或電話號碼。 +例如,如果{@code emilyd@gmail.com} 的 "Thomas Higginson" (Thomas Higginson 的原始聯絡人列,與 Google 帳戶 emilyd@gmail.com 關聯) 的住家電子郵件地址為 + thigg@gmail.com,而工作電子郵件地址為 + thomas.higginson@gmail.com,則聯絡人供應程式會儲存這兩個電子郵件地址列,並將兩者連結到原始聯絡人。 + + + +

      +

      + 請注意,此單一表格中儲存了不同類型的資料。{@link android.provider.ContactsContract.Data} 表格中可以看到顯示名稱、電話號碼、電子郵件、郵寄地址、相片以及網站詳細資料等列。 + +為了協助管理這些內容, +{@link android.provider.ContactsContract.Data} 表格有一些欄附有描述性名稱,而其他欄則有一般名稱。 +描述性名稱欄的內容有相同的意義 (無論列中的資料類型為何),而一般名稱欄的內容則視資料的類型會有不同的意義。 + + +

      +

      描述性欄名稱

      +

      + 以下提供幾個描述性欄名稱範例: +

      +
      +
      + {@link android.provider.ContactsContract.Data#RAW_CONTACT_ID} +
      +
      + 原始聯絡人 _ID 欄的資料值。 +
      +
      + {@link android.provider.ContactsContract.Data#MIMETYPE} +
      +
      + 儲存在此列中的資料類型,以自訂 MIME 類型表示。聯絡人供應程式會使用 + {@link android.provider.ContactsContract.CommonDataKinds} 子類別中定義的MIME 類型。 +這些 MIME 類型屬開放原始碼,可以和聯絡人供應程式搭配的任何應用程式或同步配接器都可以加以使用。 + +
      +
      + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} +
      +
      + 如果原始聯絡人的此類型資料列出現多次,則 + {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 欄會在包含主要資料類型的資料列加上旗標。 +例如,使用者長按聯絡人的電話號碼並選取 [設為預設值],則包含該號碼的 {@link android.provider.ContactsContract.Data} 列就會將它的 {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 欄設為零以外的值。 + + + + +
      +
      +

      一般欄名稱

      +

      + 一般欄有 15 個 (名稱為 DATA1 到 + DATA15),系統通常會提供這些欄。另外有 4 個一般欄(SYNC1SYNC4) 只能透過同步配接器使用。 + +不管列中的資料類型為何,一定可以使用一般欄名稱常數。 + +

      +

      + DATA1 欄會建立索引。聯絡人供應程式一律會使用此欄的資料,而且供應程式預期此欄為最常查詢的目標。 +例如,在電子郵件列中,此欄內含實際的電子郵件地址。 + +

      +

      + 一般來說,DATA15 欄會保留用於儲存「二進位大型物件」(BLOB) 資料,例如相片縮圖。 + +

      +

      類型特定的欄名稱

      +

      + 為了要協助欄處理具有特定類型的列,聯絡人供應程式也提供類型特定的欄名稱常數。這些常數會在 + {@link android.provider.ContactsContract.CommonDataKinds}的子類別中定義。 +常數只是為相同欄名稱指定不同的常數名稱,以協助您存取列中特定類型的資料。 + + +

      +

      + 例如,{@link android.provider.ContactsContract.CommonDataKinds.Email} 類別定義了 {@link android.provider.ContactsContract.Data} 列的類型特定欄名稱常數。此列內含 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE}。 + + +類別含有電子郵件地址欄的常數 + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS}。 + + {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS}的實際值是「data1」。此值與欄的一般名稱相同。 + +

      +

      + 注意:如果 + {@link android.provider.ContactsContract.Data} 表格使用供應程式預先定義 MIME 類型的其中一種,請不要將您自訂的資料新增至此表格。 +假如將您自訂的資料新增至此表格,可能會遺失資料或讓供應程式發生故障。 +例如,您不應該將含有 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE +Email.CONTENT_ITEM_TYPE} (內含使用者名稱,而不是電子郵件地址) 的列新增至 DATA1 欄。 + +如果您的列使用自訂 MIME 類型,那麼您可以自行定義專屬的類型特定的欄名稱,並按照您的需求使用這些欄。 + +

      +

      + 圖 2 顯示描述性欄和資料欄顯示在 + {@link android.provider.ContactsContract.Data} 列的樣式,以及類型特定欄名稱與一般欄名稱的「重疊」方式。 + +

      +How type-specific column names map to generic column names +

      + 圖 2.特定類型欄名稱與一般欄名稱。 +

      +

      特定類型欄名稱類別

      +

      + 表 2 列出最常用的特定類型欄名稱類別: +

      +

      + 表 2.特定類型欄名稱類別

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      對應類別資料類型備註
      {@link android.provider.ContactsContract.CommonDataKinds.StructuredName}原始聯絡人 (與此資料列相關聯) 的名稱資料。原始聯絡人只有一列此資料。
      {@link android.provider.ContactsContract.CommonDataKinds.Photo}原始聯絡人 (與此資料列相關聯) 的主要相片。原始聯絡人只有一列此資料。
      {@link android.provider.ContactsContract.CommonDataKinds.Email}原始聯絡人 (與此資料列相關聯) 的電子郵件地址。原始聯絡人可以有多個電子郵件地址。
      {@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal}原始聯絡人 (與此資料列相關聯) 的郵寄地址。原始聯絡人可以有多個郵寄地址。
      {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership}將原始聯絡人連結至聯絡人供應程式內其中一個群組的識別碼。 + 群組是帳戶類型和帳戶名稱的選用功能。如要進一步瞭解群組,請參閱聯絡人群組。 + +
      +

      聯絡人

      +

      + 聯絡人供應程式會合併所有帳戶類型和帳戶名稱的原始聯絡人,而成為「聯絡人」。 +藉此協助使用者顯示及修改針對某個人所收集的所有資料。 +聯絡人供應程式負責建立新的聯絡人列,以及彙總原始聯絡人與現有的聯絡人列。 +應用程式或同步配接器都可以新增聯絡人,而聯絡人列中的某些欄屬於唯讀性質。 + +

      +

      + 注意:如果您嘗試使用 + {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()} 將聯絡人新增至聯絡人供應程式,將會收到 {@link java.lang.UnsupportedOperationException} 例外狀況。 +如果您試著更新列為「唯讀」的欄,則會略過此更新作業。 + +

      +

      + 如果新增的原始聯絡人與現有的聯絡人都不相符,聯絡人供應程式就會建立新的聯絡人。 +如果現有原始聯絡人的資料在變更後,不再與之前附加的聯絡人相符,則供應程式也會建立新的聯絡人。 + +如果應用程式或同步配接器建立的新原始聯絡人「符合」現有的聯絡人,則新的原始聯絡人會彙總為現有的聯絡人。 + + +

      +

      + 聯絡人供應程式使用 {@link android.provider.ContactsContract.Contacts Contacts} 表格中的聯絡人 + _ID 欄,將聯絡人列連結到其原始聯絡人列。 +原始聯絡人表格 + {@link android.provider.ContactsContract.RawContacts} 的 CONTACT_ID 欄,內含聯絡人列 (與每個原始聯絡人列相關聯) 的 _ID 值。 + +

      +

      + {@link android.provider.ContactsContract.Contacts} 表格也有 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 欄,此為聯絡人列的「永久」連結。 +因為聯絡人供應程式會自動維護聯絡人,它會變更聯絡人列的 {@code android.provider.BaseColumns#_ID} 值,以回應彙總或同步操作。 + +即使發生這種情況,與聯絡人的 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 合併的內容 URI {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} 仍會指向聯絡人列,因此,您可以使用 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} + 來維護「常用聯絡人」等聯絡人的連結。 + +此欄有自己的格式,與 {@code android.provider.BaseColumns#_ID} 欄的格式無關。 + +

      +

      + 圖 3 說明這三個主要表格彼此之間的關係。 +

      +Contacts provider main tables +

      + 圖 3.聯絡人、原始聯絡人以及詳細資料表格的關係。 +

      +

      來自同步配接器的資料

      +

      + 使用者將聯絡人資料直接輸入裝置,但資料也會過「同步配接器」從網路服務流入聯絡人供應程式 (同步配接器會自動將資料在裝置和服務之間傳輸)。 + +同步配接器受到系統的控制、在背景執行,並且會呼叫 {@link android.content.ContentResolver} 方法來管理資料。 + + +

      +

      + 在 Android 中,與同步配接器搭配運作的網路服務,是透過帳戶類型加以識別。 + 每個同步配接器會與一種帳戶類型搭配,但可以支援該類型的多個帳戶名稱。 +帳戶類型和帳戶名稱在原始聯絡人資料的來源中會有更詳細的說明。 + 下列定義提供更詳細的資訊,說明帳戶類型和名稱與同步配接器和服務之間的關係。 + +

      +
      +
      + 帳戶類型 +
      +
      + 識別使用者儲存資料的服務。在大部分情況下,使用者必須經過服務的驗證。 +例如,Google 聯絡人是一種帳戶類型,由程式碼 google.com 加以識別。 +此值會對應到 + {@link android.accounts.AccountManager} 所使用的帳戶類型。 +
      +
      + 帳戶名稱 +
      +
      + 識別特定帳戶或帳戶類型的登入。Google 聯絡人帳戶與 Google 帳戶相同,都是使用電子郵件地址做為帳戶名稱。 + + 其他服務可能是以單一文字使用者名稱或數值 ID 做為帳戶名稱。 +
      +
      +

      + 帳戶類型不必是唯一的。使用者可以設定多個 Google 聯絡人帳戶,並將其資料下載至聯絡人供應程式;如果使用者有一組個人帳戶名稱的個人聯絡人,還有另一組工作用的聯絡人,就可能發生此情形。 + +帳戶名稱通常是唯一的。 +兩者加起來,就可以識別聯絡人供應程式和外部服務之間的特定資料流程。 + +

      +

      + 如果您要將服務的資料傳輸到聯絡人供應程式,則需要編寫您自己的同步配接器。 +如要進一步瞭解同步配接器,請參閱聯絡人供應程式同步配接器。 + +

      +

      + 圖 4 顯示聯絡人供應程式在人員相關的資料流程中所扮演的角色。 +在標記為「同步配接器」的方塊中,每個配接器都以其帳戶類型做為標籤。 +

      +Flow of data about people +

      + 圖 4.聯絡人供應程式資料流程。 +

      +

      必要權限

      +

      + 要存取聯絡人供應程式的應用程式必須要求下列權限: + +

      +
      +
      一或多份表格的讀取權限
      +
      + {@link android.Manifest.permission#READ_CONTACTS},在 + AndroidManifest.xml 的 + + <uses-permission> 元素中,以 + <uses-permission android:name="android.permission.READ_CONTACTS"> 指定。 +
      +
      一或多份表格的寫入權限
      +
      + {@link android.Manifest.permission#WRITE_CONTACTS},在 +AndroidManifest.xml 的 + + <uses-permission> 元素中,以 +<uses-permission android:name="android.permission.WRITE_CONTACTS"> 指定。 +
      +
      +

      + 這些權限不會延伸到使用者設定檔資料。如要瞭解使用者設定檔及其必要權限,請參閱使用者設定檔。 + + +

      +

      + 請記住,使用者的聯絡人資料是個人的敏感資訊。使用者很關心隱私權相關的問題,因此不希望應用程式收集使用者本身或其聯絡人的相關資訊。 + + 如果沒有明確說明為何需要存取使用者的聯絡人資料,使用者可能會給您的應用程式低評分,或直接拒絕安裝。 + +

      +

      使用者設定檔

      +

      + {@link android.provider.ContactsContract.Contacts} 表格中有一列內含裝置使用者的設定檔資料, +此資料描述裝置的 user,而不是其中一位使用者的聯絡人。 +針對使用設定檔的每個系統,設定檔聯絡人列會連結到原始聯絡人列。 + + 每個設定檔原始聯絡人列可以有多個資料列。{@link android.provider.ContactsContract.Profile} 類別中提供存取使用者設定檔的常數。 + +

      +

      + 存取使用者設定檔需要特殊權限。除了 +{@link android.Manifest.permission#READ_CONTACTS} 和 +{@link android.Manifest.permission#WRITE_CONTACTS} 的讀取和寫入權限以外,存取使用者設定檔還分別需要 {@code android.Manifest.permission#READ_PROFILE} 讀取權限和 +{@code android.Manifest.permission#WRITE_PROFILE} 寫入權限。 + + +

      +

      + 請務必將使用者的設定檔視為敏感資訊。 +{@code android.Manifest.permission#READ_PROFILE}權限可以讓您存取裝置上使用者的身分識別資料。 +請務必在應用程式的簡介中告訴使用者,為何需要使用者設定檔存取權限。 + +

      +

      + 如要擷取內含使用者的設定檔的聯絡人列,請呼叫 {@link android.content.ContentResolver#query(Uri,String[], String, String[], String) +ContentResolver.query()}。 +將內容 URI 設為 +{@link android.provider.ContactsContract.Profile#CONTENT_URI},並且不要提供任何選取條件。 +您也可以使用此內容 URI 做為擷取原始聯絡人或設定檔資料的基礎 URI。 +例如,以下程式碼片段會擷取設定檔資料: +

      +
      +// Sets the columns to retrieve for the user profile
      +mProjection = new String[]
      +    {
      +        Profile._ID,
      +        Profile.DISPLAY_NAME_PRIMARY,
      +        Profile.LOOKUP_KEY,
      +        Profile.PHOTO_THUMBNAIL_URI
      +    };
      +
      +// Retrieves the profile from the Contacts Provider
      +mProfileCursor =
      +        getContentResolver().query(
      +                Profile.CONTENT_URI,
      +                mProjection ,
      +                null,
      +                null,
      +                null);
      +
      +

      + 注意:如果您擷取了多個聯絡人列,而想要判斷其中之一是否為使用者設定檔,請測試該列的 +{@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} 欄。 +如果聯絡人為使用者設定檔,此欄會設為「1」。 + +

      +

      聯絡人供應程式中繼資料

      +

      + 聯絡人供應程式管理的資料可以追蹤存放庫中聯絡人資料的 +狀態。存放庫相關的中繼資料儲存在不同的位置,包括 +「原始聯絡人」、「資料」以及「聯絡人」表格列, +{@link android.provider.ContactsContract.Settings} 表格以及 +{@link android.provider.ContactsContract.SyncState} 表格。以下表格說明 +這些中繼資料的作用: +

      +

      + 表 3.聯絡人供應程式的中繼資料

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      表格意義
      {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#DIRTY}「0」:上次同步後沒有變更。 + 標記裝置上經過變更,且必須同步回伺服器的原始聯絡人。 +Android 應用程式更新列時,聯絡人供應程式會自動設定此值。 + +

      + 修改原始聯絡人或資料表格的同步配接器一律會將字串{@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER} 附加到其使用的內容 URI, + +藉此防止供應程式將列標記為已變更 (dirty)。 + 否則,同步配接器修改會顯示為本機修改,因而傳送到伺服器,儘管伺服器才是修改的來源。 + +

      +
      「1」:上次同步後已變更,需要同步回伺服器。
      {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#VERSION}此列的版本號碼。 + 每當列或其相關資料變更時,聯絡人供應程式都會自動增加此值。 + +
      {@link android.provider.ContactsContract.Data}{@link android.provider.ContactsContract.DataColumns#DATA_VERSION}此列的版本號碼。 + 每當資料列變更時,聯絡人供應程式都會自動增加此值。 + +
      {@link android.provider.ContactsContract.RawContacts}{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} + 可唯一識別帳戶原始聯絡人的字串值(此帳戶是以此字串值所建立)。 + + + 同步配接器建立新的原始聯絡人時,此欄應設為原始聯絡人的伺服器唯一 ID。 +Android 應用程式建立新的原始聯絡人時,應用程式應將此欄保留空白。 +這樣會提供訊號給同步配接器,要在伺服器上建立新的原始聯絡人,然後取得 +{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} 值。 + +

      + 尤其是來源 ID 對於每個帳戶類型必須具備唯一性,在同步時應該很穩定: + +

      +
        +
      • + 唯一:帳戶的每個原始聯絡人都必須有自己的來源 ID。如果沒有強制執行此條件,則聯絡人應用程式會發生問題。 + + 請注意,同一個帳戶「類型」的兩個原始聯絡人可能會有相同的來源 ID。 +例如,{@code emily.dickinson@gmail.com} 帳戶的原始聯絡人「Thomas Higginson」與 + {@code emilyd@gmail.com} 帳戶的原始聯絡人「Thomas Higginson」的來源 ID相同。 + + +
      • +
      • + 穩定:來源 ID 是線上服務中原始聯絡人資料的永久部分。 +例如,如果使用者從應用程式設定中清除「聯絡人儲存空間」,然後重新同步,則還原的原始聯絡人的來源 ID 應該與先前相同。 + +如果沒有強制執行此條件,捷徑將停止運作。 + +
      • +
      +
      {@link android.provider.ContactsContract.Groups}{@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE}「0」:Android 應用程式 UI 中不應顯示此群組中的聯絡人。 + 有些伺服器可以讓使用者隱藏某些群組中的聯絡人,此欄的設計提供了與這類伺服器的相容性。 + +
      「1」:應用程式 UI 中會顯示此群組中的聯絡人。
      {@link android.provider.ContactsContract.Settings} + {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} + 「0」:針對此帳戶和帳戶類型,Android 應用程式 UI 中不會顯示不屬於群組的聯絡人。 + + + 如果沒有任何原始聯絡人屬於某個群組,則聯絡人預設為不可見(原始聯絡人的群組成員資格是由{@link android.provider.ContactsContract.Data} 表格中的一或多個 +{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} 列所指出)。 + + + 在 {@link android.provider.ContactsContract.Settings} 表格列中為帳戶類型和帳戶設定此旗標,可以強制讓不屬於任何群組的聯絡人成為可見的。 + + 此旗標的其中一個用途是,顯示伺服器中不屬於任何群組的聯絡人。 +
      + 「1」:針對此帳戶和帳戶類型,應用程式 UI 中會顯示不屬於群組的聯絡人。 + +
      {@link android.provider.ContactsContract.SyncState}(全部) + 使用此表格儲存同步配接器的中繼資料。 + + 使用此表格,您可以儲存同步狀態,以及其他與同步相關、會永久放在裝置上的資料。 + +
      +

      聯絡人供應程式存取

      +

      + 本節說明從聯絡人供應程式存取資料的指導方針,著重於以下各項: + +

      +
        +
      • + 實體查詢。 +
      • +
      • + 批次修改。 +
      • +
      • + 使用意圖進行擷取及修改。 +
      • +
      • + 資料完整性。 +
      • +
      +

      + 從同步配接器進行修改,在聯絡人供應程式同步配接器中也提供更詳細的資訊。 + +

      +

      實體查詢

      +

      + 因為聯絡人供應程式為階層式表格,擷取某一列及連結至此列的所有「子」列時非常實用。 +例如,如要顯示人員的所有資訊,您可能要擷取單一 + {@link android.provider.ContactsContract.Contacts} 列的 +所有 {@link android.provider.ContactsContract.RawContacts} 列,或單一 +{@link android.provider.ContactsContract.RawContacts} 列的所有 +{@link android.provider.ContactsContract.CommonDataKinds.Email} 列。 +為了協助此操作,聯絡人供應程式提供實體建構,其運作方式就像是資料庫結合各個表格一樣。 + + +

      +

      + 實體就像是一份表格,由上層表格及其下層表格中的選取欄所組成。 + 查詢實體時,您會提供投影 (projection) 和搜尋條件根據該實體可用的欄。 +結果會是 {@link android.database.Cursor},擷取到的每個下層表格列在其中都會有一列。 +例如,如果您查詢 +{@link android.provider.ContactsContract.Contacts.Entity} 的聯絡人名稱,並且查詢所有 {@link android.provider.ContactsContract.CommonDataKinds.Email} 列中該名稱的所有原始聯絡人,則會取回 {@link android.database.Cursor},每個 {@link android.provider.ContactsContract.CommonDataKinds.Email} 列都會有一列。 + + + +

      +

      + 實體簡化查詢。使用實體,您可以一次擷取聯絡人或原始聯絡人的所有聯絡人資料,而不用先查詢父項表格以取得 ID,再以此 ID 查詢子項表格。另外,聯絡人供應程式會在單一交易中處理針對實體的查詢,以確保所擷取的資料在內部的一致性。 + + + + +

      +

      + 注意:實體通常不會包含上層表格和下層表格的所有欄。 +如果您嘗試使用的欄名稱未列在實體的欄名稱常數中,將會收到 {@link java.lang.Exception}。 + +

      +

      + 以下程式碼片段展示如何擷取一位聯絡人的所有原始聯絡人列。此程式碼片段屬於大型應用程式的一部分,此應用程式有兩個 Activity:「主要」和「詳細」。 +主要 Activity 會顯示聯絡人列的清單,當使用者選取其中一項時,此 Activity 會將其 ID 傳送給詳細 Activity。 + +詳細 Activity 會使用 {@link android.provider.ContactsContract.Contacts.Entity},針對所選取的聯絡人,顯示與其關聯的所有原始聯絡人的所有資料列。 + + +

      +

      + 此程式碼片段是取自「詳細」Activity: +

      +
      +...
      +    /*
      +     * Appends the entity path to the URI. In the case of the Contacts Provider, the
      +     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
      +     */
      +    mContactUri = Uri.withAppendedPath(
      +            mContactUri,
      +            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
      +
      +    // Initializes the loader identified by LOADER_ID.
      +    getLoaderManager().initLoader(
      +            LOADER_ID,  // The identifier of the loader to initialize
      +            null,       // Arguments for the loader (in this case, none)
      +            this);      // The context of the activity
      +
      +    // Creates a new cursor adapter to attach to the list view
      +    mCursorAdapter = new SimpleCursorAdapter(
      +            this,                        // the context of the activity
      +            R.layout.detail_list_item,   // the view item containing the detail widgets
      +            mCursor,                     // the backing cursor
      +            mFromColumns,                // the columns in the cursor that provide the data
      +            mToViews,                    // the views in the view item that display the data
      +            0);                          // flags
      +
      +    // Sets the ListView's backing adapter.
      +    mRawContactList.setAdapter(mCursorAdapter);
      +...
      +@Override
      +public Loader<Cursor> onCreateLoader(int id, Bundle args) {
      +
      +    /*
      +     * Sets the columns to retrieve.
      +     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
      +     * DATA1 contains the first column in the data row (usually the most important one).
      +     * MIMETYPE indicates the type of data in the data row.
      +     */
      +    String[] projection =
      +        {
      +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
      +            ContactsContract.Contacts.Entity.DATA1,
      +            ContactsContract.Contacts.Entity.MIMETYPE
      +        };
      +
      +    /*
      +     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
      +     * contact collated together.
      +     */
      +    String sortOrder =
      +            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
      +            " ASC";
      +
      +    /*
      +     * Returns a new CursorLoader. The arguments are similar to
      +     * ContentResolver.query(), except for the Context argument, which supplies the location of
      +     * the ContentResolver to use.
      +     */
      +    return new CursorLoader(
      +            getApplicationContext(),  // The activity's context
      +            mContactUri,              // The entity content URI for a single contact
      +            projection,               // The columns to retrieve
      +            null,                     // Retrieve all the raw contacts and their data rows.
      +            null,                     //
      +            sortOrder);               // Sort by the raw contact ID.
      +}
      +
      +

      + 載入完成時,{@link android.app.LoaderManager} 會呼叫 {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) + onLoadFinished()} 的回呼。 +此方法的其中一個傳入引數是內含查詢結果的 + {@link android.database.Cursor}。在您的應用程式中,您可以從這個 {@link android.database.Cursor} 取得資料,然後加以顯示或進一步處理。 + +

      +

      批次修改

      +

      + 您應該儘可能透過建立 + {@link android.content.ContentProviderOperation} 物件的{@link java.util.ArrayList},然後呼叫 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()},以「批次模式」在聯絡人供應程式中進行資料的插入、更新以及刪除。 +因為聯絡人供應程式會在單一交易中執行 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的所有操作,所以您所做的修改不會讓聯絡人存放庫處於不一致的狀態。 + + +批次修改同時也有助於插入原始聯絡人及其詳細資料。 + +

      +

      + 注意:如要修改「單一」原始聯絡人,請考慮將意圖傳送到裝置的聯絡人應用程式,而不要在您的應用程式中處理修改操作。這些動作在使用意圖擷取和修改中有更詳細的資料。 + + + +

      +

      降伏點

      +

      + 包含大量操作的批次修改可能會封鎖其他處理程序,導致整體的使用者體驗不良。 +如要將您想要執行的所有修改,儘可能安排在較少的清單中執行,同時要避免這些修改讓系統無法進行其他操作,則應該要為一或多個操作設定「降伏點」。 + + + 降伏點是一個 {@link android.content.ContentProviderOperation} 物件,而且其 +{@link android.content.ContentProviderOperation#isYieldAllowed()} 值是設為 +true。當聯絡人供應程式遇到降伏點時,會暫停它的工作,以便讓其他處理程序執行,並關閉目前的交易。 +供應程式再次啟動時,會繼續 {@link java.util.ArrayList} 中的下一項操作,並啟動新的交易。 + + +

      +

      + 降伏點會讓每次呼叫 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 產生一個以上的交易。基於這項原因,您必須將上次操作的降伏點設為一組相關的列。 + + 例如,您應該為以下兩種上次操作設定降伏點:新增原始聯絡人列及其相關資料列的一組動作,或與單一聯絡人相關的一組列。 + + +

      +

      + 降伏點也是微型操作的單位。兩個降伏點之間的所有存取會以單一單元來看待為成功或失敗。 +如果沒有設定任何降伏點,則最小的微型操作就是整批操作。 +如果使用降伏點,您可以防止操作降低系統效能,同時確保操作的子集是微型操作。 + + +

      +

      修改反向參考

      +

      + 以一組 + {@link android.content.ContentProviderOperation} 物件插入新的原始聯絡人及其關聯的資料列時,您必須透過插入原始聯絡人的 + {@code android.provider.BaseColumns#_ID} 值做為 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 值,將資料列連結到原始聯絡人列。 +不過,此 +值在您為資料列建立 {@link android.content.ContentProviderOperation} +時並不存在,這是因為您尚未替原始聯絡人列 +套用 {@link android.content.ContentProviderOperation}。為解決此狀況,{@link android.content.ContentProviderOperation.Builder} 類別提供了 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} 這個方法。 + + 此方法可以讓您使用之前操作的結果來插入或修改欄。 + +

      +

      + {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} +方法有兩個引數: +

      +
      +
      + key +
      +
      + 鍵值對的鍵。此引數的值是您要修改表格中的欄名稱。 + +
      +
      + previousResult +
      +
      + +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 中的 +{@link android.content.ContentProviderResult} 物件以 0 開始的陣列索引值。套用批次操作時,每次操作結果都會儲存在結果的中繼陣列。 + +previousResult 值是這些結果的其中一個索引,這些結果是以 key值擷取並加以儲存。 + +這樣可以讓您插入新的原始聯絡人記錄,並取得其 +{@code android.provider.BaseColumns#_ID} 值,然後在您新增 {@link android.provider.ContactsContract.Data} 列時,做為此值的「反向參考」。 + +

      + 您首次呼叫 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 時會建立整個結果陣列,此陣列的大小等同於您所提供 + {@link android.content.ContentProviderOperation} 物件的{@link java.util.ArrayList} 大小。 +不過,結果陣列中的所有元素會設為 null,如果您嘗試要針對尚未套用的操作結果製作反向參考, +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}則會擲回 {@link java.lang.Exception}。 + + + + +

      +
      +
      +

      + 以下程式碼片段展示如何插入大量新的原始聯絡人和資料。其中包括建立降伏點和使用反向參考的程式碼。 +此程式碼片段是 createContacEntry() 方法的擴充版本。而這個方法是 + + Contact Manager 範例應用程式中 + ContactAdder 類別的一部分。 + +

      +

      + 第一個程式碼片段會從 UI 擷取聯絡人資料。此時,使用者已經選好要加入的新原始聯絡人帳戶。 + +

      +
      +// Creates a contact entry from the current UI values, using the currently-selected account.
      +protected void createContactEntry() {
      +    /*
      +     * Gets values from the UI
      +     */
      +    String name = mContactNameEditText.getText().toString();
      +    String phone = mContactPhoneEditText.getText().toString();
      +    String email = mContactEmailEditText.getText().toString();
      +
      +    int phoneType = mContactPhoneTypes.get(
      +            mContactPhoneTypeSpinner.getSelectedItemPosition());
      +
      +    int emailType = mContactEmailTypes.get(
      +            mContactEmailTypeSpinner.getSelectedItemPosition());
      +
      +

      + 下一個程式碼片段的操作會將原始聯絡人列插入 +{@link android.provider.ContactsContract.RawContacts} 表格: +

      +
      +    /*
      +     * Prepares the batch operation for inserting a new raw contact and its data. Even if
      +     * the Contacts Provider does not have any data for this person, you can't add a Contact,
      +     * only a raw contact. The Contacts Provider will then add a Contact automatically.
      +     */
      +
      +     // Creates a new array of ContentProviderOperation objects.
      +    ArrayList<ContentProviderOperation> ops =
      +            new ArrayList<ContentProviderOperation>();
      +
      +    /*
      +     * Creates a new raw contact with its account type (server type) and account name
      +     * (user's account). Remember that the display name is not stored in this row, but in a
      +     * StructuredName data row. No other data is required.
      +     */
      +    ContentProviderOperation.Builder op =
      +            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
      +            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
      +            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
      +
      +    // Builds the operation and adds it to the array of operations
      +    ops.add(op.build());
      +
      +

      + 接著,程式碼會建立顯示名稱、電話以及電子郵件列的資料列。 +

      +

      + 每項操作建立器物件會使用 +{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()}來取得 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID}。 +參照會指向第一項操作中的 {@link android.content.ContentProviderResult} 物件 (第一項操作會新增原始聯絡人列,並傳回其新的 {@code android.provider.BaseColumns#_ID} 值)。 + + +因此,每個資料列會透過其 +{@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 自動連結到它所屬的新 {@link android.provider.ContactsContract.RawContacts} 列。 + +

      +

      + 新增電子郵件列的 {@link android.content.ContentProviderOperation.Builder} 物件會帶有 {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) + withYieldAllowed()} 旗標,而這會設定降伏點: + +

      +
      +    // Creates the display name for the new raw contact, as a StructuredName data row.
      +    op =
      +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
      +            /*
      +             * withValueBackReference sets the value of the first argument to the value of
      +             * the ContentProviderResult indexed by the second argument. In this particular
      +             * call, the raw contact ID column of the StructuredName data row is set to the
      +             * value of the result returned by the first operation, which is the one that
      +             * actually adds the raw contact row.
      +             */
      +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
      +
      +            // Sets the data row's MIME type to StructuredName
      +            .withValue(ContactsContract.Data.MIMETYPE,
      +                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
      +
      +            // Sets the data row's display name to the name in the UI.
      +            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
      +
      +    // Builds the operation and adds it to the array of operations
      +    ops.add(op.build());
      +
      +    // Inserts the specified phone number and type as a Phone data row
      +    op =
      +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
      +            /*
      +             * Sets the value of the raw contact id column to the new raw contact ID returned
      +             * by the first operation in the batch.
      +             */
      +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
      +
      +            // Sets the data row's MIME type to Phone
      +            .withValue(ContactsContract.Data.MIMETYPE,
      +                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
      +
      +            // Sets the phone number and type
      +            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
      +            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);
      +
      +    // Builds the operation and adds it to the array of operations
      +    ops.add(op.build());
      +
      +    // Inserts the specified email and type as a Phone data row
      +    op =
      +            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
      +            /*
      +             * Sets the value of the raw contact id column to the new raw contact ID returned
      +             * by the first operation in the batch.
      +             */
      +            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
      +
      +            // Sets the data row's MIME type to Email
      +            .withValue(ContactsContract.Data.MIMETYPE,
      +                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
      +
      +            // Sets the email address and type
      +            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
      +            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);
      +
      +    /*
      +     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
      +     * will yield priority to other threads. Use after every set of operations that affect a
      +     * single contact, to avoid degrading performance.
      +     */
      +    op.withYieldAllowed(true);
      +
      +    // Builds the operation and adds it to the array of operations
      +    ops.add(op.build());
      +
      +

      + 最後一個程式碼片段顯示 +{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的呼叫,以插入新的原始聯絡人和資料列。 + +

      +
      +    // Ask the Contacts Provider to create a new contact
      +    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
      +            mSelectedAccount.getType() + ")");
      +    Log.d(TAG,"Creating contact: " + name);
      +
      +    /*
      +     * Applies the array of ContentProviderOperation objects in batch. The results are
      +     * discarded.
      +     */
      +    try {
      +
      +            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
      +    } catch (Exception e) {
      +
      +            // Display a warning
      +            Context ctx = getApplicationContext();
      +
      +            CharSequence txt = getString(R.string.contactCreationFailure);
      +            int duration = Toast.LENGTH_SHORT;
      +            Toast toast = Toast.makeText(ctx, txt, duration);
      +            toast.show();
      +
      +            // Log exception
      +            Log.e(TAG, "Exception encountered while inserting contact: " + e);
      +    }
      +}
      +
      +

      + 批次操作也可以讓您實作開放式並行存取控制,此方法可以在套用修改交易時,不需要鎖定底層存放庫。 + + 如要使用此方法,您要套用交易,然後檢查同時發生的其他修改操作。 +如果您發現不一致的修改,請將交易復原並加以重試。 + +

      +

      + 開放式並行存取控制很適合用在行動裝置,這是因為行動裝置一次只會有一位使用者,而且很少會發生同時存取資料存放庫的情形。 +由於不會使用到鎖定,因此您不必花時間在設定鎖定,或等待其他交易釋放鎖定。 + +

      +

      + 如要在更新單一 + {@link android.provider.ContactsContract.RawContacts} 列時使用開放式並行存取控制,請遵循以下步驟: +

      +
        +
      1. + 隨著您要擷取的其他資料,一起擷取原始聯絡人的 {@link android.provider.ContactsContract.SyncColumns#VERSION} 欄。 + +
      2. +
      3. + 使用 + {@link android.content.ContentProviderOperation#newAssertQuery(Uri)} 方法建立適合用於強制執行限制的{@link android.content.ContentProviderOperation.Builder} 物件。 +如果是內容 URI,請使用 {@link android.provider.ContactsContract.RawContacts#CONTENT_URI +RawContacts.CONTENT_URI} 並附加原始聯絡人的 {@code android.provider.BaseColumns#_ID}。 + + +
      4. +
      5. + 如果是 {@link android.content.ContentProviderOperation.Builder} 物件,請呼叫 {@link android.content.ContentProviderOperation.Builder#withValue(String, Object) + withValue()},以比較 {@link android.provider.ContactsContract.SyncColumns#VERSION} 欄和您剛才擷取的版本號碼。 + + +
      6. +
      7. + 如果是相同的 {@link android.content.ContentProviderOperation.Builder},請呼叫 {@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) +withExpectedCount()} 以確保此判斷提示只測試一列。 + +
      8. +
      9. + 呼叫 {@link android.content.ContentProviderOperation.Builder#build()} 以建立 +{@link android.content.ContentProviderOperation} 物件,然後將此物件新增為您傳送到 + {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} 的第一個 {@link java.util.ArrayList} 物件。 + +
      10. +
      11. + 套用批次交易。 +
      12. +
      +

      + 如果在您讀取原始聯絡人列和嘗試加以修改之間,有另一項操作要加以更新,則「判斷提示」{@link android.content.ContentProviderOperation} 將會失敗,而且整個批次的操作將會退出。 + +您之後可以選擇重試此批次作業或採取其他動作。 + +

      +

      + 以下程式碼片段展示如何在使用 {@link android.content.CursorLoader} 查詢單一原始聯絡人後,建立「判斷提示」 +{@link android.content.ContentProviderOperation}: + +

      +
      +/*
      + * The application uses CursorLoader to query the raw contacts table. The system calls this method
      + * when the load is finished.
      + */
      +public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
      +
      +    // Gets the raw contact's _ID and VERSION values
      +    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
      +    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
      +}
      +
      +...
      +
      +// Sets up a Uri for the assert operation
      +Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);
      +
      +// Creates a builder for the assert operation
      +ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);
      +
      +// Adds the assertions to the assert operation: checks the version and count of rows tested
      +assertOp.withValue(SyncColumns.VERSION, mVersion);
      +assertOp.withExpectedCount(1);
      +
      +// Creates an ArrayList to hold the ContentProviderOperation objects
      +ArrayList ops = new ArrayList<ContentProviderOperationg>;
      +
      +ops.add(assertOp.build());
      +
      +// You would add the rest of your batch operations to "ops" here
      +
      +...
      +
      +// Applies the batch. If the assert fails, an Exception is thrown
      +try
      +    {
      +        ContentProviderResult[] results =
      +                getContentResolver().applyBatch(AUTHORITY, ops);
      +
      +    } catch (OperationApplicationException e) {
      +
      +        // Actions you want to take if the assert operation fails go here
      +    }
      +
      +

      使用意圖進行擷取及修改

      +

      + 將意圖傳送給裝置的聯絡人應用程式,可讓您間接存取聯絡人供應程式。 +意圖會啟動裝置的聯絡人應用程式 UI,使用者可以在此執行與聯絡人相關的工作。 +透過此類型的存取方式,使用者可以: +

        +
      • 從清單挑選聯絡人,並將它傳送給應用程式以進行其他操作。
      • +
      • 編輯現有的聯絡人資料。
      • +
      • 為使用者的任何帳戶插入新的原始聯絡人。
      • +
      • 刪除聯絡人或聯絡人資料。
      • +
      +

      + 如果使用者正在插入或更新資料,您可以先收集資料,然後讓它成為意圖的一部分加以傳送。 + +

      +

      + 當您透過裝置的聯絡人應用程式使用意圖來存取聯絡人供應程式時,不需要自已撰寫存取供應程式的 UI 或程式碼。 +您也不需要要求供應程式的讀取或寫入權限。 +裝置的聯絡人應用程式可以將某個聯絡人的讀取權限委派給您,而且因為是透過另一個應用程式對供應程式進行修改,所以不需要具備寫入權限。 + + +

      +

      + 傳送意圖以存取供應程式的一般流程在內容供應程式基本概念指南的「透過意圖存取資料」中有詳細的說明。 + +表 4 摘要說明您可以在工作中使用的動作、MIME 類型以及資料值,而您可以和 + {@link android.content.Intent#putExtra(String, String) putExtra()} 搭配使用的額外值則列於 {@link android.provider.ContactsContract.Intents.Insert} 的參考文件: + + + +

      +

      + 表 4.聯絡人供應程式意圖。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      工作動作資料MIME 類型備註
      從清單挑選聯絡人{@link android.content.Intent#ACTION_PICK} + 可以是以下其中一種: +
        +
      • +{@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI},可顯示聯絡人的清單。 + +
      • +
      • +{@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI},可顯示原始聯絡人的電話號碼清單。 + +
      • +
      • +{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI +StructuredPostal.CONTENT_URI},可顯示原始聯絡人的郵寄地址清單。 + +
      • +
      • +{@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI},可顯示原始聯絡人的電子郵件地址清單。 + +
      • +
      +
      + 未使用 + + 顯示原始聯絡人清單或原始聯絡人的資料清單,視您提供的內容 URI 類型而定。 + +

      + 呼叫 +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()}可傳回所選取列的內容 URI。 +URI 的格式是表格的內容 URI 附加列的 LOOKUP_ID。 + + 裝置的聯絡人應用程式會在 Activity 的生命週期內,將讀取和寫入權限委派給此內容 URI。 +詳情請參閱內容供應程式基本概念指南。 + + +

      +
      插入新的原始聯絡人{@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION}不適用 + {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE +RawContacts.CONTENT_TYPE},一組原始聯絡人的 MIME 類型。 + + 顯示裝置聯絡人應用程式中的「新增聯絡人」畫面。會顯示您新增至意圖的額外值。 +如果隨著 +{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} 一起傳送,則新增的原始聯絡人內容 URI 會在 {@link android.content.Intent} 引數的 [資料] 欄位中傳回給 Activity 的 +{@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()}回呼方法。 + + +如要取得此值,請呼叫 {@link android.content.Intent#getData()}。 +
      編輯聯絡人{@link android.content.Intent#ACTION_EDIT} + 聯絡人的 {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}。 +編輯器 Activity 可讓使用者編輯與此聯絡人關聯的任何資料。 + + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE +Contacts.CONTENT_ITEM_TYPE},單一聯絡人。 + 顯示聯絡人應用程式中的「編輯聯絡人」畫面。顯示您新增至意圖的額外值。 +使用者按一下 [完成] 來儲存編輯內容時,您的 Activity 會回到前景。 + +
      顯示也能夠新增資料的挑選器{@link android.content.Intent#ACTION_INSERT_OR_EDIT} + 不適用 + + {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} + + 此意圖一律會顯示聯絡人應用程式的挑選器畫面。使用者可以挑選要編輯的聯絡人,或新增聯絡人。 +不論使用者選擇編輯或新增,所顯示的畫面中也會顯示您在意圖中傳送的額外資料。 + +如果您的應用程式顯示電子郵件或電話號碼之類的聯絡人資料,使用此意圖會讓使用者將資料新增至現有聯絡人。 + + +

      + 注意:不需要在意圖的額外值中傳送名稱值,這是因為使用者一定會挑選現有名稱或新增名稱。 +再者,如果您傳送名稱,而使用者選擇編輯,則聯絡人應用程式會覆寫之前的值,以顯示您傳送的名稱。 + +如果使用者沒有注意到此情形並儲存本次編輯,則會遺失舊的值。 + +

      +
      +

      + 裝置的聯絡人應用程式不會讓您刪除原始聯絡人或任何含有意圖的資料。 +如要刪除原始聯絡人,請改用 +{@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} 或 {@link android.content.ContentProviderOperation#newDelete(Uri) +ContentProviderOperation.newDelete()}。 + +

      +

      + 以下程式碼片段展示如何建構及傳送可插入新原始聯絡人和資料,的意圖: + +

      +
      +// Gets values from the UI
      +String name = mContactNameEditText.getText().toString();
      +String phone = mContactPhoneEditText.getText().toString();
      +String email = mContactEmailEditText.getText().toString();
      +
      +String company = mCompanyName.getText().toString();
      +String jobtitle = mJobTitle.getText().toString();
      +
      +// Creates a new intent for sending to the device's contacts application
      +Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);
      +
      +// Sets the MIME type to the one expected by the insertion activity
      +insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
      +
      +// Sets the new contact name
      +insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);
      +
      +// Sets the new company and job title
      +insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
      +insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);
      +
      +/*
      + * Demonstrates adding data rows as an array list associated with the DATA key
      + */
      +
      +// Defines an array list to contain the ContentValues objects for each row
      +ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();
      +
      +
      +/*
      + * Defines the raw contact row
      + */
      +
      +// Sets up the row as a ContentValues object
      +ContentValues rawContactRow = new ContentValues();
      +
      +// Adds the account type and name to the row
      +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
      +rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());
      +
      +// Adds the row to the array
      +contactData.add(rawContactRow);
      +
      +/*
      + * Sets up the phone number data row
      + */
      +
      +// Sets up the row as a ContentValues object
      +ContentValues phoneRow = new ContentValues();
      +
      +// Specifies the MIME type for this data row (all data rows must be marked by their type)
      +phoneRow.put(
      +        ContactsContract.Data.MIMETYPE,
      +        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
      +);
      +
      +// Adds the phone number and its type to the row
      +phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);
      +
      +// Adds the row to the array
      +contactData.add(phoneRow);
      +
      +/*
      + * Sets up the email data row
      + */
      +
      +// Sets up the row as a ContentValues object
      +ContentValues emailRow = new ContentValues();
      +
      +// Specifies the MIME type for this data row (all data rows must be marked by their type)
      +emailRow.put(
      +        ContactsContract.Data.MIMETYPE,
      +        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
      +);
      +
      +// Adds the email address and its type to the row
      +emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
      +
      +// Adds the row to the array
      +contactData.add(emailRow);
      +
      +/*
      + * Adds the array to the intent's extras. It must be a parcelable object in order to
      + * travel between processes. The device's contacts app expects its key to be
      + * Intents.Insert.DATA
      + */
      +insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);
      +
      +// Send out the intent to start the device's contacts app in its add contact activity.
      +startActivity(insertIntent);
      +
      +

      資料完整性

      +

      + 因為聯絡人存放庫內含重要的敏感資料,使用者會期待這些資料為正確且為最新狀態,聯絡人供應程式對於資料完整性有定義良好的規則。 +因此,您在修改聯絡人資料時,必須符合這些規則。 +以下列出重要規則: + +

      +
      +
      + 務必為您新增的每個 {@link android.provider.ContactsContract.RawContacts} 列新增 {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列。 + +
      +
      + {@link android.provider.ContactsContract.Data} 表格中不含 + {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 列的 +{@link android.provider.ContactsContract.RawContacts} 列,在彙總時會造成問題。 + +
      +
      + 務必將 {@link android.provider.ContactsContract.Data} 列連結到其上層的 +{@link android.provider.ContactsContract.RawContacts} 列。 +
      +
      + 裝置的聯絡人應用程式中將看不到未連結到 + {@link android.provider.ContactsContract.RawContacts} 的 {@link android.provider.ContactsContract.Data} 列,而且與同步配接器搭配使用時可能會造成問題。 + +
      +
      + 只針對您擁有的原始聯絡人變更資料。 +
      +
      + 請記住,聯絡人供應程式通常用來管理來自不同帳戶類型或線上服務的資料。 +您必須確認應用程式只會修改或刪除屬於您的資料列,並且確認應用程式插入的資料只含有您可控制的帳戶類型和名稱。 + + +
      +
      + 務必使用 {@link android.provider.ContactsContract} 及其子類別中定義的常數,做為授權、內容 URI、URI 路徑、欄名稱、MIME 類型以及 +{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} 值。 + +
      +
      + 使用這些常數可協助您避免發生錯誤。如果有任何常數已失效,則編譯器會發出通知。 + +
      +
      +

      自訂資料列

      +

      + 透過建立自訂的 MIME 類型,您可以插入、編輯、刪除以及擷取 {@link android.provider.ContactsContract.Data} 表格中您自己的資料列。 +儘管您可以將自己的類型特定欄名稱對應到預設的欄名稱,您的列仍受限於使用 + {@link android.provider.ContactsContract.DataColumns} 中所定義的欄。 + +在裝置的聯絡人應用程式中,可以顯示您的列中資料,但無法加以編輯或刪除,而且使用者無法新增其他資料。 + +如要讓使用者修改您自訂的資料列,您必須在自己的應用程式中提供編輯器 Activity。 + +

      +

      + 如要顯示自己的資料,請提供 contacts.xml 檔案,其中要包含 +<ContactsAccountType> 元素以及一或多個其 +<ContactsDataKind> 子元素。詳情請參閱 <ContactsDataKind> element一節。 + +

      +

      + 如要更瞭解自訂 MIME 類型的詳細資訊,請閱讀建立內容供應程式指南。 + + +

      +

      聯絡人供應程式同步配接器

      +

      + 聯絡人供應程式的設計是專門用來處理裝置和線上服務之間聯絡人資料的「同步作業」。 +以便讓使用者將現有資料下載到新裝置,以及將現有資料上傳到新帳戶。 + + 同步作業也可以確保使用者手邊使用的是最新的資料,不論來源經過哪些新增和變更。 +同步作業的另一個好處是,即使裝置沒有連上網路,使用者仍然可以存取聯絡人資料。 + +

      +

      + 您可以用各種方式實作同步作業,不過 Android 系統提供的外掛程式同步架構可以將以下工作自動化: + +

        + +
      • + 檢查網路可用性。 +
      • +
      • + 根據使用者偏好設定,安排並執行同步作業。 +
      • +
      • + 重新啟動已停止的同步作業。 +
      • +
      +

      + 如要使用此架構,您要提供同步配接器外掛程式。每個同步配接器對於服務和內容供應程式來說是唯一的,但可以處理相同服務的多個帳戶名稱。 +此架構也可以讓相同服務和供應程式使用多個同步配接器。 + +

      +

      同步配接器類別和檔案

      +

      + 您將同步配接器實做為 {@link android.content.AbstractThreadedSyncAdapter} 的子類別,並以 Android 應用程式的一部分加以安裝。 + +系統會從應用程式宣示說明中的元素,以及從宣示說明所指向的特殊 XML 檔案中瞭解同步配接器的相關資訊。 +此 XML 檔案定義線上服務的帳戶類型,以及內容供應程式的授權,這兩者可用來唯一識別此配接器。 + +同步配接器要在使用者新增同步配接器的帳戶類型, +並啟用要與同步配接器的內容供應程式同步後, +同步配接器才會變成使用中。此時,系統會開始管理配接器並視需要加以呼叫,以便在內容供應程式和伺服器之間進行同步。 + +

      +

      + 注意:使用帳戶類型做為同步配接器識別的一部分,可以讓系統在偵測後,將存取不同服務、但來自相同組織的同步配接器群組在一起。 + +例如,Google 線上服務的同步配接器都有相同的帳戶類型 com.google。 +使用者將 Google 帳戶新增至其裝置後,所有已安裝的 Google 服務同步配接器會列在一起,每個列出的同步配接器會與裝置上不同的內容供應程式進行同步。 + + +

      +

      + 由於大多數服務都需要在存取資料之前先驗證其身分,因此 Android 系統提供類似的驗證架構,而且通常會與同步配接器架構一起搭配使用。 + +驗證架構使用外掛程式驗證器,這是 + {@link android.accounts.AbstractAccountAuthenticator} 的子類別。 +驗證器會以下列步驟驗證使用者的身分: + +

        +
      1. + 收集使用者的名稱、密碼或類似資訊 (使用者的憑證)。 + +
      2. +
      3. + 將憑證傳送給服務 +
      4. +
      5. + 檢驗服務的回覆。 +
      6. +
      +

      + 如果服務接受此憑證,則驗證器可以儲存憑證供以後使用。 +由於外掛程式驗證器架構的緣故, +{@link android.accounts.AccountManager} 可以存取驗證器支援且選擇顯示的任何 authtoken,例如 OAuth2 authtoken。 + +

      +

      + 雖然驗證並非必要,大部分聯絡人服務仍會加以使用。 + 不過,您不一定要使用 Android 驗證架構來進行驗證動作。 +

      +

      同步配接器實作

      +

      + 如要實作聯絡人供應程式的同步配接器,要從建立內含以下各項的 Android 應用程式開始: + +

      +
      +
      + 回應來自系統的要求,以繫結至同步配接器的 {@link android.app.Service} 元件。 + +
      +
      + 系統要執行同步時,會呼叫服務的 + {@link android.app.Service#onBind(Intent) onBind()} 方法,以取得同步配接器的 + {@link android.os.IBinder}。這樣可以讓系統以跨處理程序的方式呼叫配接器的方法。 + +

      + 在範例同步配接器範例應用程式中,此服務的名稱為 + com.example.android.samplesync.syncadapter.SyncService。 + +

      +
      +
      + 實際的同步配接器是以 + {@link android.content.AbstractThreadedSyncAdapter} 的實體子類別加以實作。 +
      +
      + 此類別會執行的工作包括:從伺服器下載資料、從裝置上傳資料以及解決衝突。 +配接器的主要工作會使用 {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( +Account, Bundle, String, ContentProviderClient, SyncResult) +onPerformSync()} 方法完成。 +此類別必須以單一執行個體的方式加以具現化。 +

      + 在範例同步配接器範例應用程式中,同步配接器定義在 +com.example.android.samplesync.syncadapter.SyncAdapter 類別中。 + +

      +
      +
      + {@link android.app.Application} 的子類別。 +
      +
      + 此類別就像是同步配接器單一執行個體的工廠。使用 +{@link android.app.Application#onCreate()} 方法具現化同步配接器,並提供靜態的「getter」方法將單一執行個體傳回給同步配接器服務的 +{@link android.app.Service#onBind(Intent) onBind()} 方法。 + + +
      +
      + 選用:回應系統針對使用者發出的驗證要求的 {@link android.app.Service} 元件。 + +
      +
      + {@link android.accounts.AccountManager} 會啟動此服以開始驗證程序。 +服務的 {@link android.app.Service#onCreate()} 方法會具現化為驗證器物件。 +系統需驗證應用程式同步配接器的使用者帳戶時,會呼叫服務的 +{@link android.app.Service#onBind(Intent) onBind()} 方法以取得驗證器的 + {@link android.os.IBinder}。 +這樣可以讓系統以跨處理程序的方式呼叫驗證器的方法。 + +

      + 在範例同步配接器範例應用程式中,此服務的類別名稱為 +com.example.android.samplesync.authenticator.AuthenticationService。 + +

      +
      +
      + 選用:處理驗證要求的 +{@link android.accounts.AbstractAccountAuthenticator} 實體子類別。 + +
      +
      + {@link android.accounts.AccountManager} 會呼叫此類別提供的方法,透過伺服器驗證使用者的憑證。 +驗證程序的詳細方式會根據所使用的伺服器技術,而有很大的差異。 +建議您參閱伺服器軟體的說明文件,進一步瞭解驗證。 + +

      + 在範例同步配接器範例應用程式中,驗證器是在 +com.example.android.samplesync.authenticator.Authenticator 類別中完成定義。 + +

      +
      +
      + 定義系統同步配接器和驗證器的 XML 檔案。 +
      +
      + 應用程式宣示說明中的 + <service> 元素會定義之前說明的同步配接器和驗證器服務元件。 + +這些元素包含的 +<meta-data> 子元素可提供系統的特定資料: + + + +
        +
      • + 同步配接器服務的 + <meta-data> 元素,此元素會指向 XML 檔案 res/xml/syncadapter.xml。 + +此檔案會按順序指出 +將與聯絡人供應程式同步的網路服務 URI, +以及網路服務的帳戶類型。 +
      • +
      • + 選用:驗證器服務的 + <meta-data> 元素,此元素會指向 XML 檔案 +res/xml/authenticator.xml。 +此檔案會按順序指出此驗證器支援的帳戶類型,以及驗證程序中會出現的 UI 資源。 + +此元素中指定的帳戶類型必須與同步配接器中指定的帳戶類型相同。 + + +
      • +
      +
      +
      +

      社交串流資料

      +

      + {@code android.provider.ContactsContract.StreamItems} 和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格負責管理社交網路的傳入資料。 +您可以編寫一個同步配接器,將來自您自己網路的串流資料 +新增至這些表格,或是可以從這些表格讀取串流資料,然後 +顯示於您自己的應用程式中,或同時具備這兩種功能。有了這些功能,您的社交網路服務和應用程式就可以整合到 Android 的社交網路體驗。 + +

      +

      社交串流文字

      +

      + 串流項目永遠會與原始聯絡人關聯。 +{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} 會連結到原始聯絡人的 + _ID 值。原始聯絡人的帳戶類型和帳戶名稱也會儲存在串流項目列。 + +

      +

      + 串流中的資料會儲存在下列各欄: +

      +
      +
      + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE} +
      +
      + 必要。與此串流項目相關聯的原始聯絡人使用者帳戶類型。 +請記得在插入串流項目時設定此值。 +
      +
      + {@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME} +
      +
      + 必要。與此串流項目相關聯的原始聯絡人使用者帳戶名稱。 +請記得在插入串流項目時設定此值。 +
      +
      + 識別碼欄 +
      +
      + 必要。您必須在插入串流項目時插入以下識別碼欄: + +
        +
      • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID}:與此串流項目相關聯的聯絡人 +{@code android.provider.BaseColumns#_ID} 值。 + +
      • +
      • + {@code android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY}:與此串流項目相關聯的聯絡人 +{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} 值。 + +
      • +
      • + {@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID}:與此串流項目相關聯的原始聯絡人 +{@code android.provider.BaseColumns#_ID} 值。 + +
      • +
      +
      +
      + {@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS} +
      +
      + 選用。儲存您可以在串流項目開頭顯示的摘要資訊。 +
      +
      + {@code android.provider.ContactsContract.StreamItemsColumns#TEXT} +
      +
      + 串流項目的文字,可能是項目來源張貼的內容,或者會產生串流項目動作的描述。 +此欄可以包含 +{@link android.text.Html#fromHtml(String) fromHtml()} 能夠轉譯的任何格式內嵌資源影像。 +供應程式會截斷較長的內容,但會試著避免破壞標籤。 + +
      +
      + {@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} +
      +
      + 內含插入或更新串流項目時間的文字字串, +,以「毫秒」為單位。插入或更新串流項目的應用程式負責維護此欄,聯絡人供應程式不會自動加以維護。 + + +
      +
      +

      + 如要顯示串流項目的識別資訊,請使用 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}、 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL} 以及 +{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} 連結到應用程式中的資源。 + +

      +

      + {@code android.provider.ContactsContract.StreamItems} 表格也包含 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} 到 +{@code android.provider.ContactsContract.StreamItemsColumns#SYNC4} 欄,專門供同步配接器使用。 + +

      +

      社交串流相片

      +

      + {@code android.provider.ContactsContract.StreamItemPhotos} 表格會儲存與串流項目相關聯的相片。 +表格的 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} 欄會連結到 + {@code android.provider.ContactsContract.StreamItems} 表格中 {@code android.provider.BaseColumns#_ID} 欄的值。 +相片參照會儲存在表格中的以下各欄: + +

      +
      +
      + {@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} 欄 (BLOB)。 +
      +
      + 相片的二進位檔,由供應程式調整大小以進行儲存和顯示。 + 此欄是要提供與使用舊版聯絡人供應程式儲存相片的向下相容性。 +不過,在目前版本中,您不應使用此欄來儲存相片。 +請改用 {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} 或 +{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} (下文提供這兩者的相關說明) 在檔案中儲存相片。 + +此欄現在包含相片的縮圖可供讀取。 + +
      +
      + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} +
      +
      + 原始聯絡人相片的數字識別碼。將此值附加到常數 +{@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI} 可取得指向單一相片檔案的內容 URI,然後呼叫 {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 可取得此相片檔案的控制代碼。 + + +
      +
      + {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI} +
      +
      + 直接指向此列所呈現相片的相片檔案內容 URI。 + 使用此 URI 呼叫 {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) +openAssetFileDescriptor()} 可取得相片檔案的控制代碼。 +
      +
      +

      使用社交串流表格

      +

      + 這些表格的運作方式與聯絡人供應程式中的其他主要表格大致相同,以下各項除外: +

      +
        +
      • + 這些表格需要額外的存取權限。如要從中讀取,您的應用程式必須具備 {@code android.Manifest.permission#READ_SOCIAL_STREAM} 權限。 +如要加以修改,您的應用程式必須具備 +{@code android.Manifest.permission#WRITE_SOCIAL_STREAM} 權限。 + +
      • +
      • + 針對 {@code android.provider.ContactsContract.StreamItems} 表格,每個原始聯絡人的儲存列數是有限制的。 +達到此限制後,聯絡人供應程式會自動刪除 +{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP} 最舊的列,為新的串流項目列騰出空間。 + +如要取得此限制,請發出查詢給內容 URI +{@code android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI}。 +您只要將內容 URI 設為 null 即可,其餘引數不需要處理。 +此查詢會傳回內含單一列的 Cursor 與單一欄 +{@code android.provider.ContactsContract.StreamItems#MAX_ITEMS}。 + +
      • +
      + +

      + 類別 {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} 定義了單一串流項目,且內含相片列的 {@code android.provider.ContactsContract.StreamItemPhotos} 子表格。 + + +

      +

      社交串流互動

      +

      + 社交串流資料受到聯絡人供應程式與裝置聯絡人應用程式的管理,提供強大的方式將您的社交網路系統與現有聯絡人連接起來。 + +提供下列功能: +

      +
        +
      • + 使用同步配接器將您的社交網路服務同步到聯絡人供應程式後,您可以擷取某位使用者的聯絡人最近 Activity,並將它儲存在 {@code android.provider.ContactsContract.StreamItems} 和 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格中,供後續使用。 + + +
      • +
      • + 除了一般同步之外,您可以在使用者選取要檢視的聯絡人時,觸發您的同步配接器,以擷取其他資料。 +此舉可讓您的同步配接器擷取聯絡人高解析度的相片,以及聯絡人最近的串流項目。 + +
      • +
      • + 藉由向裝置的聯絡人應用程式和聯絡人供應程式註冊通知,您可以在檢視聯絡人時「收到」意圖,並於此時更新您服務中的聯絡人狀態。 + +相較於與同步配接器執行完整同步, +此方式較快速且使用的頻寬較少。 +
      • +
      • + 使用者在裝置的聯絡人應用程式查看聯絡人時,可以將聯絡人新增至您的社交網路服務。 +您可以透過「邀請聯絡人」啟用上述功能。「邀請聯絡人」會啟用一連串的 Activity,將現有聯絡人新增至您的網路和 XML 檔案。此檔案會將您應用程式的詳細資訊提供給裝置的聯絡人應用程式和聯絡人供應程式。 + + + +
      • +
      +

      + 串流項目與聯絡人供應程式的一般同步與其他同步相同。 +如要進一步瞭解同步,請參閱聯絡人供應程式同步配接器。 +以下兩節說明如何註冊通知和邀請聯絡人。 + +

      +

      註冊以處理社交網路檢視

      +

      + 註冊您的同步配接器,使其在使用者查看由您的同步配接器所管理的聯絡人時收到通知: + +

      +
        +
      1. + 在專案的 res/xml/ 目錄中建立名稱為 contacts.xml的檔案。 +如果已經有這個檔案,可略過此步驟。 +
      2. +
      3. + 在此檔案中,新增 +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 元素。 + 如果這個元素已經存在,可略過此步驟。 +
      4. +
      5. + 為了註冊服務,讓使用者在裝置的聯絡人應用程式中開啟聯絡人的詳細資訊頁面時收到通知,請將 + viewContactNotifyService="serviceclass" 屬性新增至此元素,其中 + serviceclass 是該服務的完整類別名稱,而此服務會收到來自裝置聯絡人應用程式的意圖。 + +對於通知器服務而言,使用擴充 {@link android.app.IntentService} 的類別可以讓此服務接收意圖。 + +傳入意圖的資料中含有使用者所點擊該名原始聯絡人的內容 URI。 +您可以從通知器服務繫結,然後呼叫您的同步配接器,以更新原始聯絡人的資料。 + +
      6. +
      +

      + 如何註冊使用者點擊串流項目或相片 (或兩者) 時所呼叫的 Activity: +

      +
        +
      1. + 在專案的 res/xml/ 目錄中建立名稱為 contacts.xml的檔案。 +如果已經有這個檔案,可略過此步驟。 +
      2. +
      3. + 在此檔案中,新增 +<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 元素。 + 如果這個元素已經存在,可略過此步驟。 +
      4. +
      5. + 為了註冊其中一個 Activity,讓它處理使用者在裝置的聯絡人應用程式中點擊串流項目的 Activity,請將 + viewStreamItemActivity="activityclass" 屬性新增至此元素,其中 + activityclass 是該 Activity 的完整類別名稱,而此 Activity 會收到來自裝置聯絡人應用程式的意圖。 + + +
      6. +
      7. + 為了註冊其中一個 Activity,讓它處理使用者在裝置的聯絡人應用程式中點擊串流相片的活動,請將 + viewStreamItemPhotoActivity="activityclass" 屬性新增至此元素,其中 + activityclass 是該 Activity 的完整類別名稱,而此 Activity 會收到來自裝置聯絡人應用程式的意圖。 + + +
      8. +
      +

      + 如要進一步瞭解 <ContactsAccountType> 元素,請參閱 <ContactsAccountType> 元素。 + +

      +

      + 傳入意圖的資料中含有使用者所按下項目或相片的內容 URI。 + 如要針對文字項目和相片採取不同的 Activity,請在相同的檔案中同時使用兩個屬性。 +

      +

      與社交網路服務互動

      +

      + 使用者不需要離開裝置的聯絡人應用程式,就可以邀請聯絡人到您的社交網路網站。 +您可以改為讓裝置的聯絡人應用程式傳送意圖,以邀請聯絡人前往您的 Activity。 +如要進行此設定: +

      +
        +
      1. + 在專案的 res/xml/ 目錄中建立名稱為 contacts.xml的檔案。 +如果已經有這個檔案,可略過此步驟。 +
      2. +
      3. + 在此檔案中,新增 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> 元素。 + + 如果這個元素已經存在,可略過此步驟。 +
      4. +
      5. + 新增下列屬性: +
          +
        • inviteContactActivity="activityclass"
        • +
        • + inviteContactActionLabel="@string/invite_action_label" +
        • +
        + activityclass 值是 Activity 的完整類別名稱,以此 Activity 接收意圖。 +invite_action_label 值是顯示在裝置聯絡人應用程式內 [新增連線] 選單中的文字字串。 + + +
      6. +
      +

      + 注意:ContactsSource 是 + ContactsAccountType 已淘汰的標籤名稱。 +

      +

      contacts.xml 參照

      +

      + contacts.xml 檔案包含的 XML 元素,可控制您的同步配接器與應用程式 (聯絡人應用程式和聯絡人供應程式) 之間的互動。 +這些元素在以下各節有詳細的說明。 + +

      +

      <ContactsAccountType> 元素

      +

      + <ContactsAccountType> 元素控制您的應用程式與聯絡人應用程式之間的互動。 +此元素的語法如下: +

      +
      +<ContactsAccountType
      +        xmlns:android="http://schemas.android.com/apk/res/android"
      +        inviteContactActivity="activity_name"
      +        inviteContactActionLabel="invite_command_text"
      +        viewContactNotifyService="view_notify_service"
      +        viewGroupActivity="group_view_activity"
      +        viewGroupActionLabel="group_action_text"
      +        viewStreamItemActivity="viewstream_activity_name"
      +        viewStreamItemPhotoActivity="viewphotostream_activity_name">
      +
      +

      + 包含於: +

      +

      + res/xml/contacts.xml +

      +

      + 可以包含: +

      +

      + <ContactsDataKind> +

      +

      + 描述: +

      +

      + 宣告 Android 元件和 UI 標籤,讓使用者可以邀請聯絡人加入社交網路、使用者的社交網路串流更新內容時通知使用者等等。 + + +

      +

      + 請注意,<ContactsAccountType> 的屬性不需要使用屬性前置詞 android:。 + +

      +

      + 屬性: +

      +
      +
      {@code inviteContactActivity}
      +
      + 使用者從裝置的聯絡人應用程式選取[新增連線]時,您希望在應用程式中啟動的 Activity 完整類別名稱。 + + +
      +
      {@code inviteContactActionLabel}
      +
      + 在 [新增連線] 選單的 + {@code inviteContactActivity} 中所指定 Activity 的顯示文字字串。 + 例如,您可以使用「關注我的網路活動」字串。此標籤可以使用字串資源識別碼。 + +
      +
      {@code viewContactNotifyService}
      +
      + 使用者檢視聯絡人時,要接收通知的應用程式中的服務完整類別名稱。 +此通知是由裝置的聯絡人應用程式所傳送,這樣可以讓您的應用程式延後要處理大量資料的操作,需要時再加以處理。 + +例如,您的應用程式可以藉由讀取並顯示聯絡人的高解析度相片,以及最近的社交串流項目,以回應此通知。 + +如要進一步瞭解此功能,請參閱社交串流互動。 +您可以在 + SampleSyncAdapter 範例應用程式的 NotifierService.java中查看通知服務的範例。 + + +
      +
      {@code viewGroupActivity}
      +
      + 應用程式中可以顯示群組資訊的 Activity 完整類別名稱。 +使用者在裝置的聯絡人應用程式中按一下群組標籤時,會顯示此 Activity 的 UI。 + +
      +
      {@code viewGroupActionLabel}
      +
      + 聯絡人應用程式顯示 UI 控制項的標籤,可以讓使用者在您的應用程式中查看群組。 + +

      + 例如,如果您在裝置上安裝 Google+ 應用程式,而您將Google+ 與聯絡人應用程式進行同步,您會看到 Google+ 社交圈已列為聯絡人應用程式 [群組] 標籤中的群組。 + +如果按一下 Google+ 社交圈,您會看到該社交圈中的人員已列為「群組」。 +系統會在畫面頂端顯示 Google+ 圖示,如果您按一下此圖示,則控制權會切換到 Google+ 應用程式。聯絡人應用程式使用 +{@code viewGroupActivity} 執行此動作,並使用 Google+ 圖示做為 +{@code viewGroupActionLabel} 的值。 + + +

      +

      + 此屬性可以使用字串資源識別碼。 +

      +
      +
      {@code viewStreamItemActivity}
      +
      + 使用者按一下原始聯絡人的串流項目時,裝置的聯絡人應用程式所啟動應用程式中的 Activity 完整類別名稱。 + +
      +
      {@code viewStreamItemPhotoActivity}
      +
      + 使用者按一下原始聯絡人串流項目中的相片時,裝置的聯絡人應用程式所啟動應用程式中的 Activity 完整類別名稱。 + + +
      +
      +

      <ContactsDataKind> 元素

      +

      + <ContactsDataKind> 元素控制聯絡人應用程式的 UI 中,您的應用程式自訂資料列所顯示的控制項。此元素的語法如下: + +

      +
      +<ContactsDataKind
      +        android:mimeType="MIMEtype"
      +        android:icon="icon_resources"
      +        android:summaryColumn="column_name"
      +        android:detailColumn="column_name">
      +
      +

      + 包含於: +

      +<ContactsAccountType> +

      + 描述: +

      +

      + 使用此元素讓聯絡人應用程式,將自訂資料列的內容顯示為原始聯絡人詳細資訊的一部分。 +<ContactsAccountType> 的每個 <ContactsDataKind> 子元素都代表同步配接器新增至 {@link android.provider.ContactsContract.Data} 表格的自訂資料列類型。 + +針對您使用的每個自訂 MIME 類型,新增一個 + <ContactsDataKind> 元素。如果您不要顯示某個自訂資料列的資料,就不用為該列新增元素。 + +

      +

      + 屬性: +

      +
      +
      {@code android:mimeType}
      +
      + 您在 + {@link android.provider.ContactsContract.Data} 表格中已經為自訂資料列類型所定義的自訂 MIME 類型。例如, +vnd.android.cursor.item/vnd.example.locationstatus 值可能是 +記錄聯絡人最後已知位置資料列的自訂 MIME 類型。 +
      +
      {@code android:icon}
      +
      + 聯絡人應用程式顯示在資料旁邊的 Android +可繪資源。 +使用此項向使用者指出資料是來自您的服務。 + +
      +
      {@code android:summaryColumn}
      +
      + 從資料列擷取兩個值,其中第一個值的欄名稱。此資料列的值會顯示為該項目的第一行。 +第一行的用意是做為資料的摘要使用,但為選用。 +另請參閱 + android:detailColumn。 +
      +
      {@code android:detailColumn}
      +
      + 從資料列擷取兩個值,其中第二個值的欄名稱。此資料列的值會顯示為該項目的第二行。 +另請參閱 +{@code android:summaryColumn}。 +
      +
      +

      其他聯絡人供應程式功能

      +

      + 除了上一節所描述的主要功能之外,聯絡人供應程式也提供以下實用功能來處理聯絡人資料: + +

      +
        +
      • 聯絡人群組
      • +
      • 相片功能
      • +
      +

      聯絡人群組

      +

      + 聯絡人供應程式可以選擇為群組資料相關的聯絡人集合貼上標籤。 +如果與使用者帳戶關聯的伺服器要維護群組,該帳戶的帳戶類型所屬的同步配接器,應該要在聯絡人供應程式和伺服器之間傳輸群組資料。 + +使用者將新的聯絡人新增至伺服器,然後將此聯絡人放置於新群組時,同步配接器必須將新群組新增至 {@link android.provider.ContactsContract.Groups} 表格。 + +原始聯絡人所屬的一或多個群組會使用 {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} MIME 類型儲存在 {@link android.provider.ContactsContract.Data} 表格。 + + +

      +

      + 如果您設計的同步配接器,會將原始聯絡人資料從伺服器新增至聯絡人供應程式,表示您並未使用群組,那麼您需要告訴供應程式讓您的資料變成可見的。 + +在使用者將帳戶新增至裝置時,要執行的程式碼中,更新聯絡人供應程式為帳戶新增的 {@link android.provider.ContactsContract.Settings} 列。 + +在此列中,將 {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE +Settings.UNGROUPED_VISIBLE} 欄的值設為 1。 +這麼做之後,即使您沒有使用群組,聯絡人供應程式 +一律會讓您的聯絡人資料成為可見的。 +

      +

      聯絡人相片

      +

      + {@link android.provider.ContactsContract.Data} 表格會使用 MIME 類型 {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 在列中儲存相片。 +列的 +{@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} 欄是連結到其所屬原始聯絡人的 + {@code android.provider.BaseColumns#_ID} 欄。 + 類別 {@link android.provider.ContactsContract.Contacts.Photo} 定義了 + {@link android.provider.ContactsContract.Contacts} 的子表格,其中包含聯絡人主要相片的相片資訊,也就是聯絡人之主要原始聯絡人的主要相片。 +同樣地, +類別 {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 定義了 {@link android.provider.ContactsContract.RawContacts} 的子表格,其中包含原始聯絡人之主要相片的相片資訊。 + + +

      +

      + {@link android.provider.ContactsContract.Contacts.Photo} 和 +{@link android.provider.ContactsContract.RawContacts.DisplayPhoto} 的參考文件含有擷取相片資訊的範例。 +擷取原始聯絡人的主要縮圖沒有方便使用的類別,不過您可以傳送查詢到 +{@link android.provider.ContactsContract.Data} 表格,然後選取原始聯絡人的 +{@code android.provider.BaseColumns#_ID}、{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE +Photo.CONTENT_ITEM_TYPE} 以及 {@link android.provider.ContactsContract.Data#IS_PRIMARY} 欄,以尋找原始聯絡人的主要相片列。 + + + +

      +

      + 人員的社交串流資料可能也包括相片。這些資訊都儲存在 +{@code android.provider.ContactsContract.StreamItemPhotos} 表格。社交串流相片中針對此表格會有更詳細的說明。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd new file mode 100644 index 0000000000000000000000000000000000000000..78314784ba9e13ac8af69ae6e41cb9b986b24839 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-basics.jd @@ -0,0 +1,1196 @@ +page.title=內容供應程式基本概念 +@jd:body +
      +
      + +

      本文件內容

      +
        +
      1. + 總覽 +
          +
        1. + 存取供應程式 +
        2. +
        3. + 內容 URI +
        4. +
        +
      2. +
      3. + 從供應程式擷取資料 +
          +
        1. + 要求讀取權限 +
        2. +
        3. + 建構查詢 +
        4. +
        5. + 顯示查詢結果 +
        6. +
        7. + 從查詢結果取得資料 +
        8. +
        +
      4. +
      5. + 內容供應程式權限 +
      6. +
      7. + 插入、更新及刪除資料 +
          +
        1. + 插入資料 +
        2. +
        3. + 更新資料 +
        4. +
        5. + 刪除資料 +
        6. +
        +
      8. +
      9. + 供應程式資料類型 +
      10. +
      11. + 供應程式存取權的替代形式 +
          +
        1. + 批次存取 +
        2. +
        3. + 透過意圖存取資料 +
        4. +
        +
      12. +
      13. + 合約類別 +
      14. +
      15. + MIME 類型參考資料 +
      16. +
      + + +

      重要類別

      +
        +
      1. + {@link android.content.ContentProvider} +
      2. +
      3. + {@link android.content.ContentResolver} +
      4. +
      5. + {@link android.database.Cursor} +
      6. +
      7. + {@link android.net.Uri} +
      8. +
      + + +

      相關範例

      +
        +
      1. + + 游標 (使用者) +
      2. +
      3. + + 游標 (電話) +
      4. +
      + + +

      另請參閱

      +
        +
      1. + + 建立內容供應程式 +
      2. +
      3. + + 日曆供應程式 +
      4. +
      +
      +
      + + +

      + 內容供應程式可管理中央資料存放庫的存取權。供應程式是 Android 應用程式的一部分,通常可提供本身的 UI 方便使用者處理資料。 + +不過,內容供應程式主要是供其他應用程式使用 (透過供應程式用戶端物件進行存取)。 +供應程式與供應程式用戶端可提供一致的標準介面,除了可用於存取資料,還能用來處理程序間通訊以及保護資料存取的安全。 + + +

      +

      + 本主題涵蓋以下基本概念: +

      +
        +
      • 內容供應程式的運作方式。
      • +
      • 可用於從內容供應程式擷取資料的 API。
      • +
      • 可用於插入、更新或刪除內容供應程式資料的 API。
      • +
      • 有助於使用供應程式的其他 API 功能。
      • +
      + + +

      總覽

      +

      + 內容供應程式會向外部應用程式以一或多份表格顯示資料,這些表格的樣式類似於關聯式資料庫中的表格。 +每個資料列代表供應程式所收集部分資料類型的單一執行個體,而列中的每個資料欄則代表針對該執行個體收集的個別資料。 + + +

      +

      + 例如,Android 平台內建的其中一個供應程式是使用者字典,其中儲存使用者想保留的非標準字詞的拼寫方式。 +表 1 說明此供應程式表格顯示這種資料的方式: + +

      +

      + 表 1:使用者字典表格範例。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      字詞應用程式 ID頻率地區_ID
      mapreduceuser1100en_US1
      precompileruser14200fr_FR2
      appletuser2225fr_CA3
      constuser1255pt_BR4
      intuser5100en_UK5
      +

      + 在表 1 中,每個資料列代表標準字典未收錄的某個字詞執行個體。 +每個資料欄則代表該字詞的部分資料,例如初次出現該字詞的地區。 +欄標題為儲存在供應程式中的欄名稱。 +如果想得知某一列的地區,請查看相對應的 locale 欄。以這個供應程式為例,_ID 欄的用途與供應程式自動維護的「主索引鍵」欄相同。 + + +

      +

      + 注意:供應程式不需具備主索引鍵,也不必採用 _ID 做為主索引鍵欄的名稱 (如果有此欄的話)。 +不過,如果您想將供應程式的資料繫結至 {@link android.widget.ListView},就必須將其中一個資料欄命名為 _ID。 + +如要進一步瞭解這項規定,請參閱顯示查詢結果。 + +

      +

      存取供應程式

      +

      + 應用程式會透過 {@link android.content.ContentResolver} 用戶端物件存取內容供應程式的資料。 +此物件內含的方法可呼叫供應程式物件 ({@link android.content.ContentProvider} 子類別的其中一項執行個體) 中的同名方法。 + + +{@link android.content.ContentResolver} 方法可提供永久儲存空間的「CRUD」(建立、擷取、更新、刪除) 基本功能。 + +

      +

      + 用戶端應用程式處理程序中的 {@link android.content.ContentResolver} 物件以及擁有供應程式的應用程式中的 {@link android.content.ContentProvider} 物件,會自動處理程序間通訊。 +{@link android.content.ContentProvider} 還可當作其資料存放庫與外部資料表格之間的抽象層。 + + + +

      +

      + 注意:如要存取供應程式,您的應用程式通常必須透過本身的宣示說明檔案要求特定權限。 +如需詳細資料,請參閱內容供應程式權限。 + +

      +

      + 例如,如要從使用者字典供應程式取得一份列出字詞及其地區的清單,請呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 + + {@link android.content.ContentResolver#query query()} 方法會呼叫使用者字典供應程式所定義的 +{@link android.content.ContentProvider#query ContentProvider.query()} 方法。 +以下是 +{@link android.content.ContentResolver#query ContentResolver.query()} 呼叫的程式碼: +

      +

      +// Queries the user dictionary and returns results
      +mCursor = getContentResolver().query(
      +    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
      +    mProjection,                        // The columns to return for each row
      +    mSelectionClause                    // Selection criteria
      +    mSelectionArgs,                     // Selection criteria
      +    mSortOrder);                        // The sort order for the returned rows
      +
      +

      + 表 2 列出 +{@link android.content.ContentResolver#query +query(Uri,projection,selection,selectionArgs,sortOrder)} 的引數及相對應的 SQL SELECT 陳述式: +

      +

      + 表 2:Query() 與 SQL 查詢的對照表。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      query() 引數SELECT 關鍵字/參數備註
      UriFROM table_nameUri 會對應至供應程式中名為的「table_name」表格。
      projectioncol,col,col,... + projection 代表需針對每個擷取的資料列 +納入的一系列資料欄。 +
      selectionWHERE col = valueselection 會指定資料列選取條件。
      selectionArgs + (沒有任何相對應的關鍵字/參數。選取引數會取代選取子句中的 +? 預留位置。) +
      sortOrderORDER BY col,col,... + sortOrder 會指定資料列在傳回的 {@link android.database.Cursor} 中的顯示順序。 + +
      +

      內容 URI

      +

      + 內容 URI 是指用於識別供應程式資料的 URI,內容 URI 包括整個供應程式的符號名稱 (亦即供應程式的授權),以及指向表格的名稱 (亦即路徑)。 + +當您呼叫用戶端方法來存取供應程式中的表格時,該表格的內容 URI 即為其中一個引數。 + + +

      +

      + 在上方程式碼中, +{@link android.provider.UserDictionary.Words#CONTENT_URI} 常數包含使用者字典表格「字詞」的內容 URI。 +{@link android.content.ContentResolver} 物件會剖析該 URI 的授權,然後比較授權和已知供應程式的系統表格,藉此「解析」供應程式。 + +接著 {@link android.content.ContentResolver} 可以查詢引數分派給正確的供應程式。 + + +

      +

      + {@link android.content.ContentProvider} 會使用內容 URI 的路徑部分選擇要存取的表格。 +供應程式通常包含用於公開每個表格的「路徑」。 +

      +

      + 以上方程式碼為例,「字詞」的完整 URI 會如下所示: +

      +
      +content://user_dictionary/words
      +
      +

      + 其中的 user_dictionary 字串代表供應程式的授權,而 words 字串則是表格的路徑。 +字串 content:// (配置) 一律會顯示,而起會將此項目識別為內容 URI。 + + +

      +

      + 許多供應程式都可讓您存取表格中的單一資料列,方法是在 URI 後方附加 ID 值。例如,如要從使用者字典擷取 _ID4 的資料列,請使用以下內容 URI: + + +

      +
      +Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
      +
      +

      + 在更新或刪除您擷取的一組資料列時,通常需要使用 ID 值。 + +

      +

      + 注意:{@link android.net.Uri} 和 {@link android.net.Uri.Builder} 類別包含可用於從字串建構格式正確之 URI 物件的簡便方法。 +而 {@link android.content.ContentUris} 則包含可用於將 ID 值附加至 URI 的簡便方法。上方程式碼片段是使用 {@link android.content.ContentUris#withAppendedId +withAppendedId()} 將 ID 附加至 UserDictionary 內容 URI。 + + +

      + + + +

      從供應程式擷取資料

      +

      + 本節以使用者字典供應程式為例,說明如何從供應程式擷取資料。 + +

      +

      + 為了避免造成混淆,本節中的程式碼片段是透過「UI 執行緒」呼叫 {@link android.content.ContentResolver#query ContentResolver.query()}。 +不過在實際程式碼中,您需要透過個別執行緒以非同步方式進行查詢。 +您可以使用 {@link android.content.CursorLoader} 類別 (詳情請參閱載入器指南) 來完成這項作業。 + + +此外,本節中的程式碼都只是程式碼片段,無法呈現完整的應用程式。 + +

      +

      + 如要從供應程式擷取資料,請按照下列基本步驟操作: +

      +
        +
      1. + 要求供應程式的讀取權限。 +
      2. +
      3. + 定義可將查詢傳送至供應程式的程式碼。 +
      4. +
      +

      要求讀取權限

      +

      + 您的應用程式需取得供應程式的「讀取權限」,才能從供應程式擷取資料。 +您無法在執行階段要求此權限;您必須使用 <uses-permission> 元素和供應程式所定義的確切權限名稱,在宣示說明中指明您需要此權限。 + + + +在宣示說明中指定此元素後,即可為您的應用程式「要求」這項權限。 +當使用者安裝您應用程式時,他們會間接核准此要求。 + +

      +

      + 如要找出所使用供應程式的確切讀取權限名稱,以及該供應程式使用的其他存取權限名稱,請查閱供應程式的說明文件。 + + +

      +

      + 如要進一步瞭解用於存取供應程式的權限角色,請參閱內容供應程式權限。 + +

      +

      + 使用者字典供應程式會在本身的宣示說明檔案中定義 android.permission.READ_USER_DICTIONARY 權限,因此想讀取該供應程式的應用程式都必須要求此權限。 + + +

      + +

      建構查詢

      +

      + 從供應程式擷取資料的下一個步驟是建構查詢。本節中的第一個程式碼片段可定義數個用於存取使用者字典供應程式的變數: + +

      +
      +
      +// A "projection" defines the columns that will be returned for each row
      +String[] mProjection =
      +{
      +    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
      +    UserDictionary.Words.WORD,   // Contract class constant for the word column name
      +    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
      +};
      +
      +// Defines a string to contain the selection clause
      +String mSelectionClause = null;
      +
      +// Initializes an array to contain selection arguments
      +String[] mSelectionArgs = {""};
      +
      +
      +

      + 下一個程式碼片段以使用者字典供應程式為例,示範 {@link android.content.ContentResolver#query ContentResolver.query()} 的使用方式。 + +供應程式用戶端查詢類似於 SQL 查詢,包含一組要傳回的資料欄、一組選取條件以及一個排序順序。 + +

      +

      + 查詢需傳回的一組資料欄稱為「投影」(即 mProjection 變數)。 + +

      +

      + 指定要擷取的資料列的運算式會分為選取子句和選取引數。 +選取子句包含邏輯運算式和布林運算式、欄名稱和值 (即 mSelectionClause 變數)。 +如果您指定可替換的參數 ?,而不是某個值,則查詢方法會從選取引數陣列 (即 mSelectionArgs 變數) 擷取相關值。 + + +

      +

      + 在下方程式碼片段中,如果使用者未輸入任何字詞,選取子句會設定為 null,此時查詢會傳回供應程式中的所有字詞。 +如果使用者輸入了某個字詞,則選取子句會設定為 UserDictionary.Words.WORD + " = ?",而選取引數陣列的第一個元素會設為使用者所輸入的字詞。 + + +

      +
      +/*
      + * This defines a one-element String array to contain the selection argument.
      + */
      +String[] mSelectionArgs = {""};
      +
      +// Gets a word from the UI
      +mSearchString = mSearchWord.getText().toString();
      +
      +// Remember to insert code here to check for invalid or malicious input.
      +
      +// If the word is the empty string, gets everything
      +if (TextUtils.isEmpty(mSearchString)) {
      +    // Setting the selection clause to null will return all words
      +    mSelectionClause = null;
      +    mSelectionArgs[0] = "";
      +
      +} else {
      +    // Constructs a selection clause that matches the word that the user entered.
      +    mSelectionClause = UserDictionary.Words.WORD + " = ?";
      +
      +    // Moves the user's input string to the selection arguments.
      +    mSelectionArgs[0] = mSearchString;
      +
      +}
      +
      +// Does a query against the table and returns a Cursor object
      +mCursor = getContentResolver().query(
      +    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
      +    mProjection,                       // The columns to return for each row
      +    mSelectionClause                   // Either null, or the word the user entered
      +    mSelectionArgs,                    // Either empty, or the string the user entered
      +    mSortOrder);                       // The sort order for the returned rows
      +
      +// Some providers return null if an error occurs, others throw an exception
      +if (null == mCursor) {
      +    /*
      +     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
      +     * call android.util.Log.e() to log this error.
      +     *
      +     */
      +// If the Cursor is empty, the provider found no matches
      +} else if (mCursor.getCount() < 1) {
      +
      +    /*
      +     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
      +     * an error. You may want to offer the user the option to insert a new row, or re-type the
      +     * search term.
      +     */
      +
      +} else {
      +    // Insert code here to do something with the results
      +
      +}
      +
      +

      + 這個查詢類似下方的 SQL 陳述式: +

      +
      +SELECT _ID, word, locale FROM words WHERE word = <userinput> ORDER BY word ASC;
      +
      +

      + 這個 SQL 陳述式會使用實際的欄名稱,而不是合約類別常數。 +

      +

      防範惡意輸入

      +

      + 如果內容供應程式所管理的資料是儲存在 SQL 資料庫中,在原始 SQL 陳述式中納入不受信任的外部資料可能會導致 SQL 遭植入惡意程式碼。 + +

      +

      + 以下提供選取子句範例: +

      +
      +// Constructs a selection clause by concatenating the user's input to the column name
      +String mSelectionClause =  "var = " + mUserInput;
      +
      +

      + 使用這個選取子句可讓使用者能夠將惡意 SQL 串連至您的 SQL 陳述式。 + 例如,使用者可針對 mUserInput 輸入「nothing; DROP TABLE *;」,以產生 var = nothing; DROP TABLE *; 選取子句。 +由於系統會將選取子句視為 SQL 陳述式,因此這種選取子句可能會導致供應程式清除底層 SQLite 資料庫中的所有表格 (除非您設定供應程式捕捉 SQL 植入嘗試)。 + + + +

      +

      + 為了避免發生這個問題,請使用包含 ? 可替換參數和一系列選取引數的選取子句。 +當您執行此動作時,使用者輸入會直接繫結到查詢,而不是被解譯為 SQL 陳述式的一部分。 + + 這樣一來,系統就不會將選取子句視為 SQL,進而防止使用者輸入植入惡意 SQL。您也可以使用以下選取子句,而不是透過串連方式納入使用者輸入: + +

      +
      +// Constructs a selection clause with a replaceable parameter
      +String mSelectionClause =  "var = ?";
      +
      +

      + 設定如下所示的一系列選取引數: +

      +
      +// Defines an array to contain the selection arguments
      +String[] selectionArgs = {""};
      +
      +

      + 如下所示在選取引數中加入一個值: +

      +
      +// Sets the selection argument to the user's input
      +selectionArgs[0] = mUserInput;
      +
      +

      + 建議您使用包含 ? 可替換參數和一系列選取引數的選取子句,即使供應程式並非以 SQL 資料庫為基礎亦然。 + + +

      + +

      顯示查詢結果

      +

      + {@link android.content.ContentResolver#query ContentResolver.query()} 用戶端方法一律會針對符合查詢選取條件的資料列,傳回內含查詢的投影所指定資料欄的 {@link android.database.Cursor}。 + +{@link android.database.Cursor} 物件會針對本身包含的資料列和資料欄提供隨機讀取存取權。 + +您可以使用 {@link android.database.Cursor} 方法逐一查看結果中的資料列、決定每個資料欄的資料類型、匯出資料欄的資料,以及檢查結果的其他屬性。 + +實作某些 {@link android.database.Cursor} 方法可在供應程式的資料變更時自動更新相關物件,或在 {@link android.database.Cursor} 變更時觸發觀察器物件中的方法,或是同時進行以上兩者。 + + +

      +

      + 注意:供應程式可能會根據建立查詢物件的屬性限制資料欄的存取權。 +例如,內容供應程式會限制同步配接器存取部分資料欄,避免將這些資料欄傳回 Activity 或服務。 + +

      +

      + 如果沒有任何資料欄符合選取條件,則供應程式會傳回 {@link android.database.Cursor#getCount Cursor.getCount()} 為 0 的 +{@link android.database.Cursor} 物件 (即沒有任何內容的游標)。 + +

      +

      + 如果發生內部錯誤,查詢結果將取決於供應程式的決定。供應程式可能會選擇傳回 null,或是擲回 {@link java.lang.Exception}。 + +

      +

      + 由於 {@link android.database.Cursor} 是一份資料欄「清單」,因此要顯示 {@link android.database.Cursor} 的內容,最佳做法是透過 {@link android.widget.SimpleCursorAdapter} 將其連結至 {@link android.widget.ListView}。 + + +

      +

      + 以下程式碼片段是上一個程式碼片段的延伸。它會建立內含查詢所擷取 {@link android.database.Cursor} 的 +{@link android.widget.SimpleCursorAdapter} 物件,並將該物件設定為 {@link android.widget.ListView} 的配接器: + + +

      +
      +// Defines a list of columns to retrieve from the Cursor and load into an output row
      +String[] mWordListColumns =
      +{
      +    UserDictionary.Words.WORD,   // Contract class constant containing the word column name
      +    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
      +};
      +
      +// Defines a list of View IDs that will receive the Cursor columns for each row
      +int[] mWordListItems = { R.id.dictWord, R.id.locale};
      +
      +// Creates a new SimpleCursorAdapter
      +mCursorAdapter = new SimpleCursorAdapter(
      +    getApplicationContext(),               // The application's Context object
      +    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
      +    mCursor,                               // The result from the query
      +    mWordListColumns,                      // A string array of column names in the cursor
      +    mWordListItems,                        // An integer array of view IDs in the row layout
      +    0);                                    // Flags (usually none are needed)
      +
      +// Sets the adapter for the ListView
      +mWordList.setAdapter(mCursorAdapter);
      +
      +

      + 注意:如要返回內含 {@link android.database.Cursor} 的{@link android.widget.ListView},您必須為游標加入名為 _ID 的資料欄。 + + 因為這樣,上述查詢會擷取「字詞」表格的 _ID 欄,即使 {@link android.widget.ListView} 未顯示該資料欄亦然。 + + 這項限制也是為何大多數供應程式會針對本身的所有表格設定 _ID 欄的原因。 + +

      + + +

      從查詢結果取得資料

      +

      + 除了單純顯示查詢結果以外,您還可以將它們用於其他工作。例如,您可以從使用者字典擷取拼字,然後在其他供應程式中查閱這些拼字。 + +如要這麼做,請逐一查看 {@link android.database.Cursor} 中的資料列: +

      +
      +
      +// Determine the column index of the column named "word"
      +int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
      +
      +/*
      + * Only executes if the cursor is valid. The User Dictionary Provider returns null if
      + * an internal error occurs. Other providers may throw an Exception instead of returning null.
      + */
      +
      +if (mCursor != null) {
      +    /*
      +     * Moves to the next row in the cursor. Before the first movement in the cursor, the
      +     * "row pointer" is -1, and if you try to retrieve data at that position you will get an
      +     * exception.
      +     */
      +    while (mCursor.moveToNext()) {
      +
      +        // Gets the value from the column.
      +        newWord = mCursor.getString(index);
      +
      +        // Insert code here to process the retrieved word.
      +
      +        ...
      +
      +        // end of while loop
      +    }
      +} else {
      +
      +    // Insert code here to report an error if the cursor is null or the provider threw an exception.
      +}
      +
      +

      + {@link android.database.Cursor} 實作方法包含數個用於從物件擷取不同資料類型的「get」方法。 +例如,上方程式碼片段使用了 {@link android.database.Cursor#getString getString()}。 +此外,這種實作方法還包括 +{@link android.database.Cursor#getType getType()} 方法,可傳回指定資料欄資料類型的值。 + +

      + + + +

      內容供應程式權限

      +

      + 供應程式的應用程式可指定其他應用程式在存取供應程式的資料時所需的權限。 +這些權限可確保使用者瞭解應用程式嘗試存取的資料為何。 +根據供應程式需求,其他應用程式必須取得相關必要權限才能存取供應程式。 +使用者可在安裝應用程式時得知這些必要的權限。 + +

      +

      + 如果供應程式的應用程式未指定任何權限,則其他應用程式就無法存取供應程式的資料。 +不過,供應程式的應用程式元件一律會具備完整的讀取及寫入存取權,無論供應程式指定的權限為何。 + +

      +

      + 如上所述,使用者字典供應程式要求其他應用程式需取得 android.permission.READ_USER_DICTIONARY 權限,才能擷取其中的資料。 + + 而該供應程式還個別針對插入、更新或刪除資料用途,指定了不同的 android.permission.WRITE_USER_DICTIONARY 權限。 + +

      +

      + 如要取得存取供應程式時所需的權限,應用程式可利用 <uses-permission> 元素在本身的宣示說明檔案中要求相關權限。 + +當 Android 套件管理員安裝應用程式時,使用者必須授予該應用程式要求的所有權限。 +使用者授予所有權限後,套件管理員才能繼續進行安裝作業;如果使用者不授予權限,則套件管理員就會取消安裝。 + + +

      +

      + 以下的 +<uses-permission> + 元素會要求使用者字典供應程式的讀取存取權: +

      +
      +    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
      +
      +

      + 如要進一步瞭解權限對供應程式存取的影響,請參閱安全性和權限指南。 + +

      + + + +

      插入、更新及刪除資料

      +

      + 透過從供應程式擷取資料的相同方式,您也可以利用供應程式用戶端與供應程式的 {@link android.content.ContentProvider} 之間的互動來修改資料。 + + 如要這麼做,請呼叫其中引數已傳送到相對應 {@link android.content.ContentProvider} 方法的 {@link android.content.ContentResolver} 方法。 +供應程式和供應程式用戶端會自動處理安全性和處理程序間通訊。 + +

      +

      插入資料

      +

      + 如要在供應程式中插入資料,請呼叫 +{@link android.content.ContentResolver#insert ContentResolver.insert()} 方法。 +這個方法會在供應程式中插入新的資料列,並傳回該列的內容 URI。 + 以下程式碼片段示範如何在使用者字典供應程式中插入新的字詞: +

      +
      +// Defines a new Uri object that receives the result of the insertion
      +Uri mNewUri;
      +
      +...
      +
      +// Defines an object to contain the new values to insert
      +ContentValues mNewValues = new ContentValues();
      +
      +/*
      + * Sets the values of each column and inserts the word. The arguments to the "put"
      + * method are "column name" and "value"
      + */
      +mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
      +mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
      +mNewValues.put(UserDictionary.Words.WORD, "insert");
      +mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
      +
      +mNewUri = getContentResolver().insert(
      +    UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
      +    mNewValues                          // the values to insert
      +);
      +
      +

      + 新資料列的資料會傳入單一 {@link android.content.ContentValues} 物件,該物件的格式與單列游標類似。 +您不必為這個物件中的資料欄指定相同的資料類型,而且如果您不想指定任何值,可以將資料欄設定為 null 以使用 {@link android.content.ContentValues#putNull ContentValues.putNull()}。 + + +

      +

      + 該程式碼片段並不會新增 _ID 欄,這是因為系統自動會維護該欄。 +供應程式會將不重複值 _ID 指派給新增的所有資料列。 +供應程式通常會採用該值做為表格的主索引鍵。 +

      +

      + newUri 以下方格式傳回的內容 URI 可用於識別新增的資料列: + +

      +
      +content://user_dictionary/words/<id_value>
      +
      +

      + <id_value> 是 ID 為 _ID 的資料列內容。 + 大多數供應程式可自動偵測這種格式的內容 URI,並據以針對指定的資料列執行特定作業。 + +

      +

      + 如要從傳回的 {@link android.net.Uri} 取得 _ID 的值,請呼叫 +{@link android.content.ContentUris#parseId ContentUris.parseId()}。 +

      +

      更新資料

      +

      + 如要更新資料列,請使用內含經過更新的值 (與您在插入資料時所使用的值相同) 以及選取條件 (與您在建立查詢時所使用的選取條件相同) 的 {@link android.content.ContentValues} 物件。 + + 您所使用的用戶端方法為 +{@link android.content.ContentResolver#update ContentResolver.update()}。您只需針對要更新的資料欄,將相關值加到 {@link android.content.ContentValues} 物件即可。 +如果您想清除資料欄的內容,請將值設定為 null。 + +

      +

      + 以下程式碼片段會針對地區代碼含有「en」字詞的所有資料列,將其地區代碼變更為 null。 +系統會傳回已更新的資料列數量: +

      +
      +// Defines an object to contain the updated values
      +ContentValues mUpdateValues = new ContentValues();
      +
      +// Defines selection criteria for the rows you want to update
      +String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
      +String[] mSelectionArgs = {"en_%"};
      +
      +// Defines a variable to contain the number of updated rows
      +int mRowsUpdated = 0;
      +
      +...
      +
      +/*
      + * Sets the updated value and updates the selected words.
      + */
      +mUpdateValues.putNull(UserDictionary.Words.LOCALE);
      +
      +mRowsUpdated = getContentResolver().update(
      +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
      +    mUpdateValues                       // the columns to update
      +    mSelectionClause                    // the column to select on
      +    mSelectionArgs                      // the value to compare to
      +);
      +
      +

      + 建議您在呼叫 {@link android.content.ContentResolver#update ContentResolver.update()} 時對使用者輸入進行檢測。 +如需詳細資訊,請參閱防範惡意輸入。 + +

      +

      刪除資料

      +

      + 刪除資料列的方法與擷取資料列資料類似:您必須為想刪除的資料列指定選取條件,用戶端方法最後會傳回已刪除的資料列數量。 + + 以下程式碼片段可刪除應用程式 ID 為「user」的資料列。此外,這個方法還會傳回已刪除的資料列數量。 + +

      +
      +
      +// Defines selection criteria for the rows you want to delete
      +String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
      +String[] mSelectionArgs = {"user"};
      +
      +// Defines a variable to contain the number of rows deleted
      +int mRowsDeleted = 0;
      +
      +...
      +
      +// Deletes the words that match the selection criteria
      +mRowsDeleted = getContentResolver().delete(
      +    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
      +    mSelectionClause                    // the column to select on
      +    mSelectionArgs                      // the value to compare to
      +);
      +
      +

      + 建議您在呼叫 {@link android.content.ContentResolver#delete ContentResolver.delete()} 時對使用者輸入進行檢測。 +如需詳細資訊,請參閱防範惡意輸入。 + +

      + +

      供應程式資料類型

      +

      + 內容供應程式可提供多種資料類型。使用者字典供應程式只能提供文字,但供應程式還可提供下列格式: + +

      +
        +
      • + 整數 +
      • +
      • + 長整數 (long) +
      • +
      • + 浮點數 +
      • +
      • + 長浮點數 (double) +
      • +
      +

      + 供應程式慣用的其他資料類型為二進位大型物件 (BLOB),這種資料會實作成 64 KB 位元組陣列。 +如果想瞭解可用的資料類型,請查閱 {@link android.database.Cursor} 類別的「get」方法。 + +

      +

      + 供應程式的說明文件通常會列出其中每個資料欄的資料類型。 + 使用者字典供應程式的資料類型列在其合約類別 {@link android.provider.UserDictionary.Words} 的參考文件 (如需合約類別的相關資訊,請參閱合約類別一節) 中。 + + + 您也可以呼叫 {@link android.database.Cursor#getType +Cursor.getType()} 來確認可用的資料類型。 +

      +

      + 此外,供應程式也會保留任何所定義內容 URI 的 MIME 資料類型資訊。您可以利用 MIME 類型資訊確認您的應用程式是否可處理供應程式所提供的資料,或是根據 MIME 類型選擇處理方式類型。 + +當您使用內含複雜的資料結構或檔案的供應程式時,通常需要使用 MIME 類型。 + +例如,聯絡人供應程式中的 {@link android.provider.ContactsContract.Data} 表格會使用 MIME 類型為每個資料列中儲存的聯絡人資料加上標籤。 + +如要取得與內容 URI 相對應的 MIME 類型,請呼叫 +{@link android.content.ContentResolver#getType ContentResolver.getType()}。 +

      +

      + 如要瞭解標準與自訂 MIME 類型的語法,請參閱 MIME 類型參考資料。 + +

      + + + +

      供應程式存取權的替代形式

      +

      + 開發應用程式時會使用到 3 種供應程式存取權的替代形式: +

      +
        +
      • + 批次存取:您可以使用 {@link android.content.ContentProviderOperation} 類別中的方法建立批次存取權呼叫,然後利用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 套用這些呼叫。 + + +
      • +
      • + 非同步查詢:建議您在個別執行緒中進行查詢。您可以使用 {@link android.content.CursorLoader} 類別來完成這項作業。 +如需相關示範說明,請參閱載入器指南中的範例。 + + +
      • +
      • + 透過意圖存取資料:您無法直接將意圖傳送到供應程式,但可以將意圖傳送到供應程式的應用程式;供應程式的應用程式通常是修改供應程式資料的最佳環境。 + + +
      • +
      +

      + 如需透過意圖進行批次存取和修改作業的相關資訊,請參閱下文。 +

      +

      批次存取

      +

      + 您可利用供應程式的批次存取功能插入大量資料列、透過相同方法呼叫在多個表格中插入資料列,或是透過單次交易 (微型作業) 在處理程序之間執行一組作業。 + + +

      +

      + 如要以「批次模式」存取供應程式,請建立一系列 {@link android.content.ContentProviderOperation} 物件,然後使用 {@link android.content.ContentResolver#applyBatch ContentResolver.applyBatch()} 將這些物件分派給內容供應程式。 + + +請將內容供應程式的「授權」(而不是特定內容 URI) 傳入這個方法。這樣可讓陣列中的所有 {@link android.content.ContentProviderOperation} 物件能夠在不同表格中運作。 + + +如果您呼叫 {@link android.content.ContentResolver#applyBatch +ContentResolver.applyBatch()},則系統會傳回一系列結果。 +

      +

      + {@link android.provider.ContactsContract.RawContacts} 合約類別的說明包含可展示批次插入作業的程式碼片段。 +聯絡人管理員範例應用程式的 ContactAdder.java 來源檔案包含批次存取範例供您參考。 + + + +

      + +

      透過意圖存取資料

      +

      + 意圖可提供內容供應程式的間接存取權。即使您的應用程式沒有存取權限,您仍可透過以下方式允許使用者存取供應程式的資料:從具備權限的應用程式取回結果意圖,或是啟用具備權限的應用程式並允許使用者存取該應用程式。 + + + +

      +

      透過臨時權限取得存取權

      +

      + 即使沒有適當的存取權限,您仍可存取內容供應程式的資料,方法是傳送意圖到沒有權限的應用程式,然後接收內含「URI」權限的結果意圖。 + + + URI 權限是特定內容 URI 專用的權限;在接收權限的 Activity 結束之前,這類權限會維持有效狀態。 +具備永久權限的應用程式可授予臨時權限,只要在結果意圖中設定旗標即可: + +

      +
        +
      • + 讀取權限: + {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} +
      • +
      • + 寫入權限: + {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} +
      • +
      +

      + 注意:這些旗標並不會將讀取或寫入存取權授予系統在內容 URI 中提供授權的供應程式。存取權僅供 URI 使用。 + +

      +

      + 供應程式會使用 +<provider> + 元素的 +android:grantUriPermission + 屬性以及 +<provider> + 元素的 +<grant-uri-permission> + 子元素在本身的宣示說明中為內容 URI 定義 URI 權限。如要進一步瞭解 URI 權限的運作機制,請參閱安全性和權限指南的「URI 權限」。 + + +

      +

      + 例如,即使您沒有 {@link android.Manifest.permission#READ_CONTACTS} 權限,您仍可透過聯絡人供應程式擷取聯絡人資料。 +您可能會想透過可用來在聯絡人的生日當天傳送電子賀卡給對方的應用程式進行這項動作。 +您偏好讓使用者控管要讓您的應用程式使用的聯絡人資料,而不是要求 {@link android.Manifest.permission#READ_CONTACTS} 授權您存取使用者的所有聯絡人以及個人資訊。 + + +為了達到這個目的,您進行了下列程序: +

      +
        +
      1. + 您的應用程式使用 {@link android.app.Activity#startActivityForResult +startActivityForResult()} 方法,傳送了內含 +{@link android.content.Intent#ACTION_PICK} 動作的意圖以及「聯絡人」MIME 類型 +{@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE}。 + +
      2. +
      3. + 由於該意圖符合聯絡人應用程式的「選取」Activity 的意圖篩選器,因此該 Activity 會在移動到前景。 + +
      4. +
      5. + 在選取 Activity 中,使用者選取了要更新的聯絡人。 +一旦使用者進行這項動作,選取 Activity 便會呼叫 +{@link android.app.Activity#setResult setResult(resultcode, intent)} 來設定要傳回您應用程式的意圖。 +該意圖包含使用者所選聯絡人的內容 URI,以及「額外」的旗標 +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION}。 +這些旗標可將 URI 權限授予您的應用程式,以便其讀取內容 URI 指向的聯絡人資料。選取 Activity 隨後會呼叫 {@link android.app.Activity#finish()} 來傳回您應用程式的控制權。 + + + +
      6. +
      7. + 您的 Activity 返回前景,而系統呼叫您 Activity 的 +{@link android.app.Activity#onActivityResult onActivityResult()} 方法。 +這個方法可接收聯絡人應用程式中的選取 Activity 所建立的結果意圖。 + +
      8. +
      9. + 即使您未在宣示說明中要求供應程式的永久讀取權限,只要利用結果意圖的內容 URI 即可從聯絡人供應程式讀取聯絡人資料。 + +您之後可以取得聯絡人的出生日期資訊或聯絡人的電子郵件地址,以便傳送電子賀卡給對方。 + +
      10. +
      +

      使用其他應用程式

      +

      + 允許使用者修改您無法存取的資料的簡單方式,是啟用具備相關權限的應用程式,並且讓使用者透過該應用程式進行修改作業。 + +

      +

      + 例如,日曆應用程式接受可讓您啟用應用程式插入 UI 的 +{@link android.content.Intent#ACTION_INSERT} 意圖。您可以在該意圖中傳入「額外」的資料,供應用程式用於預先填入使用者介面。由於週期性活動的語法較為複雜,因此建議您利用 {@link android.content.Intent#ACTION_INSERT} 啟用日曆應用程式,然後讓使用者透過該應用程式將活動插入日曆供應程式。 + + + + +

      + +

      合約類別

      +

      + 合約類別可定義協助應用程式使用內容 URI、欄名稱、意圖動作,以及內容供應程式的其他功能的常數。 +合約類別並不會自動納入供應程式;供應程式的開發人員必須自行定義合約類別,然後將其提供給其他開發人員。 + +Android 平台內建的大多數供應程式都可在 {@link android.provider} 套件中取得對應的合約類別。 + +

      +

      + 例如,使用者字典供應程式有一個內含內容 URI 和欄名稱常數的 {@link android.provider.UserDictionary} 合約類別。 +「字詞」表格的內容 URI 是在 +{@link android.provider.UserDictionary.Words#CONTENT_URI UserDictionary.Words.CONTENT_URI} 常數中定義。 + + 此外,{@link android.provider.UserDictionary.Words} 類別也包含欄名稱常數,可用於本指南中的程式碼片段範例。 +例如,您可以將查詢投影定義成如下所示: + +

      +
      +String[] mProjection =
      +{
      +    UserDictionary.Words._ID,
      +    UserDictionary.Words.WORD,
      +    UserDictionary.Words.LOCALE
      +};
      +
      +

      + 聯絡人供應程式的另一個合約類別為 {@link android.provider.ContactsContract}。 + 此類別的參考文件附有程式碼片段範例。其中一個 +{@link android.provider.ContactsContract.Intents.Insert} 子類別為內含意圖常數和意圖資料的合約類別。 + +

      + + + +

      MIME 類型參考資料

      +

      + 內容供應程式可傳回標準 MIME 媒體類型或自訂媒體類型字串,或是以上兩者。 +

      +

      + 以下是 MIME 類型的格式 +

      +
      +type/subtype
      +
      +

      + 例如,常見的 MIME 類型 text/html 包含 text 類型以及 html 子類型。 +如果供應程式傳回這種 URI 類型,代表採用該 URI 的查詢會傳回內含 HTML 標記的文字。 + +

      +

      + 自訂 MIME 類型字串 (亦稱為「廠商專用」MIME 類型) 包含較為複雜的類型和子類型值。 +針對多個資料欄,類型值一律會如下所示 +

      +
      +vnd.android.cursor.dir
      +
      +

      + 或者,針對單一資料欄,類型值一律會如下所示 +

      +
      +vnd.android.cursor.item
      +
      +

      + 。 +

      +

      + 子類型為供應程式專用。Android 內建的供應程式通常包含簡易的子類型。 +例如,當聯絡人應用程式建立電話號碼資料列時,會為該列設定下列 MIME 類型: + +

      +
      +vnd.android.cursor.item/phone_v2
      +
      +

      + 請注意,子類型值為 phone_v2。 +

      +

      + 其他的供應程式開發人員可能會根據供應程式的授權和表格名稱,自行建立專屬的子類型模式。 +例如,考慮一個包含火車時刻表的供應程式。 + 供應程式的授權為 com.example.trains 且包括 Line1、Line2 和 Line3 表格。 +根據 Line1 表格的內容 URI +

      +

      +

      +content://com.example.trains/Line1
      +
      +

      + 供應程式會傳回 MIME 類型 +

      +
      +vnd.android.cursor.dir/vnd.example.line1
      +
      +

      + 根據 Line1 表格的內容 URI +

      +
      +content://com.example.trains/Line2/5
      +
      +

      + 供應程式會傳回 MIME 類型 +

      +
      +vnd.android.cursor.item/vnd.example.line2
      +
      +

      + 大多數內容供應程式會針對其使用的 MIME 類型定義合約類別常數。例如,聯絡人供應程式的合約類別 {@link android.provider.ContactsContract.RawContacts} 會為某個 MIME 類型的原始聯絡人列定義 {@link android.provider.ContactsContract.RawContacts#CONTENT_ITEM_TYPE} 常數。 + + + + +

      +

      + 如要進一步瞭解個別資料列的內容 URI,請參閱內容 URI。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd new file mode 100644 index 0000000000000000000000000000000000000000..3d46ee4b4c8092c176a3d31893b819034a94c2af --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-provider-creating.jd @@ -0,0 +1,1214 @@ +page.title=建立內容供應程式 +@jd:body +
      +
      + + +

      本文件內容

      +
        +
      1. + 設計資料儲存空間 +
      2. +
      3. + 設計內容 URI +
      4. +
      5. + 實作 ContentProvider 類別 +
          +
        1. + 必要方法 +
        2. +
        3. + 實作 query() 方法 +
        4. +
        5. + 實作 insert() 方法 +
        6. +
        7. + 實作 delete() 方法 +
        8. +
        9. + 實作 update() 方法 +
        10. +
        11. + 實作 onCreate() 方法 +
        12. +
        +
      6. +
      7. + 實作內容供應程式 MIME 類型 +
          +
        1. + 表格 MIME 類型 +
        2. +
        3. + 檔案 MIME 類型 +
        4. +
        +
      8. +
      9. + 實作合約類別 +
      10. +
      11. + 實作內容供應程式權限 +
      12. +
      13. + <provider> 元素 +
      14. +
      15. + 意圖和資料存取權 +
      16. +
      +

      重要類別

      +
        +
      1. + {@link android.content.ContentProvider} +
      2. +
      3. + {@link android.database.Cursor} +
      4. +
      5. + {@link android.net.Uri} +
      6. +
      +

      相關範例

      +
        +
      1. + Note Pad 範例應用程式 + + +
      2. +
      +

      另請參閱

      +
        +
      1. + 內容供應程式基本存放庫概念 + +
      2. +
      3. + 日曆供應程式 + +
      4. +
      +
      +
      + + +

      + 內容供應程式可管理中央資料存放庫的存取權。您可以將供應程式實作成 Android 應用程式的一或多個類別,以及宣示說明檔案的元素。 + +您的其中一個類別會實作子類別 +{@link android.content.ContentProvider} (供應程式與其他應用程式之間的介面)。 +雖然內容供應程式的用途是將資料提供給其他應用程式,不過您也可以在應用程式中加入 Activity,讓使用者查詢及修改供應程式所管理的資料。 + + +

      +

      + 本主題的其餘部分為一份說明建置內容供應程式的步驟清單,以及一份可供您使用的 API 清單。 + +

      + + + +

      建置供應程式的前置作業

      +

      + 請先完成下列事項,再開始建置供應程式: +

      +
        +
      1. + 評估建置內容供應程式的必要性。如果您想提供下列功能,您就必須建置內容供應程式: + +
          +
        • 您想將複雜的資料或檔案提供給其他應用程式。
        • +
        • 您想讓使用者從您的應用程式將複雜的資料複製到其他應用程式。
        • +
        • 您想使用搜尋架構提供自訂搜尋建議。
        • +
        +

        + 如果您的應用程式內建所有您想提供的功能,您就「不需要」建置供應程式來使用 SQLite 資料庫。 + +

        +
      2. +
      3. + 詳閱內容供應程式基本概念進一步瞭解供應程式 (如果您尚未閱讀該文章的話)。 + + +
      4. +
      +

      + 完成上述事項後,請按照下列步驟建置供應程式: +

      +
        +
      1. + 為您的資料設計原始儲存空間。內容供應程式會以兩種方式提供資料: +
        +
        + 檔案資料 +
        +
        + 通常儲存在檔案中的資料,例如相片、音訊或影片。 +這種檔案會儲存在您應用程式的私人空間中。 +您的供應程式可針對此檔案提供處理支援,藉此回應其他應用程式發出的檔案要求。 + +
        +
        + 「結構化」資料 +
        +
        + 通常儲存在資料庫、陣列或類似結構中的資料。 + 這種資料會採用與內含資料列和資料欄的表格相容的格式儲存。資料列代表一個實體,例如某個人或庫存中的商品。 +而資料欄則代表實體的部分資料,例如某個人的名稱或商品的售價。 +這種資料類型一般會儲存在 SQLite 資料庫,但您也可以將它儲存在其他類型的永久儲存空間。 + +如要進一步瞭解 Android 系統提供的儲存空間類型,請參閱設計資料儲存空間。 + + +
        +
        +
      2. +
      3. + 定義 {@link android.content.ContentProvider} 類別及其所需方法的實作方式。 +這個類別是您的資料與 Android 系統其餘部分之間的介面。 +如要進一步瞭解這個類別,請參閱實作 ContentProvider 類別。 + +
      4. +
      5. + 定義供應程式的授權字串、內容 URI 和資料列名稱。如果您希望供應程式的應用程式能控制意圖,請同時定義意圖動作、額外資料和旗標。 + +此外,您還需要針對想存取您資料的應用程式,定義其所需的權限。 +建議您將這些值定義為個別合約類別中的常數;您之後可將這個類別提供給其他開發人員。 +如要進一步瞭解內容 URI,請參閱設計內容 URI。 + + + 如要進一步瞭解意圖,請參閱意圖和資料存取權。 + +
      6. +
      7. + 視需要進行其他動作,例如新增範例資料,或實作 {@link android.content.AbstractThreadedSyncAdapter} 讓應程式與雲端資料之間的資料保持同步。 + + +
      8. +
      + + + +

      設計資料儲存空間

      +

      + 內容供應程式是模組化格式資料的介面。建議這個介面之前,請先決定您儲存資料的方式。 +您可用任何偏好格式儲存資料,然後設計對應介面來讀取及寫入所需資料。 + +

      +

      + 以下是 Android 提供的部分資料儲存技術: +

      +
        +
      • + Android 系統內含 SQLite 資料庫 API,可讓 Android 本身的供應程式用於儲存以表格為基礎的資料。 + +{@link android.database.sqlite.SQLiteOpenHelper} 類別可協助您建立資料庫,而 {@link android.database.sqlite.SQLiteDatabase} 類別則是用於存取資料庫的基礎類別。 + + +

        + 請記住,您不需要使用資料庫實作存放庫。供應程式會以一組表格的形式對外顯示 (類似於相關的資料庫),不過這並非在內部實作供應程式的必要條件。 + + +

        +
      • +
      • + 如果您想儲存檔案資料,請使用 Android 提供的多種適用於檔案的 API。 + 如要進一步瞭解檔案儲存空間,請參閱資料儲存空間。 +如果您想設計可提供媒體相關資料 (例如音樂或影片) 的供應程式,建議您建立結合表格資料與檔案的供應程式。 + + +
      • +
      • + 如果您想存取以網路為基準的資料,請使用 {@link java.net} 和 +{@link android.net} 中的類別。您也可以將以網路為基準的資料同步到本機資料儲存空間 (例如資料庫),然後以表格或檔案的形式提供資料。 + + 範例同步配接器應用程式範例示範了如何進行這種同步處理作業。 + +
      • +
      +

      + 資料設計注意事項 +

      +

      + 以下提供一些有關設計供應程式資料結構的祕訣: +

      +
        +
      • + 表格資料一律需包含「主索引鍵」欄,方便供應程式保存每個資料列的數值。 +您可以使用這些值將資料列連結至其他表格中的相關資料列 (也就是將這些值當作「外部索引鍵」使用)。 +事實上,您也可以使用此資料欄的任何名稱進行連結,但使用 {@link android.provider.BaseColumns#_ID BaseColumns._ID} 是最佳做法,這是因為將供應程式的查詢結果連結至 +{@link android.widget.ListView} 時需要將某個擷取出的資料列命名為 +_ID。 + + +
      • +
      • + 如果您想提供點陣圖或檔案資料的一大部分,請將資料儲存在檔案中,然後以間接方式提供該檔案,而不要直接將該檔案儲存在表格中。 + +如果您採用這種做法,請務必通知供應程式的使用者採用 +{@link android.content.ContentResolver} 檔案方法來存取資料。 +
      • +
      • + 使用二進位大型物件 (BLOB) 資料類型儲存大小或結構不同的資料。 +例如,您可以使用 BLOB 欄儲存通訊協定緩衝區 或 JSON 結構。 + + +

        + 此外,您也可以使用 BLOB 實作「按結構定義分門別類」的表格。在這種表格中,您需要定義主索引鍵欄、MIME 類型欄和一或多個 BLOB 一般資料欄。 + +MIME 類型欄中的值會決定 BLOB 欄的資料定義, +這可讓您在同一表格中儲存多種資料列類型。 +聯絡人供應程式的「資料」表格 +{@link android.provider.ContactsContract.Data} 即為按結構定義分門別類的表格範例。 + +

        +
      • +
      + +

      設計內容 URI

      +

      + 內容 URI 是指用於識別供應程式資料的 URI,其中包括整個供應程式的符號名稱 (亦即供應程式的授權),以及指向表格或檔案的名稱 (亦即路徑)。 + +選用的 ID 部分則會指向表格中的個別資料欄。 + +{@link android.content.ContentProvider} 的每個資料存取方法均包括一個內容 URI (引數);該內容 URI 可讓您決定要存取哪個表格、資料列或檔案。 + +

      +

      + 如需內容 URI 的基本概念,請參閱內容供應程式基本概念。 + + +

      +

      設計授權

      +

      + 供應程式通常會包含一個授權,可當做供應程式 Android 內部名稱。為了避免與其他供應程式發生衝突,請務必反向使用網際網路網域擁有權作為供應程式授權的基礎。 + +此外,也請針對 Android 套件名稱採取此建議做法;您可以將供應程式授權定義為內含供應程式的套件名稱的副檔名。 + +例如,假設您 Android 套件的名稱為 + com.example.<appname>,則請將供應程式的授權定義為 +com.example.<appname>.provider。 +

      +

      設計路徑結構

      +

      + 開發人員通常只要附加指向個別表格的路徑,即可從授權建立內容 URI。 +例如,假設您有「table1」和「table2」這兩個表格,則您可以結合上述範例中的授權來產生內容 URI +com.example.<appname>.provider/table1 和 +com.example.<appname>.provider/table2。 + +路徑並不侷限於單一區隔,而您也不必為每個路徑層級產生表格。 + +

      +

      處理內容 URI ID

      +

      + 一般來說,供應程式可利用 URI 尾端資料列的 ID 值接受內容 URI,藉此提供某個資料列的存取權。此外,供應程式通常也可比對 ID 值與表格的 _ID 欄,然後對相符的資料列執行要求的存取動作。 + + + +

      +

      + 這種機制可協助採用一般設計模式應用程式存取供應程式,讓應用程式對供應程式執行查詢,並且利用 {@link android.widget.CursorAdapter} 在 {@link android.widget.ListView} 中顯示最終的 {@link android.database.Cursor}。 + + + 定義 {@link android.widget.CursorAdapter} 時需要將 +{@link android.database.Cursor} 的其中一個資料列設為 _ID。 +

      +

      + 使用者之後挑選了顯示在 UI 中的某一資料列,以查看或修改資料。 +應用程式會從支援 + {@link android.widget.ListView} 的 {@link android.database.Cursor} 取得對應的資料列、取得該資料列的 _ID 值,然後將該值附加到內容 URI 並向供應程式發出存取要求。 +供應程式之後可對使用者挑選的資料列進行查詢或修改。 + +

      +

      內容 URI 模式

      +

      + 為協助您決定要對傳入內容 URI 採取什麼動作,供應程式 API 提供了簡便類別 {@link android.content.UriMatcher},可將內容 URI「模式」對應至整數值。 + +您可以在 switch 陳述式中使用整數值,決定要對符合特定模式的內容 URI 採取的動作。 + +

      +

      + 內容 URI 模式會比對採用萬用字元的內容 URI: +

      +
        +
      • + *比對由任何有效字元組成的字串;長度不限。 +
      • +
      • + #比對由數字組成的字串;長度不限。 +
      • +
      +

      + 以設計及編寫內容 URI 處理方式為例,假設供應程式內含 com.example.app.provider 授權,可識別下列指向表格的內容 URI: + + +

      +
        +
      • + content://com.example.app.provider/table1:名稱為 table1 的表格。 +
      • +
      • + content://com.example.app.provider/table2/dataset1:名稱為 +dataset1 的表格。 +
      • +
      • + content://com.example.app.provider/table2/dataset2:名稱為 +dataset2 的表格。 +
      • +
      • + content://com.example.app.provider/table3:名稱為 table3 的表格。 +
      • +
      +

      + 此外,供應程式也會識別附有資料列 ID 的內容 URI,例如 content://com.example.app.provider/table3/1 會指定 table3 中 ID 為 + 1 的資料列。 + +

      +

      + 以下是可能的內容 URI 模式: +

      +
      +
      + content://com.example.app.provider/* +
      +
      + 與供應程式的任何內容 URI 相符。 +
      +
      + content://com.example.app.provider/table2/*: +
      +
      + 與 dataset1dataset2 表格的內容 URI 相符,但與 table1 或 +table3 的內容 URI 不符。 + +
      +
      + content://com.example.app.provider/table3/#:比對 table3 中單一資料列的內容 URI,例如 + content://com.example.app.provider/table3/6 會比對其中的 + 6 所指定的資料列。 + +
      +
      +

      + 以下程式碼片段說明各種方法在 {@link android.content.UriMatcher} 中的運作方式。 + 這個程式碼會以不同方式處理整個表格的 URI 以及單一資料列的 URI;針對表格使用內容 URI 模式 +content://<authority>/<path>,針對單一資料列則使用 +content://<authority>/<path>/<id>。 + +

      +

      + {@link android.content.UriMatcher#addURI(String, String, int) addURI()} 方法會將授權和 +路徑對應至某個整數值,而 {@link android.content.UriMatcher#match(Uri) + match()} 方法會傳回 URI 的整數值。switch 陳述式則會要查詢整個表格,還是查詢單一記錄: + +

      +
      +public class ExampleProvider extends ContentProvider {
      +...
      +    // Creates a UriMatcher object.
      +    private static final UriMatcher sUriMatcher;
      +...
      +    /*
      +     * The calls to addURI() go here, for all of the content URI patterns that the provider
      +     * should recognize. 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
      +     */
      +    sUriMatcher.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.
      +     */
      +    sUriMatcher.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 (sUriMatcher.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 is not recognized, you should do some error handling here.
      +        }
      +        // call the code to actually do the query
      +    }
      +
      +

      + 另一個 {@link android.content.ContentUris} 類別可提供使用內容 URI 的 id 部分的簡便方法。 +{@link android.net.Uri} 和 +{@link android.net.Uri.Builder} 類別則可提供剖析現有 +{@link android.net.Uri} 物件及建置新物件的簡便方法。 +

      + + +

      實作 ContentProvider 類別

      +

      + {@link android.content.ContentProvider} 執行個體可透過處理其他應用程式所發出要求的方式,管理一組結構化資料的存取權。 +所有形式的存取權最終都會呼叫 {@link android.content.ContentResolver},而這個類別隨後會呼叫 {@link android.content.ContentProvider} 方法來取得存取權。 + + +

      +

      必要方法

      +

      + 抽象類別 {@link android.content.ContentProvider} 會定義 6 種方法,而您必須將這些方法實作成您所擁有子類別的一部分。 +嘗試存取您內容供應程式的用戶端應用程式會呼叫以下所有方法 +({@link android.content.ContentProvider#onCreate() onCreate()} 除外): + +

      +
      +
      + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + query()} +
      +
      + 從您的供應程式擷取檔案。您可以使用引數來選取要查詢的表格、要傳回的資料列和資料欄,以及最終結果的排序順序。 + + 以 {@link android.database.Cursor} 物件的形式傳回資料。 +
      +
      + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} +
      +
      + 在供應程式中插入新的資料列。您可以使用引數來選取目標表格,以及取得要使用的資料欄值。 +此外,這個方法還可傳回新插入資料欄的內容 URI。 + +
      +
      + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} +
      +
      + 更新供應程式中的現有資料欄。您可以使用引數來選取要更新的表格和資料列,以及取得更新過後的資料欄值。 +此外,這個方法還可傳回經過更新的資料列數量。 +
      +
      + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} +
      +
      + 從您的供應程式中刪除資料列。您可以使用引數來選取要刪除的表格和資料列。 +此外,這個方法還可傳回遭刪除的資料列數量。 +
      +
      + {@link android.content.ContentProvider#getType(Uri) getType()} +
      +
      + 傳回與內容 URI 相對應的 MIME 類型。如要進一步瞭解這個方法,請參閱實作內容供應程式 MIME 類型。 + +
      +
      + {@link android.content.ContentProvider#onCreate() onCreate()} +
      +
      + 初始化您的供應程式。Android 系統會在建立供應程式後立即呼叫這個方法。 +請注意,一旦 + {@link android.content.ContentResolver} 嘗試存取您的供應程式,Android 系統便會建立供應程式。 +
      +
      +

      + 請注意,上述方法採用的簽名與同名的 +{@link android.content.ContentResolver} 方法相同。 +

      +

      + 實作這些方法時請注意下列事項: +

      +
        +
      • + 多重執行緒可能會同時呼叫 {@link android.content.ContentProvider#onCreate() onCreate()} 以外的所有方法,因此請務必確保這些方法不會對執行緒造成負面影響。 +如要進一步瞭解多重執行緒,請參閱處理程序和執行緒。 + + + +
      • +
      • + 避免透過 {@link android.content.ContentProvider#onCreate() + onCreate()} 進行需大量時間才可完成的作業。除非有實質上的需求,否則請延遲初始化工作。 + 如需相關資訊,請參閱實作 onCreate() 方法。 + +
      • +
      • + 雖然您必須實作這些方法,但您的程式碼只需傳回預期的資料類型即可,不必傳回任何其他資料。 +例如,您可能想避免其他應用程式將資料插入部分表格。 +在這種情況下,您可以略過呼叫 +{@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 並傳回 0。 + +
      • +
      +

      實作 query() 方法

      +

      + {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 方法如果未傳回 {@link android.database.Cursor} 物件,便會發生錯誤而擲回 {@link java.lang.Exception}。 + +如果您採用 SQLite 資料庫做為資料儲存空間,您可以直接傳回 +{@link android.database.sqlite.SQLiteDatabase} 類別的任一 query() 方法所傳回的 {@link android.database.Cursor}。 + + 如果查詢不符任何資料列,您就必須傳回其中的 {@link android.database.Cursor#getCount()} 方法傳回 0 的 + {@link android.database.Cursor} 執行個體。 + 請注意,只有在查詢程序發生內部錯誤時,您才需要傳回 null。 +

      +

      + 如果您並非採用 SQLite 資料庫做為資料儲存空間,請使用 + {@link android.database.Cursor} 的子類別。例如,{@link android.database.MatrixCursor} 類別 +會一系列 {@link java.lang.Object} 的所有資料列中實作游標。您可以搭配這個類別使用 {@link android.database.MatrixCursor#addRow(Object[]) addRow()} 來新增資料列。 + +

      +

      + 請記住,您必須確保 Android 系統能夠與 {@link java.lang.Exception} 通訊,而不會受到處理程序的限制。 +Android 可以針對下列例外狀況執行這項動作,藉此協助解決查詢錯誤: + +

      +
        +
      • + {@link java.lang.IllegalArgumentException} (如果供應程式接收的內容 URI 無效,您可以選擇擲回這個類別) + +
      • +
      • + {@link java.lang.NullPointerException} +
      • +
      +

      實作 insert() 方法

      +

      + {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 方法會利用 {@link android.content.ContentValues} 引數中的值,在適當的表格中新增資料列。 + +如果 {@link android.content.ContentValues} 引數中沒有資料欄名稱,建議您在供應程式的程式碼或資料庫的結構定義中,提供預設的資料欄名稱。 + + +

      +

      + 這個方法會傳回新資料列的內容 URI。如要建構這個方法,請使用 {@link android.content.ContentUris#withAppendedId(Uri, long) withAppendedId()},將新資料列的 _ID (或其他主索引鍵) 值附加到表格的內容 URI。 + + +

      +

      實作 delete() 方法

      +

      + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 方法並不會從您的資料儲存空間中刪除資料列。 +如果您搭配供應程式使用同步配接器,建議您為已刪除的資料列加上「已刪除」標示,而不是徹底移除資料列。 + +同步配接器可檢查已刪除的資料列,並且將這些資料列從伺服器中移除,然後再從供應程式中將它們刪除。 + +

      +

      實作 update() 方法

      +

      + {@link android.content.ContentProvider#update(Uri, ContentValues, String, String[]) + update()} 方法會採用 {@link android.content.ContentProvider#insert(Uri, ContentValues) insert()} 所使用的相同 {@link android.content.ContentValues} 引數,以及 + {@link android.content.ContentProvider#delete(Uri, String, String[]) delete()} 和 {@link android.content.ContentProvider#query(Uri, String[], String, String[], String) + ContentProvider.query()} 所使用的相同 selectionselectionArgs 引數, + + +以便讓您針對這些方法重複使用相同的程式碼。 +

      +

      實作 onCreate() 方法

      +

      + Android 系統會在啟動供應程式時呼叫 {@link android.content.ContentProvider#onCreate() + onCreate()}。建議您只使用這個方法執行可快速完成的初始化工作,並且等到供應程式實際收到資料要求後,再建立資料庫以及載入資料。 + +如果您使用 {@link android.content.ContentProvider#onCreate() onCreate()} 進行需大量時間才能完成工作,啟動供應程式所需的時間就會延長。 + +此外,這樣也會延遲供應程式回應其他應用程式的時間。 + +

      +

      + 例如,如果您採用 SQLite 資料庫,您可以透過 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 建立新的 {@link android.database.sqlite.SQLiteOpenHelper} 物件,然後在初次開啟資料庫時建立 SQL 表格。 + +為了加快這個程序,當您初次呼叫 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase +getWritableDatabase()} 時,該方法會自動呼叫 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()} 方法。 + + +

      +

      + 以下兩個程式碼片段展示了 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()} 與 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) + SQLiteOpenHelper.onCreate()} 之間的互動過程。 +而第一個程式碼片段是用於實作 +{@link android.content.ContentProvider#onCreate() ContentProvider.onCreate()}: +

      +
      +public class ExampleProvider extends ContentProvider
      +
      +    /*
      +     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
      +     * in a following snippet.
      +     */
      +    private MainDatabaseHelper mOpenHelper;
      +
      +    // Defines the database name
      +    private static final String DBNAME = "mydb";
      +
      +    // Holds the database object
      +    private SQLiteDatabase db;
      +
      +    public boolean onCreate() {
      +
      +        /*
      +         * Creates a new helper object. This method always returns quickly.
      +         * Notice that the database itself isn't created or opened
      +         * until SQLiteOpenHelper.getWritableDatabase is called
      +         */
      +        mOpenHelper = new MainDatabaseHelper(
      +            getContext(),        // the application context
      +            DBNAME,              // the name of the database)
      +            null,                // uses the default SQLite cursor
      +            1                    // the version number
      +        );
      +
      +        return true;
      +    }
      +
      +    ...
      +
      +    // Implements the provider's insert method
      +    public Cursor insert(Uri uri, ContentValues values) {
      +        // Insert code here to determine which table to open, handle error-checking, and so forth
      +
      +        ...
      +
      +        /*
      +         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
      +         *
      +         */
      +        db = mOpenHelper.getWritableDatabase();
      +    }
      +}
      +
      +

      + 第二個程式碼片段則是用於實作 {@link android.database.sqlite.SQLiteOpenHelper#onCreate(SQLiteDatabase) +SQLiteOpenHelper.onCreate()},包括協助程式類別: + +

      +
      +...
      +// A string that defines the SQL statement for creating a table
      +private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
      +    "main " +                       // Table's name
      +    "(" +                           // The columns in the table
      +    " _ID INTEGER PRIMARY KEY, " +
      +    " WORD TEXT"
      +    " FREQUENCY INTEGER " +
      +    " LOCALE TEXT )";
      +...
      +/**
      + * Helper class that actually creates and manages the provider's underlying data repository.
      + */
      +protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
      +
      +    /*
      +     * Instantiates an open helper for the provider's SQLite data repository
      +     * Do not do database creation and upgrade here.
      +     */
      +    MainDatabaseHelper(Context context) {
      +        super(context, DBNAME, null, 1);
      +    }
      +
      +    /*
      +     * Creates the data repository. This is called when the provider attempts to open the
      +     * repository and SQLite reports that it doesn't exist.
      +     */
      +    public void onCreate(SQLiteDatabase db) {
      +
      +        // Creates the main table
      +        db.execSQL(SQL_CREATE_MAIN);
      +    }
      +}
      +
      + + + +

      實作 ContentProvider MIME 類型

      +

      + {@link android.content.ContentProvider} 類別包含兩種用於傳回 MIME 類型的方法: +

      +
      +
      + {@link android.content.ContentProvider#getType(Uri) getType()} +
      +
      + 您必須針對任何供應程式實作的其中一個必要方法。 +
      +
      + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} +
      +
      + 如果您的供應程式會提供檔案,您就需要實作這個方法。 +
      +
      +

      表格 MIME 類型

      +

      + {@link android.content.ContentProvider#getType(Uri) getType()} 方法會採用 MIME 格式傳回 + {@link java.lang.String},以說明 URI 引數所傳回的資料類型。 +{@link android.net.Uri} 可以是一個模式 (而非特定 URI);在這種情況下,建議您傳回與符合模式的內容 URI 相關聯的資料類型。 + + +

      +

      + 針對文字、HTML、JPEG 等常見資料類型, +{@link android.content.ContentProvider#getType(Uri) getType()} 會傳回該資料的標準 MIME 類型。 +如需這些標準模式的完整清單,請造訪 + IANA MIME 媒體類型網站。 + +

      +

      + 針對指向表格資料的資料列的內容 URI, +{@link android.content.ContentProvider#getType(Uri) getType()} 會採用 Android 廠商專用的 MINE 格式傳回 MIME 類型: + +

      +
        +
      • + 類型部分:vnd +
      • +
      • + 子類型部分: +
          +
        • + 如果 URI 模式適用於單一資料列:android.cursor.item/ +
        • +
        • + 如果 URI 模式適用於一個以上的資料列:android.cursor.dir/ +
        • +
        +
      • +
      • + 供應程式專用部分:vnd.<name>.<type> +

        + 您需要提供 <name><type>。 + <name> 必須是全域唯一值,而 <type> 必須為相對應 URI 模式的專屬值。 + +建議您使用貴公司的名稱或您應用程式的部分 Android 套件名稱做為 <name>。 +針對 +<type>,則建議您使用可識別與 URI 相關的表格的字串。 + +

        + +
      • +
      +

      + 例如,假設供應程式的授權為 +com.example.app.provider,而該授權可提供 +table1 這個表格,則 table1 中多個資料列的 MIME 類型會如下所示: +

      +
      +vnd.android.cursor.dir/vnd.com.example.provider.table1
      +
      +

      + 而 table1 中單一資料列的 MIME 類型則會如下所示: +

      +
      +vnd.android.cursor.item/vnd.com.example.provider.table1
      +
      +

      檔案 MIME 類型

      +

      + 如果您的供應程式會提供檔案,您就需要實作 +{@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()}。 + 該方法會針對您的供應程式可為特定 URI 傳回的檔案,傳回一系列 MIME 類型的 {@link java.lang.String}。建議您按 MIME 類型篩選器引數篩選您提供的 MIME 類型,方便您只傳回用戶端想處理的 MIME 類型。 + + +

      +

      + 例如,假設您的供應程式會採用 .jpg、 +.png.gif 檔案格式提供相片。 + 當有應用程式使用篩選器字串 image/* (類型為「圖片」的任何檔案) 呼叫 {@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()} 時,{@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} 方法就會傳回如下所示的陣列: + + +

      +
      +{ "image/jpeg", "image/png", "image/gif"}
      +
      +

      + 如果應用程式只想取得 .jpg 檔案,則可以使用篩選器字串 *\/jpeg 呼叫 {@link android.content.ContentResolver#getStreamTypes(Uri, String) +ContentResolver.getStreamTypes()},此時 {@link android.content.ContentProvider#getStreamTypes(Uri, String) +ContentProvider.getStreamTypes()} 會傳回如下所示的陣列: + + +

      +{"image/jpeg"}
      +
      +

      + 如果您的供應程式並未提供任何篩選器字串所要求的 MIME 類型,則 + {@link android.content.ContentProvider#getStreamTypes(Uri, String) getStreamTypes()} 會傳回 null。 + +

      + + + +

      實作合約類別

      +

      + 合約類別是 public final 類別,內含以下項目的固定不變定義:URI、欄名稱、MIME 類型以及供應程式擁有的其他中繼資料。 +這個類別會在供應程式與其他應用程式之間建立合約,藉此確保使用者在 URI、欄名等值有變更的情況下,仍可正常存取供應程式。 + + + +

      +

      + 此外,由於合約類別針對其常數採用了好記的名稱,因此可協助開發人員避免使用錯誤的欄名或 URI 值。 +與其他類別相同,合約類別也包含 Javadoc 說明文件。 +如 Eclipse 這類整合式開發環境可利用合約類別自動填入常數名稱,並針對常數顯示 Javadoc。 + + +

      +

      + 開發人員無法存取您應用程式中合約類別的類別檔案,但可以靜態方式從您提供的 .jar 檔案中將類別檔案編入其應用程式。 + +

      +

      + {@link android.provider.ContactsContract} 類別及其巢狀類別均為合約類別範例。 + +

      +

      實作內容供應程式權限

      +

      + 如需 Android 系統提供的所有權限和存取權的詳細資訊,請參閱安全性和權限。 + + 您也可以參閱資料儲存空間,瞭解各種儲存空間實際提供的安全性和權限。 + + 簡言之,請注意下列重點: +

      +
        +
      • + 在預設情況下,儲存在裝置內部儲存空間的資料檔案只有您的應用程式和供應程式可存取。 + +
      • +
      • + 您所建立的 {@link android.database.sqlite.SQLiteDatabase} 資料庫只有您的應用程式和供應程式可存取。 + +
      • +
      • + 在預設情況下,您儲存到外部儲存空間的資料檔案為「公開分享」,任何人均可讀取。 +您無法使用內容供應程式針對外部儲存空間中的檔案限制存取權,這是因為其他應用程式可使用其他 API 呼叫讀取及寫入這類檔案。 + +
      • +
      • + 呼叫用於開啟/建立檔案,或是用於在裝置內部儲存空間中開啟/建立 SQLite 儲存庫的方法,可能會同時將讀取及寫入存取權授予其他所有應用程式。 +如果您採用內部檔案或資料庫做為供應程式的存放庫,並且將「開放讀取」或「開放寫入」存取權授予該存放庫,則您在供應程式的宣示說明中設定的權限無助於保護您的資料安全。 + + +內部儲存空間中的檔案和儲存庫的預設存取權為「不公開」;請勿針對供應程式的存放庫變更此存取權。 + +
      • +
      +

      + 如果您想使用內容供應程式權限控制資料的存取權,建議您將資料儲存在內部檔案、SQLite 資料庫或「雲端」(例如遠端伺服器),並確保只有您的應用程式可以存取這些檔案和資料庫。 + + +

      +

      實作權限

      +

      + 在預設情況下,您的供應程式不會設定任何權限,因此即使底層資料的存取權限為「不公開」,任何應用程式都可透過您的供應程式讀取及寫入資料。 +如果想改變這種情況,請使用屬性或 + <provider> 元素的子元素,在供應程式的宣示說明檔案中設定權限。 + +您可以選擇讓設定好的權限套用至整個供應程式、特定表格或記錄,或是套用至以上三者。 + +

      +

      + 您可以使用一或多項 + <permission> 元素,在供應程式的宣示說明檔案中定義權限。 +為了將權限設為僅適用於您的供應程式,請針對 + + android:name 屬性使用 Java 式範圍。 +例如,請將讀取權限命名為 +com.example.app.provider.permission.READ_PROVIDER。 + +

      +

      + 以下列出供應程式權限範圍的詳細說明;這份清單將從套用至整個供應程式的權限開始說明,接著逐一說明套用範圍較小的權限。 + + 套用範圍較小權限的優先等級會比套用範圍較大的權限來得高: +

      +
      +
      + 供應程式層級的單一讀取寫入權限 +
      +
      + 這項權限是由 + <provider> 元素的 + + android:permission 屬性所指定,可控制整個供應程式的讀取及寫入存取權。 + +
      +
      + 供應程式層級的個別讀取及寫入權限 +
      +
      + 整個供應程式的讀取權限及寫入權限。您可以使用 + <provider> 元素的 + + android:readPermission 和 + + android:writePermission 屬性指定這兩項權限。 +這些權限的優先等級比 + + android:permission 所需的權限來得高。 +
      +
      + 路徑層級權限 +
      +
      + 供應程式內容 URI 的讀取、寫入或讀取/寫入權限。您可以使用 + + <provider> 元素的 + + <path-permission> 子元素指定您想控制的所有 URI。 +針對您所指定的每個內容 URI,您可以指定讀取/寫入權限、讀取權限或寫入權限,或是以上三種權限。 +讀取及寫入權限的優先等級比讀取/寫入權限來得高。 +此外,路徑層級權限的優先等級比供應程式層級權限來得高。 + +
      +
      + 臨時權限 +
      +
      + 這種權限層級可將臨時存取權授予某個應用程式,即使該應用程式不具備一般的必要權限。 +臨時存取功能可減少應用程式的宣示說明所需的權限數量。 + +在啟用臨時權限的情況下,只有會繼續存取您資料的應用程式,需要供應程式的「永久」權限。 + + +

      + 如果想允許外部的圖片檢視器應用程式顯示您供應程式中的相片附加檔案,請將實作電子郵件供應程式和應用程式時所需的權限納入考量。 + +如要授予圖片檢視器必要的存取權,而不發出權限要求,請設定相片內容 URI 的臨時權限。 +並且將您的電子郵件應用程式設計成在使用者想顯示相片時,將內含相片內容 URI 和權限旗標的意圖傳送給圖片檢視器。 + +讓圖片檢視器在檢視器沒有供應程式的一般讀取權限的情況下,仍可查詢電子郵件供應程式來擷取相片。 + + +

      +

      + 如要啟用臨時權限,請設定 + + <provider> 元素的 + + android:grantUriPermissions 元素,或是在 + + <provider> 元素中新增一或多項 + + <grant-uri-permission> 子元素。如果您有使用臨時權限,您就必須在從供應程式中移除內容 URI 支援,以及將內容 URI 與臨時權限建立關聯時呼叫 {@link android.content.Context#revokeUriPermission(Uri, int) +Context.revokeUriPermission()}。 + + +

      +

      + 屬性的值會決定供應程式的可存取部分。 + 如果將屬性設為 true,則系統會將臨時權限授予整個供應程式,從而覆寫供應程式層級或路徑層級權限所需的任何其他權限。 + + +

      +

      + 如果將這個旗標設為 false,您就必須在 + + <provider> 元素中加入 + + <grant-uri-permission> 子元素。每項子元素都會指定要授予臨時存取權的內容 URI。 + +

      +

      + 如要將臨時存取權委派給某款應用程式,您就必須在意圖中加入 +{@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION} 或 +{@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION} 旗標,或是同時加入以上兩者。請使用 +{@link android.content.Intent#setFlags(int) setFlags()} 方法設定這些旗標。 +

      +

      + 如果系統未顯示 + android:grantUriPermissions 屬性,代表該屬性是設為 + false。 +

      +
      +
      + + + + +

      <provider> 元素

      +

      + 與 {@link android.app.Activity} 和 {@link android.app.Service} 元件相同,您必須使用 + + <provider> 元素在應用程式的宣示說明檔案中定義 {@link android.content.ContentProvider} 子類別:Android 系統會從元素取得下列資訊: + + + +

      +
      + 授權 + ({@code + android:authorities}) +
      +
      + 用於識別系統內整個供應程式的符號名稱。如要進一步瞭解這項屬性,請參閱設定內容 URI。 + + +
      +
      + 供應程式類別名稱 + ( +android:name + ) +
      +
      + 實作 {@link android.content.ContentProvider} 的類別。如要進一步瞭解這個類別,請參閱實作 ContentProvider 。 + + +
      +
      + 權限 +
      +
      + 這些屬性可指定其他應用程式在存取供應程式的資料時所需的權限: + + +

      + 如要進一步瞭解權限及相對應的屬性,請參閱實作內容供應程式權限。 + + +

      +
      +
      + 啟動及控制屬性 +
      +
      + 這些屬性可決定 Android 系統啟動供應程式的方式和時間、供應程式的處理程序特性,以及其他執行階段設定: + + +

      + 如需上述屬性的完整說明,請參閱開發人員指南的 + + <provider> 元素。 + +

      +
      +
      + 資訊屬性 +
      +
      + 選用的供應程式圖示和標籤: +
        +
      • + + android:icon:內含供應程式圖示的可繪資源。 + 這個圖示會顯示在應用程式清單 ([設定] > [應用程式] > [全部]) 中,供應程式標籤的旁邊。 + +
      • +
      • + + android:label:附有供應程式或其資料或以上兩者的說明的資訊標籤。 +這個標籤會顯示在應用程式清單 ([設定] > [應用程式] > [全部]) 中。 + +
      • +
      +

      + 如需上述屬性的完整說明,請參閱開發人員指南的 + + <provider> 元素。 +

      +
      +
      + + +

      意圖和資料存取權

      +

      + 應用程式可透過 {@link android.content.Intent} 以間接方式存取內容供應程式。 + 利用這種存取方式的應用程式不會呼叫 {@link android.content.ContentResolver} 或 +{@link android.content.ContentProvider} 的任何方法,而是會傳送可啟動 Activity (此 Activity 通常屬於供應程式本身的應用程式) 的意圖。 +目標 Activity 會負責擷取資料並在本身的 UI 中顯示該資料。視意圖中的動作而定,目標 Activity 也可能會提示使用者修改供應程式的資料。 + + + 此外,意圖還可能會包含目標 Activity 顯示在 UI 中的「額外」資料;使用者之後可選擇是否要先變更這些資料,然後再將其用於修改供應程式的資料。 + + +

      +

      + +

      +

      + 您可以使用意圖存取權來確保資料的完整性。您的供應程式可能會將根據詳細定義的業務邏輯插入、更新及刪除的資料做為運作依據。 +如果是這樣,允許其他應用程式直接修改您的資料可能會導致資料失效。 + +如果想讓開發人員使用意圖存取權,請務必保留相關的完整記錄。 + 並且向他們說明為何使用您應用程式 UI 的意圖存取權,比嘗試使用自己的程式碼修改資料來得好。 + +

      +

      + 處理想修改供應程式資料的傳入意圖的方式與處理其他意圖完全相同。 +如要進一步瞭解如何使用意圖,請參閱意圖和意圖篩選器。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd new file mode 100644 index 0000000000000000000000000000000000000000..7f7fa34b9c0cfa98b10724adfeb2290c52243543 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/content-providers.jd @@ -0,0 +1,108 @@ +page.title=內容供應程式 +@jd:body +
      +
      + + + +

      本文主題

      +
        +
      1. + 內容供應程式基本概念 + +
      2. +
      3. + 建立內容供應程式 + +
      4. +
      5. + 日曆供應程式 +
      6. +
      7. + 聯絡人供應程式 +
      8. +
      + + +

      相關範例

      +
        +
      1. + 聯絡人管理員應用程式 + +
      2. +
      3. + 「游標 (使用者)」 + + +
      4. +
      5. + 「游標 (電話)」 + +
      6. +
      7. + 範例同步配接器 + +
      8. +
      +
      +
      +

      + 內容供應程式可管理一組結構化資料的存取權、壓縮資料以及提供用於定義資料安全性的機制。 +內容供應程式是一種標準介面,可將某個處理程序中的資料與另一個處理程序中執行的程式碼建立連結。 + +

      +

      + 如果想透過內容供應程式存取資料,請使用您應用程式的 {@link android.content.Context} 中的{@link android.content.ContentResolver} 物件,以用戶端的身分與供應程式通訊。 + + + {@link android.content.ContentResolver} 物件會與供應程式物件 (實作 {@link android.content.ContentProvider} 的類別執行個體) 通訊。 +而供應程式物件則會接收用戶端發出的資料要求、執行要求的動作,以及傳回最終結果。 + + +

      +

      + 如果您打算與其他應用程式分享您的資料,您不必自行開發供應程式。 +不過,您必須為自己的應用程式準備專屬供應程式,以提供自訂搜尋建議。 +此外,如果您想從自己的應用程式複製複雜的資料或檔案並貼到其他應用程式,也需要準備專屬的供應程式。 + +

      +

      + Android 隨附內容供應程式,可用於管理音訊、影片、圖片、個人聯絡資訊等資料。 +如果想查看 Android 隨附的部分內容供應程式,請參閱 android.provider + 套件的參考文件。 + +這些供應程式可透過任何 Android 應用程式存取,只不過使用上有些許限制。 + +

      + 如果想進一步瞭解內容供應程式,請參閱下列主題: +

      +
      +
      + + 內容供應程式基本概念 +
      +
      + 如何在資料已整理成表格的情況下,透過內容供應程式存取資料。 +
      +
      + + 建立內容供應程式 +
      +
      + 如何自行建立內容供應程式。 +
      +
      + + 日曆供應程式 +
      +
      + 如何存取屬於 Android 平台一部分的日曆供應程式。 +
      +
      + + 聯絡人供應程式 +
      +
      + 如何存取屬於 Android 平台一部分的聯絡人供應程式。 +
      +
      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd b/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd new file mode 100644 index 0000000000000000000000000000000000000000..1dc7c46f43873167e4c2be88a2116d2691d6d685 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/providers/document-provider.jd @@ -0,0 +1,916 @@ +page.title=儲存空間存取架構 +@jd:body + + + +

      Android 4.4 (API 級別 19) 導入了「儲存空間存取架構」(Storage Access Framework (SAF)),SAF 可方便使用者透過偏好的文件儲存空間供應程式開啟文件、圖片等其他檔案。 + +提供簡單易用的標準 UI 可讓使用者在各種應用程式和供應程式中,以相同的方式瀏覽檔案及存取近期開啟的檔案。 +

      + +

      雲端或本機儲存服務可實作會封裝服務本身的 +{@link android.provider.DocumentsProvider},藉此加入這個生態系統。您只需編寫幾行程式碼,即可將需要存取供應程式文件的用戶端應用程式與 SAF 整合。 + +

      + +

      SAF 內含下列項目:

      + +
        +
      • 文件供應程式 — 可讓儲存服務 (例如 Google 雲端硬碟) 顯示所管理檔案的內容供應程式。 +文件供應程式是當作 +{@link android.provider.DocumentsProvider} 類別的子類別使用。文件供應程式結構定義是以傳統檔案階層為依據,不論您為文件供應程式設定的資料儲存方式為何。Android 平台內建數種文件供應程式,例如「下載」、「圖片」和「影片」。 + + + +
      • + +
      • 用戶端應用程式 — 可呼叫 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和 (或) +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖及接收文件供應程式所傳回檔案的自訂應用程式。 +
      • + +
      • 挑選器 — 可讓使用者透過所有符合用戶端應用程式搜尋條件的文件供應程式存取文件的系統 UI。 +
      • +
      + +

      以下是 SAF 提供的部分功能:

      +
        +
      • 可讓使用者透過所有文件供應程式 (而非單一應用程式) 瀏覽內容。
      • +
      • 可將文件供應程式所擁有文件的存取權永久授予您的應用程式, +方便使用者透過相關供應程式新增、編輯、儲存及刪除檔案。 +
      • +
      • 支援多個使用者帳戶和暫時性的根目錄,例如只在插入電腦時才會顯示的 USB 儲存空間供應程式。 +
      • +
      + +

      總覽

      + +

      SAF 是以內容供應程式 ({@link android.provider.DocumentsProvider} 類別的子類別) 為基礎。 +「文件供應程式」中的資料結構採用如下所示的傳統檔案階層: +

      +

      data model

      +

      圖 1.文件供應程式資料模型。「根目錄」會指向單一「文件」,接著該文件會展開成樹狀結構的分支。請注意下列事項: +

      + +

      +
        + +
      • 每個文件供應程式都會回報一或多個「根目錄」(也就是文件樹狀結構的起始點)。每個根目錄都有專屬的 {@link android.provider.DocumentsContract.Root#COLUMN_ROOT_ID},可導向至代表該根目錄所含內容的某份文件 (某個目錄)。根目錄的設計架構是動態的,能夠支援多重帳戶、暫時性 USB 儲存裝置或使用者登入/登出等使用狀況。 + + + + + +
      • + +
      • 每個根目錄都內含一份文件,而該文件會指向 N 份文件的 1,每份文件又可指向另外 N 份文件的 1。 +
      • + +
      • 每個儲存空間後端都會透過唯一的 +{@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 來參照個別檔案,藉此顯示這些檔案及目錄。文件 ID 不得重複而且一旦核發便不得更改,原因在於裝置重新啟動時會將這些 ID 用於永久 URI 授權。 + + +
      • + + +
      • 文件可以是可開啟的檔案 (類型為 MIME) 或內含其他文件的目錄 (類型為 +{@link android.provider.DocumentsContract.Document#MIME_TYPE_DIR} MIME )。 +
      • + +
      • 每份文件的功能會視 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} 而有所不同,例如 {@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_WRITE}、 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE} 和 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_THUMBNAIL}。相同的 {@link android.provider.DocumentsContract.Document#COLUMN_DOCUMENT_ID} 可以納入多個目錄中。 + + +
      • +
      + +

      控管流程

      +

      如上所述,文件供應程式資料模型是以傳統檔案階層為基礎。 +不過,您可以自己偏好的方式儲存您的資料,只要所儲存資料可透過 {@link android.provider.DocumentsProvider} API 存取即可。例如,您可以將資料存放在標籤式的雲端儲存空間。 + +

      + +

      圖 2 是相片應用程式如何使用 SAF 存取已儲存資料的說明範例: +

      +

      app

      + +

      圖 2.儲存空間存取架構

      + +

      請注意下列事項:

      +
        + +
      • 在 SAF 中,供應程式與用戶端無法直接進行互動。 +用戶端必須取得相關權限才能與檔案進行互動 (也就是讀取、編輯、建立或刪除檔案)。 +
      • + +
      • 應用程式 (在此範例中為相片應用程式) 觸發 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 或 {@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖後,互動程序便會開始。意圖可能包括用於縮小條件範圍的篩選器 — 例如「將所有內含 MIME 類型『圖片』的可開啟檔案提供給我」。 + +
      • + +
      • 一旦觸發意圖,系統挑選器就會前往所有已註冊的供應程式,並且向使用者顯示相符的內容根目錄。 +
      • + +
      • 即便底層文件供應程式可能不盡相同,挑選器仍會提供使用者可用於存取文件的標準介面。 +例如圖 2 中的 Google 雲端硬碟供應程式、USB 供應程式和雲端供應程式。 +
      • +
      + +

      圖 3 顯示的是使用者搜尋指定 Google 雲端硬碟帳戶中的圖片時所用的挑選器: +

      + +

      picker

      + +

      圖 3.挑選器

      + +

      使用者選取 Google 雲端硬碟後,系統就會顯示相關圖片 (如圖 4 所示)。 +此時,使用者即可與這些圖片進行供應程式和用戶端應用程式支援的互動。 + + +

      picker

      + +

      圖 4.相關圖片

      + +

      編寫用戶端應用程式

      + +

      如果您想讓應用程式在搭載 Android 4.3 以下版本的裝置上從其他應用程式擷取檔案,您的應用程式就必須呼叫 {@link android.content.Intent#ACTION_PICK}或 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖。 + +接著,使用者必須選取某款應用程式來選取檔案,而且選定的應用程式必須提供使用者介面,讓使用者瀏覽及挑選可用的檔案。 + +

      + +

      針對搭載 Android 4.4 以上版本的裝置,您的應用程式還可以呼叫 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖,以顯示系統所控管的挑選器 UI,方便使用者瀏覽其他應用程式提供的所有檔案。 + +透過這個單一 UI,使用者可以從任何受支援的應用程式挑選檔案。 +

      + +

      {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 並不是 {@link android.content.Intent#ACTION_GET_CONTENT} 的替代意圖,實際上應呼叫的意圖取決於您應用程式的需求。 + +

      + +
        +
      • 如果您只想讓應用程式讀取/匯入資料,請呼叫 {@link android.content.Intent#ACTION_GET_CONTENT}, +以便應用程式匯入資料 (例如圖片檔) 的複本。 +
      • + +
      • 如果您想將文件供應程式所擁有文件的存取權永久授予您的應用程式,請呼叫 {@link android.content.Intent#ACTION_OPEN_DOCUMENT}。 + +例如可讓使用者編輯文件供應程式中所儲存圖片的相片編輯應用程式。 +
      • + +
      + + +

      本節說明如何根據 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 和 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖編寫用戶端應用程式。

      + + + + +

      +以下程式碼片段採用 {@link android.content.Intent#ACTION_OPEN_DOCUMENT},可搜尋內含圖片檔的文件供應程式: + +

      + +
      private static final int READ_REQUEST_CODE = 42;
      +...
      +/**
      + * Fires an intent to spin up the "file chooser" UI and select an image.
      + */
      +public void performFileSearch() {
      +
      +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
      +    // browser.
      +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
      +
      +    // Filter to only show results that can be "opened", such as a
      +    // file (as opposed to a list of contacts or timezones)
      +    intent.addCategory(Intent.CATEGORY_OPENABLE);
      +
      +    // Filter to show only images, using the image MIME data type.
      +    // If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
      +    // To search for all documents available via installed storage providers,
      +    // it would be "*/*".
      +    intent.setType("image/*");
      +
      +    startActivityForResult(intent, READ_REQUEST_CODE);
      +}
      + +

      請注意下列事項:

      +
        +
      • 當應用程式觸發 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖時,挑選器便會啟動並顯示所有相符的文件供應程式。 +
      • + +
      • 將 {@link android.content.Intent#CATEGORY_OPENABLE} 這個類別加入意圖中可篩選搜尋結果,限定系統只顯示可開啟的文件 (例如圖片檔)。 +
      • + +
      • 使用 {@code intent.setType("image/*")} 陳述式可進一步篩選搜尋結果,顯示系統只顯示內含 MIME 類型圖片的文件。 +
      • +
      + +

      處理結果

      + +

      使用者在挑選器中選取某份文件後,便會呼叫 +{@link android.app.Activity#onActivityResult onActivityResult()}。指向所選文件的 URI 包含在 {@code resultData} 參數中。 + +請使用 {@link android.content.Intent#getData getData()} 擷取 URI,然後使用該 URI 擷取使用者所需的文件。 +例如: +

      + +
      @Override
      +public void onActivityResult(int requestCode, int resultCode,
      +        Intent resultData) {
      +
      +    // The ACTION_OPEN_DOCUMENT intent was sent with the request code
      +    // READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
      +    // response to some other intent, and the code below shouldn't run at all.
      +
      +    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
      +        // The document selected by the user won't be returned in the intent.
      +        // Instead, a URI to that document will be contained in the return intent
      +        // provided to this method as a parameter.
      +        // Pull that URI using resultData.getData().
      +        Uri uri = null;
      +        if (resultData != null) {
      +            uri = resultData.getData();
      +            Log.i(TAG, "Uri: " + uri.toString());
      +            showImage(uri);
      +        }
      +    }
      +}
      +
      + +

      檢查文件中繼資料

      + +

      取得文件的 URI 後,您就可以存取該文件的中繼資料。以下程式碼片段會擷取 URI 所指定文件的中繼資料並且加以記錄: +

      + +
      public void dumpImageMetaData(Uri uri) {
      +
      +    // The query, since it only applies to a single document, will only return
      +    // one row. There's no need to filter, sort, or select fields, since we want
      +    // all fields for one document.
      +    Cursor cursor = getActivity().getContentResolver()
      +            .query(uri, null, null, null, null, null);
      +
      +    try {
      +    // moveToFirst() returns false if the cursor has 0 rows.  Very handy for
      +    // "if there's anything to look at, look at it" conditionals.
      +        if (cursor != null && cursor.moveToFirst()) {
      +
      +            // Note it's called "Display Name".  This is
      +            // provider-specific, and might not necessarily be the file name.
      +            String displayName = cursor.getString(
      +                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
      +            Log.i(TAG, "Display Name: " + displayName);
      +
      +            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
      +            // If the size is unknown, the value stored is null.  But since an
      +            // int can't be null in Java, the behavior is implementation-specific,
      +            // which is just a fancy term for "unpredictable".  So as
      +            // a rule, check if it's null before assigning to an int.  This will
      +            // happen often:  The storage API allows for remote files, whose
      +            // size might not be locally known.
      +            String size = null;
      +            if (!cursor.isNull(sizeIndex)) {
      +                // Technically the column stores an int, but cursor.getString()
      +                // will do the conversion automatically.
      +                size = cursor.getString(sizeIndex);
      +            } else {
      +                size = "Unknown";
      +            }
      +            Log.i(TAG, "Size: " + size);
      +        }
      +    } finally {
      +        cursor.close();
      +    }
      +}
      +
      + +

      開啟文件

      + +

      取得文件的 URI 後,您就可以開啟該文件或是對該文件執行任何所需操作。 +

      + +

      點陣圖

      + +

      以下範例可開啟 {@link android.graphics.Bitmap}:

      + +
      private Bitmap getBitmapFromUri(Uri uri) throws IOException {
      +    ParcelFileDescriptor parcelFileDescriptor =
      +            getContentResolver().openFileDescriptor(uri, "r");
      +    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
      +    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
      +    parcelFileDescriptor.close();
      +    return image;
      +}
      +
      + +

      請注意,請不要針對 UI 執行緒進行這項作業,請在背景中使用 {@link android.os.AsyncTask} 進行。 +開啟點陣圖後,您就可以在 {@link android.widget.ImageView} 中顯示該點陣圖。 + +

      + +

      取得 InputStream

      + +

      以下範例可從 URI 中取得 {@link java.io.InputStream}。在這個程式碼片段中,系統會將每行檔案解讀為單一字串: +

      + +
      private String readTextFromUri(Uri uri) throws IOException {
      +    InputStream inputStream = getContentResolver().openInputStream(uri);
      +    BufferedReader reader = new BufferedReader(new InputStreamReader(
      +            inputStream));
      +    StringBuilder stringBuilder = new StringBuilder();
      +    String line;
      +    while ((line = reader.readLine()) != null) {
      +        stringBuilder.append(line);
      +    }
      +    fileInputStream.close();
      +    parcelFileDescriptor.close();
      +    return stringBuilder.toString();
      +}
      +
      + +

      建立新文件

      + +

      應用程式可在文件供應程式中使用 +{@link android.content.Intent#ACTION_CREATE_DOCUMENT} 意圖建立新文件。 +如要建立新檔案,請將 MIME 類型和檔案名稱提供給意圖,然後使用專屬的要求程式碼執行該意圖。 +系統會為您完成其餘的作業:

      + + +
      +// Here are some examples of how you might call this method.
      +// The first parameter is the MIME type, and the second parameter is the name
      +// of the file you are creating:
      +//
      +// createFile("text/plain", "foobar.txt");
      +// createFile("image/png", "mypicture.png");
      +
      +// Unique request code.
      +private static final int WRITE_REQUEST_CODE = 43;
      +...
      +private void createFile(String mimeType, String fileName) {
      +    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
      +
      +    // Filter to only show results that can be "opened", such as
      +    // a file (as opposed to a list of contacts or timezones).
      +    intent.addCategory(Intent.CATEGORY_OPENABLE);
      +
      +    // Create a file with the requested MIME type.
      +    intent.setType(mimeType);
      +    intent.putExtra(Intent.EXTRA_TITLE, fileName);
      +    startActivityForResult(intent, WRITE_REQUEST_CODE);
      +}
      +
      + +

      建立新文件後,您可在 +{@link android.app.Activity#onActivityResult onActivityResult()} 中取得該文件的 URI, +以便繼續在其中編寫程式碼。

      + +

      刪除文件

      + +

      如果您已取得文件的 URI,而且文件的 +{@link android.provider.DocumentsContract.Document#COLUMN_FLAGS Document.COLUMN_FLAGS} 含有 +{@link android.provider.DocumentsContract.Document#FLAG_SUPPORTS_DELETE SUPPORTS_DELETE},您就可以刪除該文件。 + +例如:

      + +
      +DocumentsContract.deleteDocument(getContentResolver(), uri);
      +
      + +

      編輯文件

      + +

      您可以使用 SAF 即時編輯文字文件。以下程式碼片段會觸發 {@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖並使用 {@link android.content.Intent#CATEGORY_OPENABLE} 類別限制系統只顯示可開啟的文件。 + + + +此外,這個程式碼還會進一步篩選搜尋結果,讓系統只顯示文字檔:

      + +
      +private static final int EDIT_REQUEST_CODE = 44;
      +/**
      + * Open a file for writing and append some text to it.
      + */
      + private void editDocument() {
      +    // ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's
      +    // file browser.
      +    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
      +
      +    // Filter to only show results that can be "opened", such as a
      +    // file (as opposed to a list of contacts or timezones).
      +    intent.addCategory(Intent.CATEGORY_OPENABLE);
      +
      +    // Filter to show only text files.
      +    intent.setType("text/plain");
      +
      +    startActivityForResult(intent, EDIT_REQUEST_CODE);
      +}
      +
      + +

      接著,您可以利用 {@link android.app.Activity#onActivityResult onActivityResult()} (詳情請參閱處理結果) 呼叫程式碼執行編輯動作。以下程式碼片段會利用 {@link android.content.ContentResolver} 取得 {@link java.io.FileOutputStream}。 + + +在預設情況下,這個程式碼片段會使用「寫入」模式。這種方法可索取最少量的所需存取權,因此如果您只需要寫入存取權,請勿要求讀取/寫入: + +

      + +
      private void alterDocument(Uri uri) {
      +    try {
      +        ParcelFileDescriptor pfd = getActivity().getContentResolver().
      +                openFileDescriptor(uri, "w");
      +        FileOutputStream fileOutputStream =
      +                new FileOutputStream(pfd.getFileDescriptor());
      +        fileOutputStream.write(("Overwritten by MyCloud at " +
      +                System.currentTimeMillis() + "\n").getBytes());
      +        // Let the document provider know you're done by closing the stream.
      +        fileOutputStream.close();
      +        pfd.close();
      +    } catch (FileNotFoundException e) {
      +        e.printStackTrace();
      +    } catch (IOException e) {
      +        e.printStackTrace();
      +    }
      +}
      + +

      保留權限

      + +

      應用程式開啟要讀取或寫入的檔案後,系統會將該檔案的 URI 權限授予您的應用程式。 +除非使用者重新啟動裝置,否則這項權限會持續保持有效狀態。不過,假如您的應用程式為圖片編輯應用程式,而您希望使用者可直接透過您的應用程式存取他們最近編輯的 5 張圖片。如果使用者重新啟動的裝置,就您必須將使用者傳回系統挑選器來搜尋所需檔案,而這並非最佳做法。 + + + +

      + +

      為了避免這種情況發生,您可以保留系統授予您應用程式的權限。實際上,您的應用程式會「取得」系統授予的永久性 URI 權限。 + +這種權限可讓使用者持續透過您的應用程式存取檔案,即使其裝置重新啟動也無妨: +

      + + +
      final int takeFlags = intent.getFlags()
      +            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
      +            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      +// Check for the freshest data.
      +getContentResolver().takePersistableUriPermission(uri, takeFlags);
      + +

      除了上述指示外,您還需要完成最後一個步驟。您儲存了您的應用程式最近存取的 URI,但這些 URI 有可能已失效 — 原因在於其他應用程式刪除或修改了文件。 + +因此,建議您一律呼叫 +{@code getContentResolver().takePersistableUriPermission()} 檢查最新資料。 +

      + +

      編寫自訂文件供應程式

      + +

      +如果您想開發可提供檔案儲存服務 (例如雲端儲存服務) 的應用程式,可以編寫自訂文件供應程式透過 SAF 提供您的檔案。 + +本節說明如何編寫這類程式。 +

      + + +

      宣示說明

      + +

      如要實作自訂文件供應程式,請將以下項目加入應用程式的宣示說明: +

      +
        + +
      • 19 以上的 API 級別目標。
      • + +
      • 宣告自訂儲存空間供應程式的 +<provider> 元素。
      • + +
      • 供應程式的名稱 (也就是供應程式的類別名稱),包括套件名稱。範例:com.example.android.storageprovider.MyCloudProvider。 +
      • + +
      • 授權的名稱 (也就是套件的名稱;在此範例中為 +com.example.android.storageprovider) 以及內容供應程式的類型 +(documents)。範例:{@code com.example.android.storageprovider.documents}。
      • + +
      • 設為 "true"android:exported 屬性。您必須將供應程式匯出,方便其他應用程式加以偵測。 +
      • + +
      • 設為 "true" 的 +android:grantUriPermissions 屬性。這項設定可讓系統將供應程式內容的存取權授予其他應用程式。 +如果想瞭解如何保留特定文件的權限,請參閱保留權限。 +
      • + +
      • {@code MANAGE_DOCUMENTS} 權限。在預設情況下,所有人都可使用供應程式。 +加入這項權限可針對系統設定供應程式限制,藉此提高其安全性。 +
      • + +
      • 設定資源檔案所定義布林值的 {@code android:enabled} 屬性。 +這項屬性可用於針對搭載 Android 4.3 以下版本的裝置停用供應程式。範例:{@code android:enabled="@bool/atLeastKitKat"}。 +除了在宣示說明中加入這項屬性以外,您還必須執行下列操作: + +
          +
        • 在位於 {@code res/values/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: +
          <bool name="atLeastKitKat">false</bool>
        • + +
        • 在位於 {@code res/values-v19/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: +
          <bool name="atLeastKitKat">true</bool>
        • +
      • + +
      • 內含 +{@code android.content.action.DOCUMENTS_PROVIDER} 動作的意圖篩選器,讓您的供應程式能夠在系統搜尋供應程式時顯示在挑選器中。 +
      • + +
      +

      以下是內含供應程式的範例宣示說明例外狀況:

      + +
      <manifest... >
      +    ...
      +    <uses-sdk
      +        android:minSdkVersion="19"
      +        android:targetSdkVersion="19" />
      +        ....
      +        <provider
      +            android:name="com.example.android.storageprovider.MyCloudProvider"
      +            android:authorities="com.example.android.storageprovider.documents"
      +            android:grantUriPermissions="true"
      +            android:exported="true"
      +            android:permission="android.permission.MANAGE_DOCUMENTS"
      +            android:enabled="@bool/atLeastKitKat">
      +            <intent-filter>
      +                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
      +            </intent-filter>
      +        </provider>
      +    </application>
      +
      +</manifest>
      + +

      支援搭載 Android 4.3 以下版本的裝置

      + +

      只有搭載 Android 4.4 以上版本的裝置可使用 +{@link android.content.Intent#ACTION_OPEN_DOCUMENT} 意圖。如果您想讓應用程式支援 {@link android.content.Intent#ACTION_GET_CONTENT} 以便與搭載 Android 4.3 以下版本的裝置相容,請針對搭載 Android 4.4 以上版本的裝置停用宣示說明中的 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器。 + + + + +文件供應器和 {@link android.content.Intent#ACTION_GET_CONTENT} 是完全不同的項目。 + +如果您同時支援這兩個項目,您的應用程式就會重複出現在系統挑選器 UI 中,讓使用者可透過兩種不同方式存取您儲存的資料, + +而這樣會造成混淆。

      + +

      以下提供針對搭載 Android 4.4 以上版本的裝置停用 +{@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器的建議做法: +

      + +
        +
      1. 在位於 {@code res/values/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: +
        <bool name="atMostJellyBeanMR2">true</bool>
      2. + +
      3. 在位於 {@code res/values-v19/} 的 {@code bool.xml} 資源檔案中,新增以下程式碼: +
        <bool name="atMostJellyBeanMR2">false</bool>
      4. + +
      5. 新增 +Activity 別名來針對搭載 Android 4.4 (API 級別 19) 以上版本的裝置停用 {@link android.content.Intent#ACTION_GET_CONTENT} 意圖篩選器。 + +例如: + +
        +<!-- This activity alias is added so that GET_CONTENT intent-filter
        +     can be disabled for builds on API level 19 and higher. -->
        +<activity-alias android:name="com.android.example.app.MyPicker"
        +        android:targetActivity="com.android.example.app.MyActivity"
        +        ...
        +        android:enabled="@bool/atMostJellyBeanMR2">
        +    <intent-filter>
        +        <action android:name="android.intent.action.GET_CONTENT" />
        +        <category android:name="android.intent.category.OPENABLE" />
        +        <category android:name="android.intent.category.DEFAULT" />
        +        <data android:mimeType="image/*" />
        +        <data android:mimeType="video/*" />
        +    </intent-filter>
        +</activity-alias>
        +
        +
      6. +
      +

      合約

      + +

      一般來說,當您編寫自訂內容供應程式時,需要完成的其中一項工作為實作合約類別 (詳情請參閱內容供應程式開發人員指南)。 + + +合約類別是 {@code public final} 類別,內含以下項目的固定不變定義:URI、欄名稱、MIME 類型以及供應程式擁有的其他中繼資料。 + +SAF 可為您提供以下合約類別,因此您不必自行編 寫合約: + +

      + +
        +
      • {@link android.provider.DocumentsContract.Document}
      • +
      • {@link android.provider.DocumentsContract.Root}
      • +
      + +

      例如,以下是在文件供應程式查詢文件或根目錄時可能會傳回的資料欄: +

      + +
      private static final String[] DEFAULT_ROOT_PROJECTION =
      +        new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES,
      +        Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
      +        Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID,
      +        Root.COLUMN_AVAILABLE_BYTES,};
      +private static final String[] DEFAULT_DOCUMENT_PROJECTION = new
      +        String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
      +        Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
      +        Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
      +
      + +

      將 DocumentsProvider 設為子類別

      + +

      編寫自動文件供應程式的下一個步驟是,將抽象類別 {@link android.provider.DocumentsProvider} 設為子類別。 +您至少必須實作下列方法: +

      + +
        +
      • {@link android.provider.DocumentsProvider#queryRoots queryRoots()}
      • + +
      • {@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()}
      • + +
      • {@link android.provider.DocumentsProvider#queryDocument queryDocument()}
      • + +
      • {@link android.provider.DocumentsProvider#openDocument openDocument()}
      • +
      + +

      以上是您必須實作的方法,不過您可能會視需要實作其他方法。 +詳情請參閱 {@link android.provider.DocumentsProvider}。 +

      + +

      實作 queryRoots

      + +

      實作 {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 後系統會使用 {@link android.provider.DocumentsContract.Root} 中定義的資料欄,傳回指向文件供應程式所有根目錄的 {@link android.database.Cursor}。 + +

      + +

      在以下程式碼片段中,{@code projection} 參數代表呼叫者想返回的特定欄位。 +這個程式碼片隊會建立新游標並在其中加入一列 — 也就是根目錄或頂層目錄 (例如「下載」或「圖片」)。 + +大多數供應程式只有一個根目錄。而您可以有多個根目錄,例如擁有多個使用者帳戶的情況下。 +在這種情況下,只要在游標中加入第二列即可。 +

      + +
      +@Override
      +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
      +
      +    // Create a cursor with either the requested fields, or the default
      +    // projection if "projection" is null.
      +    final MatrixCursor result =
      +            new MatrixCursor(resolveRootProjection(projection));
      +
      +    // If user is not logged in, return an empty root cursor.  This removes our
      +    // provider from the list entirely.
      +    if (!isUserLoggedIn()) {
      +        return result;
      +    }
      +
      +    // It's possible to have multiple roots (e.g. for multiple accounts in the
      +    // same app) -- just add multiple cursor rows.
      +    // Construct one row for a root called "MyCloud".
      +    final MatrixCursor.RowBuilder row = result.newRow();
      +    row.add(Root.COLUMN_ROOT_ID, ROOT);
      +    row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
      +
      +    // FLAG_SUPPORTS_CREATE means at least one directory under the root supports
      +    // creating documents. FLAG_SUPPORTS_RECENTS means your application's most
      +    // recently used documents will show up in the "Recents" category.
      +    // FLAG_SUPPORTS_SEARCH allows users to search all documents the application
      +    // shares.
      +    row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
      +            Root.FLAG_SUPPORTS_RECENTS |
      +            Root.FLAG_SUPPORTS_SEARCH);
      +
      +    // COLUMN_TITLE is the root title (e.g. Gallery, Drive).
      +    row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title));
      +
      +    // This document id cannot change once it's shared.
      +    row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
      +
      +    // The child MIME types are used to filter the roots and only present to the
      +    //  user roots that contain the desired type somewhere in their file hierarchy.
      +    row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
      +    row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
      +    row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
      +
      +    return result;
      +}
      + +

      實作 queryChildDocuments

      + +

      實作 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 後系統會使用 +{@link android.provider.DocumentsContract.Document} 中定義的資料欄,傳回指向特定目錄中所有檔案的 {@link android.database.Cursor}。 + +

      + +

      當您在挑選器 UI 中選擇應用程式的根目錄後,就會呼叫這個方法,藉此取得根目錄內某個目錄中的下層文件。 +您可以在檔案階層的任何層級中呼叫這個方法,而不單單只能在根目錄中呼叫。 +以下程式碼片段會使用要求的資料欄建立新游標,然後加入該游標中上層目錄的任何下層物件相關資訊。下層物件可以是圖片、其他目錄等任何檔案: + + +

      + +
      @Override
      +public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
      +                              String sortOrder) throws FileNotFoundException {
      +
      +    final MatrixCursor result = new
      +            MatrixCursor(resolveDocumentProjection(projection));
      +    final File parent = getFileForDocId(parentDocumentId);
      +    for (File file : parent.listFiles()) {
      +        // Adds the file's display name, MIME type, size, and so on.
      +        includeFile(result, null, file);
      +    }
      +    return result;
      +}
      +
      + +

      實作 queryDocument

      + +

      實作 +{@link android.provider.DocumentsProvider#queryDocument queryDocument()} 後系統會使用 {@link android.provider.DocumentsContract.Document} 中定義的資料欄,傳回指向特定檔案的 {@link android.database.Cursor}。 + + +

      + +

      {@link android.provider.DocumentsProvider#queryDocument queryDocument()}方法會針對特定檔案傳回 +{@link android.provider.DocumentsProvider#queryChildDocuments queryChildDocuments()} 所傳送的相同資訊: + +

      + + +
      @Override
      +public Cursor queryDocument(String documentId, String[] projection) throws
      +        FileNotFoundException {
      +
      +    // Create a cursor with the requested projection, or the default projection.
      +    final MatrixCursor result = new
      +            MatrixCursor(resolveDocumentProjection(projection));
      +    includeFile(result, documentId, null);
      +    return result;
      +}
      +
      + +

      實作 openDocument

      + +

      您必須實作 {@link android.provider.DocumentsProvider#openDocument +openDocument()} 來傳回代表特定檔案的 +{@link android.os.ParcelFileDescriptor}。其他應用程式可利用傳回的 {@link android.os.ParcelFileDescriptor} 傳輸資料。 +使用者選取檔案而且用戶端應用程式呼叫 +{@link android.content.ContentResolver#openFileDescriptor openFileDescriptor()} 要求存取該檔案後,系統就會呼叫這個方法。範例: + +

      + +
      @Override
      +public ParcelFileDescriptor openDocument(final String documentId,
      +                                         final String mode,
      +                                         CancellationSignal signal) throws
      +        FileNotFoundException {
      +    Log.v(TAG, "openDocument, mode: " + mode);
      +    // It's OK to do network operations in this method to download the document,
      +    // as long as you periodically check the CancellationSignal. If you have an
      +    // extremely large file to transfer from the network, a better solution may
      +    // be pipes or sockets (see ParcelFileDescriptor for helper methods).
      +
      +    final File file = getFileForDocId(documentId);
      +
      +    final boolean isWrite = (mode.indexOf('w') != -1);
      +    if(isWrite) {
      +        // Attach a close listener if the document is opened in write mode.
      +        try {
      +            Handler handler = new Handler(getContext().getMainLooper());
      +            return ParcelFileDescriptor.open(file, accessMode, handler,
      +                        new ParcelFileDescriptor.OnCloseListener() {
      +                @Override
      +                public void onClose(IOException e) {
      +
      +                    // Update the file with the cloud server. The client is done
      +                    // writing.
      +                    Log.i(TAG, "A file with id " +
      +                    documentId + " has been closed!
      +                    Time to " +
      +                    "update the server.");
      +                }
      +
      +            });
      +        } catch (IOException e) {
      +            throw new FileNotFoundException("Failed to open document with id "
      +            + documentId + " and mode " + mode);
      +        }
      +    } else {
      +        return ParcelFileDescriptor.open(file, accessMode);
      +    }
      +}
      +
      + +

      安全性

      + +

      假如您的文件供應程式為受密碼保護的雲端儲存服務,而您先想確認使用者都已登入,然後再開始分享其檔案。那麼在使用者未登入的情況下,您的應用程式應採取什麼行動? + +解決方案是不要讓文件供應程式在您實作 {@link android.provider.DocumentsProvider#queryRoots +queryRoots()} 後傳回任何根目錄。 +換句話說,就是讓供應程式傳回空的根目錄游標:

      + +
      +public Cursor queryRoots(String[] projection) throws FileNotFoundException {
      +...
      +    // If user is not logged in, return an empty root cursor.  This removes our
      +    // provider from the list entirely.
      +    if (!isUserLoggedIn()) {
      +        return result;
      +}
      +
      + +

      另一個需採取的步驟是呼叫 {@code getContentResolver().notifyChange()}。還記得 {@link android.provider.DocumentsContract} 嗎? +我們會使用該類別建立 URI。以下程式碼片段會指示系統在使用者的登入狀態變更時,查詢文件供應程式的根目錄。 + +如果使用者未登入,呼叫 {@link android.provider.DocumentsProvider#queryRoots queryRoots()} 就會如上所述傳回空的游標。 + +這樣可確保只有登入供應程式的使用者可存取其中的文件。 +

      + +
      private void onLoginButtonClick() {
      +    loginOrLogout();
      +    getContentResolver().notifyChange(DocumentsContract
      +            .buildRootsUri(AUTHORITY), null);
      +}
      +
      \ No newline at end of file diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..3a5a96121d942f5fc6f803327b4ec0bd22de8c0a --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/accessing-resources.jd @@ -0,0 +1,337 @@ +page.title=存取資源 +parent.title=應用程式資源 +parent.link=index.html +@jd:body + +
      +
      +

      快速檢視

      +
        +
      • 使用 {@code R.java} 中的整數可以參照程式碼中的資源,例如 +{@code R.drawable.myimage}
      • +
      • 使用特殊 XML 語法可以參照資源中的資源,例如 {@code +@drawable/myimage}
      • +
      • 您也可以使用 +{@link android.content.res.Resources} 中的方法存取應用程式資源
      • +
      + +

      重要類別

      +
        +
      1. {@link android.content.res.Resources}
      2. +
      + +

      本文件內容

      +
        +
      1. 存取程式碼中的資源
      2. +
      3. 存取 XML 中的資源 +
          +
        1. 參照樣式屬性
        2. +
        +
      4. +
      5. 存取平台資源
      6. +
      + +

      另請參閱

      +
        +
      1. 提供資源
      2. +
      3. 資源類型
      4. +
      +
      +
      + + + + +

      提供應用程式中的資源 (於提供資源中討論) 後,您可以參照其資源 ID 加以應用。所有資源 ID 會在專案的 {@code R} 類別中定義。此類別是由 {@code aapt} 工具自動產生的。 + +

      + +

      編譯應用程式後,{@code aapt} 會產生 {@code R} 類別。此類別內含 {@code +res/} 目錄中所有資源的資源 ID。 +每一種資源類型都有 {@code R} 子類別 (例如,所有可繪項目資源的 +{@code R.drawable}),而這種類型的每一種資源都有靜態整數 (例如,{@code R.drawable.icon})。 +此整數就是資源 ID,您可以用於擷取資源。 +

      + +

      儘管資源 ID 都指定於 {@code R} 類別,您不需要為了取得資源 ID 而加以查看。資源 ID 的組成如下: +

      +
        +
      • 資源類型:每一種資源會以「類型」分類,例如 {@code +string}、{@code drawable} 以及 {@code layout}。如需有關不同類型的詳細資訊,請參閱資源類型。 +
      • +
      • 資源名稱可能是:檔案名稱,不含副檔名;或 XML {@code android:name} 屬性中的值,如果資源是簡單值 (例如字串)。 + +
      • +
      + +

      您有兩種方式可存取資源:

      +
        +
      • 在程式碼中:使用 {@code R} 類別中子類別的靜態整數,例如: + +
        R.string.hello
        +

        {@code string} 是資源類型,而 {@code hello} 是資源名稱。若您以此格式提供資源 ID 時,很多 Android API 都可以存取您的資源。 +請參閱在程式碼中存取資源。 +

        +
      • +
      • 在 XML 中:使用特殊 XML 語法,也可以對應到您在 {@code R} 類別中定義的資源 ID,例如: + +
        @string/hello
        +

        {@code string} 是資源類型,而 {@code hello} 是資源名稱。您可以在 XML 資源中的任何位置使用此語法,只要符合您在資源中所提供的預期值即可。 +請參閱存取 XML 中的資源

        +
      • +
      + + + +

      在程式碼中存取資源

      + +

      您可以將資源 ID 作為方法參數傳遞,在程式碼中使用資源。例如,您可以利用 {@link android.widget.ImageView#setImageResource(int) setImageResource()} 將 {@link android.widget.ImageView} 設定為使用 {@code res/drawable/myimage.png} 資源: + +

      +
      +ImageView imageView = (ImageView) findViewById(R.id.myimageview);
      +imageView.setImageResource(R.drawable.myimage);
      +
      + +

      您也可以使用 {@link +android.content.res.Resources} (以 {@link android.content.Context#getResources()} 可取得執行個體) 中的方法擷取個別的資源。 +

      + + + + +

      語法

      + +

      在程式碼中參照資源的語法如下:

      + +
      +[<package_name>.]R.<resource_type>.<resource_name>
      +
      + +
        +
      • {@code <package_name>} 資源所在的封裝名稱 (從您自己的封裝中參照資源時,不需要此名稱)。 +
      • +
      • {@code <resource_type>} 是資源類型的 {@code R} 子類別。
      • +
      • {@code <resource_name>} 可以是不含副檔名的資源檔案名稱,或是 XML 元素中的 {@code android:name} 屬性值 (簡單值)。 + +
      • +
      +

      請參閱資源類型,以取得關於每個資源類型及其參照方式的詳細資訊。 +

      + + +

      使用案例

      + +

      很多方法都接受資源 ID 參數,您可以使用 +{@link android.content.res.Resources} 中的方法擷取資源。您可以透過以下方式取得 {@link +android.content.res.Resources} 的執行個體:{@link android.content.Context#getResources +Context.getResources()}。

      + + +

      以下是在程式碼中存取資源的範例:

      + +
      +// Load a background for the current screen from a drawable resource
      +{@link android.app.Activity#getWindow()}.{@link
      +android.view.Window#setBackgroundDrawableResource(int)
      +setBackgroundDrawableResource}(R.drawable.my_background_image) ;
      +
      +// Set the Activity title by getting a string from the Resources object, because
      +//  this method requires a CharSequence rather than a resource ID
      +{@link android.app.Activity#getWindow()}.{@link android.view.Window#setTitle(CharSequence)
      +setTitle}(getResources().{@link android.content.res.Resources#getText(int)
      +getText}(R.string.main_title));
      +
      +// Load a custom layout for the current screen
      +{@link android.app.Activity#setContentView(int)
      +setContentView}(R.layout.main_screen);
      +
      +// Set a slide in animation by getting an Animation from the Resources object
      +mFlipper.{@link android.widget.ViewAnimator#setInAnimation(Animation)
      +setInAnimation}(AnimationUtils.loadAnimation(this,
      +        R.anim.hyperspace_in));
      +
      +// Set the text on a TextView object using a resource ID
      +TextView msgTextView = (TextView) findViewById(R.id.msg);
      +msgTextView.{@link android.widget.TextView#setText(int)
      +setText}(R.string.hello_message);
      +
      + + +

      注意:您不應手動修改 {@code +R.java} 檔案 — 此檔案是在編譯專案時由 {@code aapt} 工具所產生。 +下次編譯時會覆寫所有變更內容。

      + + + +

      存取 XML 中的資源

      + +

      您可以使用現有資源的參照,為某些 XML 屬性和元素定義值。 +您在建立版面配置檔案時,會經常以此方式提供字串和影像讓小工具使用。 +

      + +

      例如,如果您要將 {@link android.widget.Button} 加入版面配置,應該使用按鈕文字的字串資源: +

      + +
      +<Button
      +    android:layout_width="fill_parent"
      +    android:layout_height="wrap_content"
      +    android:text="@string/submit" />
      +
      + + +

      語法

      + +

      在 XML 資源中參照資源的語法如下:

      + +
      +@[<package_name>:]<resource_type>/<resource_name>
      +
      + +
        +
      • {@code <package_name>} 資源所在的封裝名稱 (從同一個封裝中參照資源時,不需要此名稱) +
      • +
      • {@code <resource_type>} 是資源類型的 +{@code R} 子類別。
      • +
      • {@code <resource_name>} 可以是不含副檔名的資源檔案名稱,或是 XML 元素中的 {@code android:name} 屬性值 (簡單值)。 + +
      • +
      + +

      請參閱資源類型,以取得關於每個資源類型及其參照方式的詳細資訊。 +

      + + +

      使用案例

      + +

      有時候您必須在 XML 中使用資源,而不能使用值 (例如,將可繪項目影像套用至小工具)。不過,您也可以在 XML 中接受簡單值的任何位置使用資源。 +例如,如果您有下列資源檔案,其中內含色彩資源字串資源: +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<resources>
      +   <color name="opaque_red">#f00</color>
      +   <string name="hello">Hello!</string>
      +</resources>
      +
      + +

      您可以在下列版面配置檔案中使用這些資源,以設定文字色彩和文字字串: +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:layout_width="fill_parent"
      +    android:layout_height="fill_parent"
      +    android:textColor="@color/opaque_red"
      +    android:text="@string/hello" />
      +
      + +

      在此情況下,您不用在資源參照中指定封裝名稱,因為資源是來自您自己的封裝。 +如要參照系統資源,則需要使用封裝名稱。 +範例:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<EditText xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:layout_width="fill_parent"
      +    android:layout_height="fill_parent"
      +    android:textColor="@android:color/secondary_text_dark"
      +    android:text="@string/hello" />
      +
      + +

      注意:您一律都要使用字串資源,便於應用程式當地語系化。 +如需關於建立替代資源 (例如當地語系化的字串) 的詳細資訊,請參閱提供替代資源。 + + +如需將應用程式當地語系化的詳細指南,請參閱當地語系化。 +

      + +

      您甚至可以在 XML 中使用資源以建立別名。例如:您可以用其他可繪項目資源的別名,建立可繪項目資源: +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:src="@drawable/other_drawable" />
      +
      + +

      聽起來多此一舉,但是,當您使用替代資源時,就非常實用。深入瞭解建立別名資源。 +

      + + + +

      參照樣式屬性

      + +

      樣式屬性資源可以讓您在目前套用的設計風格中參照屬性的值。 +參照樣式屬性可以讓您透過目前設計風格提供的標準變化自訂 UI 元素外觀,而不需要使用硬式編碼值。 + +參照樣式屬性基本上就是「使用目前設計風格中此屬性所定義的樣式」。 +

      + +

      如要參照樣式屬性,名稱語法幾乎和一般資源格式相同,只要把小老鼠符號 ({@code @}) 換成問號 ({@code ?}) 即可,而且您可以選擇是否要使用資源類型。 + +範例:

      + +
      +?[<package_name>:][<resource_type>/]<resource_name>
      +
      + +

      例如,您可以參照屬性,讓文字色彩符合系統設計風格的「主要」文字色彩,方式如下: +

      + +
      +<EditText id="text"
      +    android:layout_width="fill_parent"
      +    android:layout_height="wrap_content"
      +    android:textColor="?android:textColorSecondary"
      +    android:text="@string/hello_world" />
      +
      + +

      這裡的 {@code android:textColor} 屬性指出目前設計風格中樣式屬性的名稱。 +Android 現在會將套用至 {@code android:textColorSecondary} 樣式屬性的值,當成此小工具中 {@code android:textColor} 的值。 +因為系統資源工具知道在此環境中會有一個屬性資源,您不需要明確陳述其類型 ( +?android:attr/textColorSecondary)— 可以不需要寫上出 {@code attr} 類型。 + +

      + + + + +

      存取平台資源

      + +

      Android 內含多種標準資源,例如樣式、設計風格以及版面配置。如要存取這些資源,則必須在參照資源時使用 +android 封裝名稱。 +例如,Android 在 {@link android.widget.ListAdapter} 中提供版面配置資源,您可以用於列出項目: +

      + +
      +{@link android.app.ListActivity#setListAdapter(ListAdapter)
      +setListAdapter}(new {@link
      +android.widget.ArrayAdapter}<String>(this, android.R.layout.simple_list_item_1, myarray));
      +
      + +

      在此範例中,{@link android.R.layout#simple_list_item_1} 是一個版面配置資源,由平台為 {@link android.widget.ListView} 中的項目所定義。 +您可以加以使用,而不用自行建立清單項目的版面配置。 +如需詳細資訊,請參閱清單檢視開發人員指南。 +

      + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..68197d17233a1d2a8eb97483c38d84cb5fa385f9 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/overview.jd @@ -0,0 +1,103 @@ +page.title=資源總覽 +@jd:body + +
      +
      +

      本文主題

      +
        +
      1. 提供資源
      2. +
      3. 存取資源
      4. +
      5. 處理執行階段變更
      6. +
      7. 本地化
      8. +
      + +

      參考資料

      +
        +
      1. 資源類型
      2. +
      +
      +
      + + +

      建議您一律將應用程式程式碼當中像是圖片和字串的資源具體化,以便個別加以維護。 +將資源具體化也可讓您提供支援特定裝置設定的替代資源,例如不同語言或螢幕大小,隨著提供不同設定的 Android 裝置日漸增加,這點也變得日益重要。 + + +為了提供不同設定的相容性,您必須整理專案的 {@code res/} 目錄中的資源,使用各種子目錄按類型與設定將資源分門別類。 + + +

      + +
      + +

      +圖 1.兩個不同的裝置,個別使用預設的版面配置 (應用程式未提供替代的版面配置)。 +

      +
      + +
      + +

      +圖 2.兩個不同的裝置,個別使用不同螢幕大小所提供的不同版面配置。 +

      +
      + +

      針對任何資源類型,您都能為應用程式指定「預設」與多項「替代」資源。 +

      +
        +
      • 不論裝置設定為何,或在沒有符合目前設定的替代資源時,您都應該使用預設資源。 + +
      • +
      • 您設計用來與特定設定搭配使用的就是替代資源。 +如要為某個設定指定一組資源,請將適當的設定限定詞附加到目錄名稱。 +
      • +
      + +

      例如,假設您的預設 UI 版面配置儲存在 {@code res/layout/} 目錄,您可能要指定一個不同的版面配置並儲存在 +{@code res/layout-land/} 目錄中,以在螢幕處於橫向時使用。 + +Android 會在比對裝置目前的設定與您的資源目錄名稱後,自動套用適當的資源。 +

      + +

      圖 1 說明系統如何在沒有替代資源可用時,對兩個不同裝置套用相同的版面配置。 +圖 2 說明相同的應用程式為較大的螢幕新增替代版面配置資源的情況。 +

      + +

      下文提供完整的說明,指引您如何整理應用程式資源、指定替代資源、在您的應用程式中存取這些資源等等: +

      + +
      +
      提供資源
      +
      您可在應用程式中加入哪些資源類型,儲存在哪裡以及如何針對不同的裝置設定建立替代資源。 +
      +
      存取資源
      +
      如何使用您提供的資源 (在應用程式的程式碼中或在其他 XML 資源中參考)。 +
      +
      處理執行階段變更
      +
      如何管理在 Activity 執行期間發生的設定變更。
      +
      本地化
      +
      從細節到整體的說明,指引您使用替代資源將應用程式本地化。雖然這只是替代資源的一種特殊用途,但為了觸及更多使用者,這樣做非常重要。 + +
      +
      資源類型
      +
      您能提供的各種資源類型參考資料,描述其 XML 元件、屬性及語法。 +例如,此參考資料說明如何建立應用程式選單、可繪項目、動畫等資源。 +
      +
      + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd new file mode 100644 index 0000000000000000000000000000000000000000..0938dc00e12db6aeb601ae646650da8408921ccc --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/providing-resources.jd @@ -0,0 +1,1094 @@ +page.title=提供資源 +parent.title=應用程式資源 +parent.link=index.html +@jd:body + +
      +
      +

      快速檢視

      +
        +
      • 不同類型的資源屬於 {@code res/} 中不同的子目錄
      • +
      • 替代資源提供特定設定資源檔案
      • +
      • 始終要包含預設資源,如此應用程式才不會依賴特定裝置設定 +
      • +
      +

      本文件內容

      +
        +
      1. 分組資源類型
      2. +
      3. 提供替代資源 +
          +
        1. 限定詞名稱規則
        2. +
        3. 建立別名資源
        4. +
        +
      4. +
      5. 使用資源提供最佳的裝置相容性
      6. +
      7. Android 如何尋找最相符的資源
      8. +
      + +

      另請參閱

      +
        +
      1. 存取資源
      2. +
      3. 資源類型
      4. +
      5. 支援多個螢幕 +
      6. +
      +
      +
      + +

      您應該一律將應用程式資源具體化,例如影像和程式碼字串,以便單獨維護它們。 +同時,您還應該將替代資源分組到特殊命名的資源目錄中,為特定裝置設定提供替代資源。 +Android 會在執行階段根據目前的設定使用適當的資源。 +例如,您可能會想根據螢幕大小提供不同的 UI 版面配置,或者根據語言設定提供不同的字串。 + +

      + +

      具體化您的應用程式資源後,可以使用在專案 {@code R} 類別中產生的資源 ID 來存取它們。 +在應用程式使用資源的方法在存取資源中有相關討論。 + +本文件說明如何在 Android 專案分組您的資源,並為特定裝置設定提供替代資源。 +

      + + +

      分組資源類型

      + +

      您應該將每個資源類型放在專案 +{@code res/} 目錄的特定子目錄中。例如,下列為簡單專案的檔案階層:

      + +
      +MyProject/
      +    src/  
      +        MyActivity.java  
      +    res/
      +        drawable/  
      +            graphic.png  
      +        layout/  
      +            main.xml
      +            info.xml
      +        mipmap/  
      +            icon.png 
      +        values/  
      +            strings.xml  
      +
      + +

      如您在此範例所見,{@code res/} 目錄包含所有資源 (在子目錄中):一個影像資源、兩個版面配置資源、啟動器圖示的 {@code mipmap/} 目錄,以及字串資源檔案。 + +資源目錄名稱非常重要,在表 1 有相關說明。 +

      + +

      注意:如需有關使用 mipmap 資料夾的詳細資訊,請參閱管理專案總覽。 +

      + +

      表 1.專案 {@code res/} 目錄內部支援資源目錄。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      目錄資源類型
      animator/定義屬性動畫的 XML 檔案。 +
      anim/定義 tween 動畫的 XML 檔案。 +(屬性動畫也能儲存在這個目錄中,但建議將屬性動畫放在{@code animator/} 目錄中,以區分這兩個類型。) + +
      color/定義色彩狀態清單的 XML 檔案。請參閱色彩狀態清單資源 +
      drawable/

      編譯成下列可繪項目資源子類型的點陣圖檔案 ({@code .png}、{@code .9.png}、{@code .jpg}、{@code .gif}) 或 XML 檔案: +

      +
        +
      • 點陣圖檔案
      • +
      • 九宮格影像 (可重新調整大小的點陣圖)
      • +
      • 狀態清單
      • +
      • 形狀
      • +
      • 動畫可繪項目
      • +
      • 其他可繪項目
      • +
      +

      請參閱可繪項目資源

      +
      mipmap/適用於不同啟動器圖示密度的可繪項目檔案。如需有關使用 {@code mipmap/} 資料夾管理啟動器圖示的詳細資訊,請參閱管理專案總覽。 + +
      layout/定義使用者介面版面配置的 XML 檔案。 + 請參閱版面配置資源
      menu/定義應用程式選單 (例如,選項選單、操作選單或子選單) 的 XML 檔案。 +請參閱選單資源
      raw/

      以原始格式儲存的任意檔案。如要使用原始 +{@link java.io.InputStream} 開啟這些資源,使用資源 ID 呼叫 {@link android.content.res.Resources#openRawResource(int) +Resources.openRawResource()},資源 ID 為 {@code R.raw.filename}。

      +

      然而,如果您需要存取原始檔案名稱和檔案階層,可以考慮將一些資源儲存在 {@code +assets/} 目錄中 (而不是 {@code res/raw/})。 +位於 {@code assets/} 的檔案不會有資源 ID,因此您只能使用 {@link android.content.res.AssetManager} 讀取它們。 +

      values/

      包含簡單值 (例如,字串、整數和色彩) 的 XML 檔案。

      +

      位於其他 {@code res/} 子目錄的 XML 資源檔案會根據 XML 檔案名稱定義單一資源,而位於 {@code values/} 目錄的檔案則會描述多個資源。 +針對這個目錄中的檔案,{@code <resources>} 元素的每個子項都會定義單一資源。 + +例如,{@code <string>} 元素建立一個 +{@code R.string} 資源,{@code <color>} 元素建立一個 {@code R.color}資源。 +

      +

      由於每個資源都是以自己的 XML 元素定義,您可以依照自己的喜好命名檔案,並將不同的資源類型放在一個檔案中。 +然而,為了更加清楚,您可以將唯一資源類型放在不同的檔案中。 +例如,針對您可以在此目錄建立的資源,這裡提供一些檔案名稱慣例: +

      + +

      請參閱字串資源、 +樣式資源和 +更多資源類型

      +
      xml/可以在執行階段讀取的任意 XML 檔案,方法是呼叫 {@link +android.content.res.Resources#getXml(int) Resources.getXML()}。各種 XML 設定檔案都必須儲存在這裡,例如可搜尋項目設定。 + +
      + +

      注意:千萬不要將資源檔案直接儲存在 +{@code res/} 目錄內 — 會發生編譯錯誤。

      + +

      如需特定資源類型的詳細資訊,請參閱資源類型文件。

      + +

      您儲存在表 1 定義之子目錄的資源是您的「預設」資源。 +也就是說,這些資源會定義您應用程式的預設設計和內容。 +然而,不同類型的 Android 平台裝置可能需要不同類型的資源。 +例如,如果裝置的螢幕大於一般螢幕,則您需要提供不同的版面配置資源,以充分利用額外的螢幕空間。 +或者,如果裝置有不同的語言設定,則您需要提供不同的字串資源,以翻譯您使用者介面中的文字。 + +如要為不同裝置設定提供這些不同的資源,除了預設資源之外,您還需要提供替代資源。 + +

      + + +

      提供替代資源

      + + +
      + +

      +圖 1.兩個不同的裝置,個別使用不同的版面配置資源。

      +
      + +

      幾乎每個應用程式都應提供替代資源以支援特定裝置設定。 +例如,您應該為不同的螢幕密度包含替代可繪項目資源,並為不同語言提供替代字串資源。 +Android 會在執行階段偵測目前的裝置設定,並為您的應用程式載入適當的資源。 + +

      + +

      如要為一組資源指定特定設定的替代項目:

      +
        +
      1. 在 {@code res/} 建立一個新的目錄,命名的格式為 {@code +<resources_name>-<config_qualifier>}。 +
          +
        • {@code <resources_name>} 是對應預設資源 (定義在表 1) 的目錄名稱。 +
        • +
        • {@code <qualifier>} 是用來指定要使用這些資源之個別設定的名稱 (定義在表 2)。 +
        • +
        +

        您可以附加一個以上的 {@code <qualifier>}。使用破折號分隔每個項目。 +

        +

        注意:附加多個限定詞時,您必須以表 2 列出的相同順序放置它們。 +如果限定詞的順序錯誤,將會忽略該資源。 +

        +
      2. +
      3. 將各個替代資源儲存在這個新的目錄。資源檔案的名稱必須和預設資源檔案的名稱完全相同。 +
      4. +
      + +

      例如,這裡有些預設和替代資源:

      + +
      +res/
      +    drawable/   
      +        icon.png
      +        background.png    
      +    drawable-hdpi/  
      +        icon.png
      +        background.png  
      +
      + +

      {@code hdpi} 限定詞代表該目錄的資源適用於高密度螢幕裝置。 +這些可繪項目目錄中的影像大小已調整為符合特定螢幕密度,但是檔案名稱完全相同。 + +如此一來,您用來參照 {@code icon.png} 或 {@code +background.png} 影像的資源 ID 一律都會相同,但 Android 會透過比對裝置設定資訊和資源目錄名稱中的限定詞,選擇最符合目前裝置的每個資源版本。 + +

      + +

      Android 支援多種設定限定詞,而且您可以將多個限定詞加到一個目錄名稱,並用破折號分隔每個限定詞。 +表 2 依優先等級列出有效的設定限定詞 — 如果您針對一個資源目錄使用多個限定詞,您必須依表格中列出的順序將它們新增到目錄名稱。 + + +

      + + +

      表 2.設定限定詞名稱。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      設定限定詞值描述
      MCC 和 MNC範例:
      + mcc310
      + mcc310-mnc004
      + mcc208-mnc00
      + 等等。 +
      +

      行動裝置國家/地區代碼 (MCC) 後面會選擇性加上裝置 SIM 卡上的行動裝置網路代碼 (MNC) +。例如,mcc310 代表美國地區的所有行動通訊業者、 + mcc310-mnc004 代表美國地區的 Verizon,以及 mcc208-mnc00 代表法國地區的 Orange。 +

      +

      如果裝置使用無線電連線 (GSM 手機),MCC 和 MNC 值都會來自SIM 卡。 +

      +

      您也可以單獨使用 MCC (例如,在應用程式包含國家特定法律資源)。 +如果您只需根據語言進行指定,則改為使用「語言和區域」限定詞 (稍後將會討論)。 +如果您決定使用 MCC 和 MNC 限定詞,應謹慎使用,並測試是否可按預期運作。 +

      +

      另請查看設定欄位 {@link +android.content.res.Configuration#mcc} 和 {@link +android.content.res.Configuration#mnc},分別提供目前的行動裝置國家代碼和行動裝置網路代碼。 +

      +
      語言和區域範例:
      + en
      + fr
      + en-rUS
      + fr-rFR
      + fr-rCA
      + 等等。 +

      語言是以兩個字母的 ISO 639-1 語言代碼定義,後面可以視需要加上兩個字母的 ISO 3166-1-alpha-2 區域代碼 (前面加上小寫 "{@code r}")。 + + + +

      + 代碼「沒有」大小寫之分;{@code r} 首碼是用來區分區域部分。 + + 您不能只指定區域。

      +

      如果使用者在系統設定變更其語言,此設定就會在應用程式生命週期內發生變更。 +請參閱處理執行階段變更,以瞭解這會如何在執行階段期間影響您的應用程式。 +

      +

      請參閱當地語系化,以取得將您的應用程式當地語系化成其他語言的詳細指南。 +

      +

      另請查看 {@link android.content.res.Configuration#locale} 設定欄位,這會提供目前的地區設定。 +

      +
      版面配置方向ldrtl
      + ldltr
      +

      應用程式的版面配置方向。{@code ldrtl} 代表「版面配置方向右至左」。 + {@code ldltr} 代表「版面配置方向左至右」,而且是預設隱含值。 +

      +

      這適用於任何資源,例如版面配置、可繪項目或值。 +

      +

      例如,如果您想針對阿拉伯語言提供一些特定版面配置,以及為任何其他「右至左」語言 (像是波斯文或希伯來文) 提供幾個一般版面配置,則程式碼如下: + +

      +
      +res/
      +    layout/   
      +        main.xml  (Default layout)
      +    layout-ar/  
      +        main.xml  (Specific layout for Arabic)
      +    layout-ldrtl/  
      +        main.xml  (Any "right-to-left" language, except
      +                  for Arabic, because the "ar" language qualifier
      +                  has a higher precedence.)
      +
      +

      注意:如要為您的應用程式啟用右至左版面配置功能,您必須將 {@code + supportsRtl} 設成 {@code "true"},以及將 {@code targetSdkVersion} 設成 17 或更高。 +

      +

      已新增至 API 級別 17。

      +
      smallestWidthsw<N>dp

      + 範例:
      + sw320dp
      + sw600dp
      + sw720dp
      + 等等。 +
      +

      螢幕的基本大小,也就是可用 +螢幕區域的最短維度。具體而言,裝置的 smallestWidth 是螢幕最短的可用高度和寬度 (您也可以把它當成螢幕的「最小寬度」)。 +您可以使用此限定詞確保無論螢幕目前的方向為何,您的應用程式至少有 {@code <N>} dps 的寬度可供 UI 使用。 + +

      +

      例如,如果您的版面配置需要隨時保持至少 600 dp 的最小螢幕區域維度,則您可以使用此限定詞建立版面配置資源 {@code +res/layout-sw600dp/}。 +系統只會在可用螢幕的最小維度至少是 600dp 時使用這些資源,無論 600dp 的長度對使用者而言是高度或寬度都一樣。 + +smallestWidth 是螢幕的固定螢幕大小特性;裝置的 smallestWidth 不會隨著螢幕方向的改變而變更。 +

      +

      裝置的 smallestWidth 會將螢幕裝飾和系統 UI 列入計算。例如,如果裝置的螢幕上有一些永久的 UI 元素,且這些元素佔用了 smallestWidth 座標軸上的空間,系統會宣告 smallestWidth 小於實際螢幕大小,因為這些是 UI 無法使用的螢幕像素。 + + +因此,您使用的值應該是版面配置所需的實際最小維度 (這個值通常是您版面配置支援的「最小寬度」,無論螢幕目前的方向為何都一樣)。 + +

      +

      您可以針對一般螢幕大小在這裡使用的一些值:

      +
        +
      • 320 適用於採用以下螢幕設定的裝置: +
          +
        • 240x320 ldpi (QVGA 手機)
        • +
        • 320x480 mdpi (手機)
        • +
        • 480x800 hdpi (高密度手機)
        • +
        +
      • +
      • 480 適用於 480x800 mdpi 這類螢幕 (平板電腦/手機)。
      • +
      • 600 適用於 600x1024 mdpi 這類螢幕 (7" 平板電腦)。
      • +
      • 720 適用於 720x1280 mdpi 這類螢幕 (10" 平板電腦)。
      • +
      +

      當您的應用程式為 smallestWidth 限定詞提供多個值不相同的資源目錄時,系統會使用最接近 (不超過)裝置 smallestWidth 的值。 + +

      +

      已新增至 API 級別 13。

      +

      另請查看 {@code +android:requiresSmallestWidthDp} 屬性,該屬性會宣告與您應用程式相容的最小 smallestWidth,以及 {@link +android.content.res.Configuration#smallestScreenWidthDp} 設定欄位,此欄位保留裝置的 smallestWidth 值。 + +

      +

      如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱支援多個螢幕開發人員指南。 + +

      +
      可用寬度w<N>dp

      + 範例:
      + w720dp
      + w1024dp
      + 等等。 +
      +

      指定資源應使用的最小可用螢幕寬度,以 {@code dp} 單位計算 — 由 <N> 值定義。 +這個設定值會隨著螢幕方向變更為橫向或直向而改變,以符合目前實際的寬度。 + +

      +

      當您的應用程式為這個設定提供多個值不相同的資源目錄時,系統會使用最接近 (不超過) 裝置目前螢幕寬度的值。 + +這個值會將螢幕裝飾列入計算,因此如果裝置顯示的左邊緣或右邊緣有一些永久的 UI 元素,則會使用比實際螢幕大小更小的寬度值,將這些 UI 元素列入計算並減少應用程式的可用空間。 + + + +

      +

      已新增至 API 級別 13。

      +

      另請查看 {@link android.content.res.Configuration#screenWidthDp} 設定欄位,該欄位保留目前的螢幕寬度。 +

      +

      如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱支援多個螢幕開發人員指南。 + +

      +
      可用高度h<N>dp

      + 範例:
      + h720dp
      + h1024dp
      + 等等。 +
      +

      指定資源應使用的最小可用螢幕高度,以「dp」單位計算 — 由 <N> 值定義。 +這個設定值會隨著螢幕方向變更為橫向或直向而改變,以符合目前實際的高度。 + +

      +

      當您的應用程式為這個設定提供多個值不相同的資源目錄時,系統會使用最接近 (不超過) 裝置目前螢幕高度的值。 + +這個值會將螢幕裝飾列入計算,因此如果裝置顯示的上邊緣或下邊緣有一些永久的 UI 元素,則會使用比實際螢幕大小更小的高度值,將這些 UI 元素列入計算並減少應用程式的可用空間。 + + + +不固定的螢幕裝飾 (例如,可在全螢幕時隱藏的手機狀態列) 在這裡不列入計算,標題列或動作列這類視窗裝飾也不會列入計算,因此應用程式必須做好準備因應比其指定還小的空間。 + + + + +

      已新增至 API 級別 13。

      +

      另請查看 {@link android.content.res.Configuration#screenHeightDp} 設定欄位,該欄位保留目前的螢幕寬度。 +

      +

      如需設計不同螢幕及使用此限定詞的詳細資訊,請參閱支援多個螢幕開發人員指南。 + +

      +
      螢幕大小 + small
      + normal
      + large
      + xlarge +
      +
        +
      • {@code small}:與低密度 QVGA 螢幕大小相似的螢幕 +。小螢幕的最低版面配置大小約 320x426 dp 單位。 +範例包含 QVGA 低密度和 VGA 高密度。 +
      • +
      • {@code normal}:與中密度 HVGA 螢幕大小相似的螢幕。 +一般螢幕的最低版面配置大小約 320x470 dp 單位。 +這類螢幕的範例有 WQVGA 低密度、HVGA 中密度、WVGA高密度。 + +
      • +
      • {@code large}:與中密度 VGA 螢幕大小相似的螢幕。 + + 大螢幕的最低版面配置大小約 480x640 dp 單位。 + 範例包含 VGA 和 WVGA 中密度螢幕。
      • +
      • {@code xlarge}:比傳統中密度 HVGA 螢幕大很多的螢幕。 +超大螢幕的最低版面配置大小約 720x960 dp 單位。 +在大多數情況下,使用超大螢幕的裝置由於尺寸過大無法放入口袋,因此最有可能是平板電腦樣式的裝置。 + +已新增至 API 級別 9。
      • +
      +

      注意:使用大小限定詞不代表資源僅適用於該大小的螢幕。 +如果您提供的替代資源沒有更符合目前裝置設定的限定詞,系統將使用最符合的資源。 + +

      +

      注意:如果所有資源都使用比目前螢幕更大的大小限定詞,系統將不會使用這些資源,您的應用程式將會在執行階段當機 (例如,如果所有版面配置資源都標記為 {@code +xlarge} 限定詞,但裝置為一般大小螢幕)。 + +

      +

      已新增至 API 級別 4。

      + +

      請參閱支援多個螢幕以取得詳細資訊。 +

      +

      另請查看 {@link android.content.res.Configuration#screenLayout} 設定欄位,該欄位會指出螢幕為小螢幕、一般螢幕或大螢幕。 + +

      +
      螢幕外觀 + long
      + notlong +
      +
        +
      • {@code long}:長螢幕,例如 WQVGA、WVGA、FWVGA
      • +
      • {@code notlong}:非長螢幕,例如 QVGA、HVGA、VGA
      • +
      +

      已新增至 API 級別 4。

      +

      這純粹是依照螢幕的外觀比例來判定 (「長」螢幕是較寬的螢幕)。這與螢幕方向無關。 +

      +

      另請查看 {@link android.content.res.Configuration#screenLayout} 設定欄位,該欄位會指出螢幕是否為長螢幕。 +

      +
      螢幕方向 + port
      + land +
      +
        +
      • {@code port}:裝置的方向為直向 (垂直)
      • +
      • {@code land}:裝置的方向為橫向 (水平)
      • + +
      +

      如果使用者旋轉螢幕,此設定就會在應用程式生命週期內發生變更。 +請參閱處理執行階段變更,以瞭解這會如何在執行階段期間影響您的應用程式。 +

      +

      另請查看 {@link android.content.res.Configuration#orientation} 設定欄位,該欄位會指出目前的裝置方向。 +

      +
      UI 模式 + car
      + desk
      + television
      + appliance + watch +
      +
        +
      • {@code car}:在車用座架顯示的裝置
      • +
      • {@code desk}:在桌面座架顯示的裝置
      • +
      • {@code television}:在電視上顯示的裝置,提供在離使用者很遠的大螢幕上顯示 UI 的「十英呎」體驗,主要透過 DPAD 或其他非指標互動指定方向。 + + +
      • +
      • {@code appliance}:做為設備使用的裝置,沒有顯示器 +
      • +
      • {@code watch}:裝置有佩戴在手腕上的顯示器
      • +
      +

      已新增至 API 級別 8、電視已新增至 API 13、手錶已新增至 API 20。

      +

      如要瞭解從座架插入或移除裝置時,您的應用程式可以如何回應的相關資訊,請參閱判斷和監控座架狀態和類型。 + +

      +

      如果使用者將裝置放入座架,此設定就會在應用程式生命週期內發生變更。 +您可以使用 {@link +android.app.UiModeManager} 啟用或停用這些模式。請參閱處理執行階段變更,以瞭解這會如何在執行階段期間影響您的應用程式。 +

      +
      夜間模式 + night
      + notnight +
      +
        +
      • {@code night}:夜間時間
      • +
      • {@code notnight}:日間模式
      • +
      +

      已新增至 API 級別 8。

      +

      如果從自動模式 (預設) 的夜間模式離開,在這種情況下模式會隨著日間時間變更,此設定就會在應用程式生命週期內發生變更。 +您可以使用 {@link android.app.UiModeManager} 啟用或停用這個模式。 +請參閱處理執行階段變更,以瞭解這會如何在執行階段期間影響您的應用程式。 +

      +
      螢幕像素密度 (dpi) + ldpi
      + mdpi
      + hdpi
      + xhdpi
      + xxhdpi
      + xxxhdpi
      + nodpi
      + tvdpi +
      +
        +
      • {@code ldpi}:低密度螢幕;約 120dpi。
      • +
      • {@code mdpi}:中密度 (在傳統 HVGA) 螢幕;約 160dpi。 +
      • +
      • {@code hdpi}:高密度螢幕;約 240dpi。
      • +
      • {@code xhdpi}:特高密度螢幕;約 320dpi。已新增至 API 級別 8。 +
      • +
      • {@code xxhdpi}:特特高密度螢幕;約 480dpi。已新增至 API 級別 16。 +
      • +
      • {@code xxxhdpi}:特特特高密度使用 (僅限啟動器圖示,請參閱支援多個螢幕中的注意);約 640dpi。 + +已新增至 API 級別 18。 +
      • +
      • {@code nodpi}:如果您不想縮放點陣圖資源以符合裝置密度,可使用此設定。 +
      • +
      • {@code tvdpi}:介於 mdpi 和 hdpi 的螢幕;約 213dpi。這不屬於「主要」密度群組。 +這大部分用於電視,大多數應用程式應該不需要用到 —mdpi 和 hdpi 資源對大多數應用程式已經足夠,系統將視需要縮放它們。 + +這個限定詞由 API 級別 13 導入。
      • +
      +

      六個主要密度之間有一個 3:4:6:8:12:16 縮放比例 (忽略tvdpi 密度)。 +因此,ldpi 的 9x9 點陣圖等同於 mdpi 的 12x12 點陣圖、hdpi 的 18x18 點陣圖、xhdpi 的 24x24 點陣圖,以此類推。 +

      +

      如果您覺得影像資源在電視或其他特定裝置上看起來不夠美觀,而想嘗試 tvdpi 資源,比例因數為 1.33*mdpi。 +例如,mdpi 螢幕上的 100px x 100px 影像在 tvdpi 螢幕應該是 133px x 133px。 +

      +

      注意:使用密度限定詞不代表資源僅適用於該密度的螢幕。 +如果您提供的替代資源沒有更符合目前裝置設定的限定詞,系統將使用最符合的資源。 + +

      +

      請參閱支援多個螢幕,深入瞭解如何處理不同的螢幕密度,以及 Android會如何縮放點陣圖以符合目前的密度。 + +

      +
      觸控螢幕類型 + notouch
      + finger +
      +
        +
      • {@code notouch}:沒有觸控螢幕的裝置。
      • +
      • {@code finger}:裝置含有觸控螢幕,其目的是透過使用者的手指進行方向互動。 +
      • +
      +

      另請查看 {@link android.content.res.Configuration#touchscreen} 設定欄位,該欄位會指出裝置的觸控螢幕類型。 +

      +
      鍵盤可用性 + keysexposed
      + keyshidden
      + keyssoft +
      +
        +
      • {@code keysexposed}:裝置包含鍵盤。如果裝置啟用軟體鍵盤 (可能性很高),即使沒有將硬體鍵盤展示給使用者或甚至裝置沒有硬體鍵盤時,都可使用此設定。 + +如果沒有提供或已停用軟體鍵盤,則只能在展示硬體鍵盤時使用此設定。 + +
      • +
      • {@code keyshidden}:裝置有可用的硬體鍵盤但已隱藏起來,且裝置沒有啟用軟體鍵盤。 +
      • +
      • {@code keyssoft}:裝置已啟用軟體鍵盤,無論是可見或不可見。 +
      • +
      +

      如果您提供 keysexposed 資源但沒有 keyssoft資源,只要系統已啟用軟體鍵盤,無論鍵盤是否可見,系統都會使用 keysexposed 資源。 + +

      +

      如果使用者開啟硬體鍵盤,此設定就會在應用程式生命週期內發生變更。 +請參閱處理執行階段變更,以瞭解這會如何在執行階段期間影響您的應用程式。 +

      +

      另請查看設定欄位 {@link +android.content.res.Configuration#hardKeyboardHidden} 和 {@link +android.content.res.Configuration#keyboardHidden},這兩個欄位會分別指出硬體鍵盤的可見度,以及任何其他類型鍵盤 (包含軟體) 的可見度。 +

      +
      主要文字輸入方式 + nokeys
      + qwerty
      + 12key +
      +
        +
      • {@code nokeys}:裝置沒有硬體按鍵可輸入文字。
      • +
      • {@code qwerty}:裝置有硬體 qwerty 軟體鍵盤,無論使用者是否可見。 + +
      • +
      • {@code 12key}:裝置有硬體 12 鍵鍵盤,無論使用者是否可見。 +
      • +
      +

      另請查看 {@link android.content.res.Configuration#keyboard} 設定欄位,該欄位會指出可用的主要文字輸入方式。 +

      +
      平台版本 (API 級別)範例:
      + v3
      + v4
      + v7
      + 等等。
      +

      裝置支援的 API 級別。例如,v1 代表 API 級別1 (裝置安裝 Android 1.0 以上版本),以及 v4 代表 API 級別 4 (裝置安裝 Android1.6 以上版本)。 + +請參閱 Android API 級別文件以取得有關這些值的詳細資訊。 +

      +
      + + +

      注意:有些設定限定詞從 Android1.0 就已新增,因此並非所有 Android 版本都支援所有限定詞。 +使用新的限定詞會以隱含的方式新增平台版本的限定詞,因此較舊的裝置一定會忽略它。 +例如,使用 + w600dp 限定詞最自動包含 v13 限定詞,因為可用寬度限定詞是 API 級別 13 的新項目。 +為了避免發生任何問題,永遠要包含一組預設資源 (一組沒有限定詞的資源)。 +如需詳細資訊,請參閱使用資源提供最佳的裝置相容性一節。 + +

      + + + +

      限定詞名稱規則

      + +

      這裡有一些使用設定限定詞名稱的規則:

      + +
        +
      • 您可以為一組資源指定多個限定詞,以破折號分隔。例如, +drawable-en-rUS-land 適用於橫向的美國英文裝置。 +
      • +
      • 限定詞的順序必須與表 2 所列的一樣。例如: + +
          +
        • 錯誤:drawable-hdpi-port/
        • +
        • 正確:drawable-port-hdpi/
        • +
        +
      • +
      • 替代資源目錄不可為巢狀。例如,您不能有 +res/drawable/drawable-en/
      • +
      • 值有大小寫之分。資源編譯器會將目錄名稱轉換為小寫再處理,以避免有大小寫之分的檔案系統發生問題。 + +名稱中的任何大寫只是為了方便閱讀。
      • +
      • 每個限定詞類型只支援一個值。例如,如果您想在西班牙文和法文使用相同的可繪項目檔案,則不能將目錄命名為 +drawable-rES-rFR/。 +您必須有兩個資源目錄,例如 +drawable-rES/drawable-rFR/,這兩個目錄要包含適當的檔案。 +但是,您不需要實際將相同的檔案複製到這兩位置。您可以為資源建立別名。 +請參閱下方的 +建立別名資源
      • +
      + +

      當您將替代資源儲存到以這些限定詞命名的目錄後,Android 會根據目前的裝置設定自動將資源套用到您的應用程式。 + +每次要求資源時,Android 會檢查包含要求之資源檔案的替代資源目錄,然後尋找最符合的資源 + (下方有相關討論)。 +如果沒有符合特定裝置設定的替代資源,則 Android 會使用對應的預設資源 (特定資源類型的資源組,不含設定限定詞)。 + + +

      + + + +

      建立別名資源

      + +

      如果您要將資源用於一個以上的裝置設定 (但不想以預設資源的形式提供),您不需要將相同的資源放入一個以上的替代資源目錄。 + +您可以 (在某些情況下) 建立一個替代資源,將它做為儲存在您預設資源目錄的資源別名。 + +

      + +

      注意:並非所有資源都提供為其他資源建立別名的機制。 +特別是動畫、選單、原始項目和其他在 {@code xml/} 目錄中未指定的資源都不提供這項功能。 +

      + +

      例如,想像您有一個應用程式圖示 {@code icon.png},且必須為不同的地區設定提供唯一的版本。 +但是,加拿大英文和加拿大法文這兩個地區設定必須使用相同的版本。 +您可能會假設必須在加拿大英文和加拿大法文這兩個資源目錄中複製相同的影像,但並不需要這樣做。 + +您可以將這兩個地區設定的影像儲存成 {@code icon_ca.png} ({@code icon.png} 以外的任何名稱),然後將該影像放入預設的 {@code res/drawable/} 目錄。 + +之後,在 {@code +res/drawable-en-rCA/} 建立一個 {@code icon.xml} 檔案,以及建立 {@code res/drawable-fr-rCA/},用於參照使用 {@code <bitmap>} 元素的 {@code icon_ca.png} 資源。 +這樣可以讓您只儲存一個版本的 PNG 檔案,以及指向該檔案的兩個小 XML 檔案。 +(範例 XML 檔案如下所示。)

      + + +

      可繪項目

      + +

      如要為現有的可繪項目建立別名,請使用 {@code <bitmap>} 元素。 +例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:src="@drawable/icon_ca" />
      +
      + +

      如果您將此檔案儲存成 {@code icon.xml} (在替代資源目錄中,像是 +{@code res/drawable-en-rCA/}),系統會將它編譯成可當作 {@code R.drawable.icon} 參照的資源,但它實際上是 {@code +R.drawable.icon_ca} 資源 (儲存在 {@code res/drawable/}) 的別名。 +

      + + +

      版面配置

      + +

      如要為現有的可繪項目建立別名,請使用以 {@code <merge>} 包裝的{@code <include>} 元素。 +例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<merge>
      +    <include layout="@layout/main_ltr"/>
      +</merge>
      +
      + +

      如果您將此檔案儲存成 {@code main.xml},系統會將它編譯成可當作 {@code R.layout.main} 參照的資源,但它實際上是 {@code R.layout.main_ltr} 資源的別名。 + +

      + + +

      字串和其他簡單值

      + +

      如要為現有的字串建立別名,只要將所需字串的資源 ID 當作新字串的值即可。 +例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<resources>
      +    <string name="hello">Hello</string>
      +    <string name="hi">@string/hello</string>
      +</resources>
      +
      + +

      {@code R.string.hi} 資源現在是 {@code R.string.hello} 的別名。

      + +

      其他簡易值的運作方法也一樣。 +例如,一個顏色:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<resources>
      +    <color name="yellow">#f00</color>
      +    <color name="highlight">@color/red</color>
      +</resources>
      +
      + + + + +

      使用資源提供最佳的裝置相容性

      + +

      如要讓您的應用程式支援多個裝置設定,務必要為您應用程式使用的每個資源類型提供預設資源。 +

      + +

      例如,如果您的應用程式支援多個語言,一律要包含一個 {@code +values/} 目錄 (儲存字串的位置),其中「不包含」語言和區域限定詞。如果您將所有字串檔案放在含有語言和區域限定詞的目錄中,當執行應用程式的裝置,其語言設定不受字串支援時,應用程式就會當機。 + +不過,只要您提供預設 {@code values/} 資源, +應用程式就能正常運作 (即便使用者不懂該語言 — 仍然比當機好)。 +

      + +

      同樣地,如果您根據螢幕方向提供不同的版面配置資源,您應該選擇一個方向做為您的預設值。 +例如,不要在 {@code +layout-land/} 提供橫向版面配置資源及在 {@code layout-port/} 提供直向版面配置資源,而是留下一個做為預設值,例如 {@code layout/} 做為橫向和 {@code layout-port/} 做為直向。 +

      + +

      提供預設資源很重要,這不只是因為您的應用程式可能執行您未預期到的設定,還因為新的 Android 版本有時候會新增舊版不支援的設定限定詞。 + +如果您使用新的資源限定詞,但與舊版 Android 保持程式碼相容,則舊版 Android 執行您的應用程式時,如果您沒有提供預設資源,應用程式就會當機,這是因為它無法使用以新限定詞命名的資源。 + + +例如,如果您的 {@code +minSdkVersion} 設為 4,而且您讓所有可繪項目資源具備使用夜間模式的資格 (已新增到 API 級別 8 的 {@code night} 或 {@code notnight}),則 API 級別 4 裝置將無法存取您的可繪項目資源且會當機。 +在這種情況下,您可能會想要將 {@code notnight} 當作預設資源,因此您應該要排除該限定詞,讓您的可繪項目資源成為 {@code drawable/} 或 {@code drawable-night/}。 + +

      + +

      因此,如要提供最佳的裝置相容性,請務必針對應用程式正常執行所需的資源提供預設資源。 +然後,使用設定限定詞為特定裝置設定建立替代資源。 +

      + +

      這個規則有一個例外狀況:如果您應用程式的 {@code minSdkVersion} 為 4 或高,當您提供含有螢幕密度限定詞的替代可繪項目資源時,則不需要預設可繪項目資源。 + +即使沒有預設可繪項目資源,Android 仍然可從替代螢幕密度中找到最符合的項目,並視需要縮放點陣圖。 + +不過,為了在所有裝置類型獲得最佳體驗,您應該為這三種密度類型提供替代可繪項目。 +

      + + + +

      Android 如何尋找最相符的資源

      + +

      當您要求已提供替代項目的資源時,Android 會根據目前的裝置設定,選擇執行階段要使用的替代資源。 +為了示範 Android 如何選取替代資源,假設下列可繪項目目錄分別包含相同影像的不同版本: + +

      + +
      +drawable/
      +drawable-en/
      +drawable-fr-rCA/
      +drawable-en-port/
      +drawable-en-notouch-12key/
      +drawable-port-ldpi/
      +drawable-port-notouch-12key/
      +
      + +

      同時假設裝置設定如下:

      + +

      +地區設定 = en-GB
      +螢幕方向 = port
      +螢幕像素密度 = hdpi
      +觸控螢幕類型 = notouch
      +主要文字輸入方式 = 12key +

      + +

      Android 透過比對裝置設定和可用的替代資源,從 {@code drawable-en-port} 選取可繪項目。 +

      + +

      系統使用下列邏輯決定要使用的資源: +

      + + +
      + +

      圖 2.Android 如何尋找最相符資源的流程圖。 +

      +
      + + +
        +
      1. 排除與裝置設定衝突的資源檔案。 +

        已排除 drawable-fr-rCA/ 目錄,因為它與 en-GB 地區設定衝突。 +

        +
        +drawable/
        +drawable-en/
        +drawable-fr-rCA/
        +drawable-en-port/
        +drawable-en-notouch-12key/
        +drawable-port-ldpi/
        +drawable-port-notouch-12key/
        +
        +

        例外狀況:螢幕像素密度是唯一沒有因為衝突而被排除的限定詞。 +即使裝置的螢幕密度是 hdpi 但仍然沒有排除 +drawable-port-ldpi/,因為此刻每個螢幕密度都視為相符。 +如需詳細資訊,請參閱支援多個螢幕文件。 +

      2. + +
      3. 在清單 (表 2) 中選擇 (下一個) 最高優先等級的限定詞(從 MCC 往下移動)。 +
      4. +
      5. 有任何資源目錄包含此限定詞嗎?
      6. +
          +
        • 如果沒有,回到步驟 2 查看下一個限定詞。(在此範例中,回答一直是「否」,直到語言限定詞為止。) +
        • +
        • 如果有,繼續到步驟 4。
        • +
        + + +
      7. 排除不包含此限定詞的來源目錄。在此範例中,系統排除所有不包含語言限定詞的目錄: +
      8. +
        +drawable/
        +drawable-en/
        +drawable-en-port/
        +drawable-en-notouch-12key/
        +drawable-port-ldpi/
        +drawable-port-notouch-12key/
        +
        +

        例外狀況:如果提到的限定詞是螢幕像素密度,Android 會選擇最符合裝置螢幕密度的選項。一般而言,Android 偏好縮小較大的原始影像以放大較小的原始影像。 + + +請參閱支援多個螢幕。 +

        + + +
      9. 重複步驟 2、3 和 4,直到剩下一個目錄為止。在此範例中,螢幕方向是下個有相符項目的限定詞。 +因此,將排除沒有指定螢幕方向的資源: + +
        +drawable-en/
        +drawable-en-port/
        +drawable-en-notouch-12key/
        +
        +

        剩下的目錄是 {@code drawable-en-port}。

        +
      10. +
      + +

      雖然會針對每個要求的資源執行此程序,但系統還是會進一步最佳化某些方面。 +其中一個最佳化是一旦知道裝置設定,它將排除永遠無法符合的替代資源。 +例如,如果設定語言是英文 ("en"),則語言限定詞設定為不是英文的任何資源目錄永遠都不會包含在檢查的資源集區內 (但仍會包含不含語言限定詞的資源目錄)。 + + +

      + +

      根據螢幕大小限定詞選取資源時,如果沒有最符合的資源,系統會使用適用於比目前螢幕更小之螢幕設計的資源 (例如,如有需要,大型螢幕會使用一般大小螢幕資源)。 + +然而,如果唯一可用的資源比目前螢幕更大,系統將不會使用這些資源,如果沒有其他資源符合裝置設定,則應用程式將會當機 (例如,如果所有版面配置資源都標記為 {@code xlarge} 限定詞,但裝置為一般大小螢幕)。 + + + +

      + +

      注意:限定詞的優先等級 (在表 2) 比完全符合裝置的限定詞數量更重要。 +例如,在上述步驟 4,清單的最後一個選擇包含三個完全符合裝置的限定詞 (方向、觸控螢幕類型和輸入方法),而 drawable-en 只有一個相符的參數(語言)。 + + +然而,語言的優先等級高於這些其他限定詞,因此將排除 +drawable-port-notouch-12key

      + +

      如需深入瞭解在應用程式使用資源的方法,請繼續閱讀存取資源

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd b/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd new file mode 100644 index 0000000000000000000000000000000000000000..7a8b3ae7fc3a08c74e5c1aa0d626686da570f25f --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/resources/runtime-changes.jd @@ -0,0 +1,281 @@ +page.title=處理執行階段變更 +page.tags=Activity、生命週期 +@jd:body + + + +

      有些裝置設定可以在執行階段期間進行變更 (例如,螢幕方向、鍵盤可用性和語言)。 +進行這類變更時,Android 會重新啟動執行中的 +{@link android.app.Activity} (呼叫 {@link android.app.Activity#onDestroy()},後面加上 {@link +android.app.Activity#onCreate(Bundle) onCreate()})。 +重新啟動行為的設計是以符合新裝置設定的替代資源自動重新載入您的應用程式,以協助您的應用程式適應新的設定。 + +

      + +

      如要正確處理重新啟動,務必透過一般 Activity 生命週期將您的 Activity 還原為之前的狀態,其中 Android 會先呼叫 +{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} 再終結您的 Activity,讓您能儲存應用程式狀態的相關資料。 + + +之後,您便能在 {@link android.app.Activity#onCreate(Bundle) onCreate()} 或 {@link +android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()} 期間還原狀態。 +

      + +

      如要測試您的應用程式是否使用原本的應用程式狀態重新啟動,您應該在應用程式執行各種工作時呼叫設定變更 (例如變更螢幕方向)。 + +您的應用程式應該能隨時重新啟動而不會遺失使用者資料或狀態,以便處理設定變更這類事件,或是使用者接聽來電,然後很久後才在應用程式程序可能終結後才返回應用程式的這類情況。 + + +如要瞭解如何還原您的 Activity 狀態,請參閱 Activity 生命週期

      + +

      不過,您可能遇到重新啟動應用程式以及還原大量資料的成本很昂貴,而且會產生使用者體驗不佳的情況。 +在這種情況下,您有兩種其他選擇: +

      + +
        +
      1. 變更設定期間保留物件 +

        允許您的 Activity 在設定變更時重新啟動,但將可設定狀態的物件帶到 Activity 的新執行個體中。 +

        + +
      2. +
      3. 自行處理設定變更 +

        避免系統在某些設定變更期間重新啟動您的 Activity,但在設定變更時接收回呼,您便能視需要手動更新您的 Activity。 + +

        +
      4. +
      + + +

      變更設定期間保留物件

      + +

      如果重新啟動您的 Activity 需要復原大量資料、重新建立網路連線或執行其他密集型操作,則由於設定變更造成的完整重新啟動可能會拖慢使用者體驗。 + +此外,您可能無法透過 {@link android.os.Bundle} 完全還原您的 Activity 狀態,這是系統透過 {@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} 回呼所為您所儲存的—它的設計並不是用來傳送大型物件 (例如點陣圖),而且其中的資料必須先序列化再還原序列化,這會耗用大量記憶體並讓設定變更變慢。 + + + +在這種情況下,您可以在 Activity 因設定變更而重新啟動時,透過保留 {@link +android.app.Fragment} 的方式來減少重新初始化 Activity 的負擔。 +這個片段可包含您要保留可設定狀態物件的參考資料。 +

      + +

      當 Android 系統因設定變更而關閉您的 Activity 時,您標示要保留的 Activity 片段不會被終結。 +您可以將這類片段新增至您的 Activity 以保留可設定狀態的物件。 +

      + +

      如要在執行階段設定變更期間,在片段中保留可設定狀態的物件:

      + +
        +
      1. 延伸 {@link android.app.Fragment} 類別並宣告可設定狀態物件的參考資料。 +
      2. +
      3. 片段建立之後,呼叫 {@link android.app.Fragment#setRetainInstance(boolean)}。 +
      4. +
      5. 將片段新增至您的 Activity。
      6. +
      7. 當 Activity 重新啟動時,使用 {@link android.app.FragmentManager} 擷取片段。 +
      8. +
      + +

      例如,將您的片段定義如下:

      + +
      +public class RetainedFragment extends Fragment {
      +
      +    // data object we want to retain
      +    private MyDataObject data;
      +
      +    // this method is only called once for this fragment
      +    @Override
      +    public void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        // retain this fragment
      +        setRetainInstance(true);
      +    }
      +
      +    public void setData(MyDataObject data) {
      +        this.data = data;
      +    }
      +
      +    public MyDataObject getData() {
      +        return data;
      +    }
      +}
      +
      + +

      注意:雖然您可以儲存任何物件,但您不應該傳送與 {@link android.app.Activity} 相關的物件,例如 {@link +android.graphics.drawable.Drawable}、{@link android.widget.Adapter}、{@link android.view.View} 或任何與 {@link android.content.Context} 關聯的其他物件。 + +如果您這樣做,會流失原始 Activity 執行個體的所有檢視和資源。 +(資源流失表示您的應用程式會繼續保留資源但無法回收記憶體,因此會流失大量記憶體。) + +

      + +

      然後使用 {@link android.app.FragmentManager} 將片段新增至您的 Activity。您可以在執行階段設定變更期間,於 Activity 再次啟動時,從片段取得資料物件。 + +例如,將您的 Activity 定義如下:

      + +
      +public class MyActivity extends Activity {
      +
      +    private RetainedFragment dataFragment;
      +
      +    @Override
      +    public void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        setContentView(R.layout.main);
      +
      +        // find the retained fragment on activity restarts
      +        FragmentManager fm = getFragmentManager();
      +        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
      +
      +        // create the fragment and data the first time
      +        if (dataFragment == null) {
      +            // add the fragment
      +            dataFragment = new DataFragment();
      +            fm.beginTransaction().add(dataFragment, “data”).commit();
      +            // load the data from the web
      +            dataFragment.setData(loadMyData());
      +        }
      +
      +        // the data is available in dataFragment.getData()
      +        ...
      +    }
      +
      +    @Override
      +    public void onDestroy() {
      +        super.onDestroy();
      +        // store the data in the fragment
      +        dataFragment.setData(collectMyLoadedData());
      +    }
      +}
      +
      + +

      在此範例中,{@link android.app.Activity#onCreate(Bundle) onCreate()} 會在 Activity 新增片段或還原參考資料。 +{@link android.app.Activity#onCreate(Bundle) onCreate()} 也會在片段執行個體內儲存可設定狀態的物件。 + +{@link android.app.Activity#onDestroy() onDestroy()} 會在保留的片段執行個體內更新可設定狀態的物件。 +

      + + + + + +

      自行處理設定變更

      + +

      如果您的應用程式在特定設定變更期間不需要更新資源,「且」您具有效能限制,要求您避免 Activity 重新啟動,則您可以宣告您的 Activity 自行處理設定變更,這樣可避免系統重新啟動您的 Activity。 + + +

      + +

      注意:自行處理設定變更讓替代資源的使用變得更加困難,因為系統無法幫您自動套用。 + +當您必須避免因為設定變更而造成重新啟動時,應將這個方式視為最後手段,而且不建議對大部分的應用程式使用。 +

      + +

      如要宣告您的 Activity 處理設定變更,可在宣示說明檔案中編輯適當的 {@code <activity>} 元素以包含 {@code +android:configChanges} 屬性和值,代表您要處理的設定。 + +{@code +android:configChanges} 屬性的可能值列於文件中 (最常用的值是 {@code "orientation"},可避免在螢幕方向變更時重新啟動,而 {@code "keyboardHidden"} 可避免鍵盤可用性變更時重新啟動)。 + +您可以使用直立線符號 {@code |} 字元來分隔,在屬性中宣告多個設定值。 +

      + +

      例如,下列宣示說明程式碼宣告同時處理螢幕方向變更和鍵盤可用性變更的 Activity: +

      + +
      +<activity android:name=".MyActivity"
      +          android:configChanges="orientation|keyboardHidden"
      +          android:label="@string/app_name">
      +
      + +

      現在,當其中一個設定變更時, {@code MyActivity} 便不會重新啟動。 +而是由 {@code MyActivity} 接收對 {@link android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 的呼叫。 +這個方法傳送一個 {@link android.content.res.Configuration} 物件,指定新裝置設定。 + +讀取 {@link android.content.res.Configuration} 中的欄位時,您可判斷新的設定,並且更新您介面中使用的資源來進行適當的變更。 + +此時,會呼叫這個方法,Activity 的 {@link android.content.res.Resources} 物件會根據新設定進行更新以傳回資源,因此您便能輕鬆地重新設定 UI 的元素,系統無需重新啟動您的 Activity。 + + +

      + +

      注意:從 Android 3.2 (API 級別 13) 開始,裝置在橫向與直向之間進行切換時,「螢幕大小」也會跟著變更。 + +因此,在開發 API 級別 13 或更高級別時 (如 {@code minSdkVersion}{@code targetSdkVersion} 屬性所宣告),如果您要避免因為方向變更而造成執行階段重新啟動,除了{@code +"orientation"} 值之外,您還必須包含 {@code "screenSize"} 值。 + +也就是說,您必須宣告 {@code +android:configChanges="orientation|screenSize"}。不過,如果您的應用程式是針對 API 級別 12 或更低級別,則您的 Activity 一律要自行處理這個設定變更 (即使在 Android 3.2 或更高版本的裝置上執行時,這個設定變更也不會重新啟動您的 Activity)。 + +

      + +

      例如,下列 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 實作會檢查目前的裝置方向: +

      + +
      +@Override
      +public void onConfigurationChanged(Configuration newConfig) {
      +    super.onConfigurationChanged(newConfig);
      +
      +    // Checks the orientation of the screen
      +    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
      +        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
      +    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
      +        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
      +    }
      +}
      +
      + +

      {@link android.content.res.Configuration} 物件代表目前所有的設定,而不只是已變更的設定。 +大多數情況下,您不用特別留意設定如何變更,只要重新指派所有的資源,提供替代項目給您正在處理的設定。 + +例如,因為 {@link +android.content.res.Resources} 物件現在已更新,您可以使用 {@link android.widget.ImageView#setImageResource(int) +setImageResource()} 重新設定任何 {@link android.widget.ImageView}並且針對新設定,使用適當的資源 (如提供資源中所述)。 + +

      + +

      請注意,{@link +android.content.res.Configuration} 欄位的值,是與 {@link android.content.res.Configuration} 類別的特定常數相符的整數。 +如需與每個欄位搭配使用之常數的相關文件,請參閱 {@link +android.content.res.Configuration} 參考資料中的適當欄位。 +

      + +

      請記住:當您宣告您的 Activity 以處理設定變更時,您要負責為提供的替代項目重新設定所有元素。 +如果您宣告您的 Activity 以處理方向變更,而且具有應該在橫向與直向之間進行方向變更的影像,則您必須在 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} 期間對每個元素重新指派每個資源。 + +

      + +

      如果您不需要根據這些設定變更來更新您的應用程式,可改為不必實作 {@link +android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}。 +在此情況下,仍然可使用設定變更前使用的所有資源,而您只要避免重新啟動您的 Activity 即可。 + +不過,您的應用程式應該能夠關閉以及重新啟動成之前原本的狀態,因此在一般 Activity 生命週期期間無法保留您的狀態時,就不應考慮使用這個方法。 + +這不只是因為有其他設定變更讓您無法防止重新啟動應用程式,還因為有您應該處理事件,例如當使用者離開應用程式後,使用者返回應用程式之前應用程式已終結。 + + +

      + +

      如需有關您在 Activity 中可以處理哪些設定變更的詳細資訊,請參閱 {@code +android:configChanges} 文件和 {@link android.content.res.Configuration} 類別。 +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd new file mode 100644 index 0000000000000000000000000000000000000000..0f27ae4e8803f256d7abf642a8e64d314a5eda9f --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/controls.jd @@ -0,0 +1,90 @@ +page.title=輸入控制項 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +
      + +
      + +

      輸入控制項是應用程式使用者介面中的互動元件。Android 提供了多種控制項讓您在 UI 中使用,例如按鈕、文字欄位、搜尋列、核取方塊、縮放按鈕、切換按鈕等。 + +

      + +

      在使用者介面中加入輸入控制項,就如同將 XML 元素加到 XML 版面配置一樣簡單。例如,以下是包含文字欄位和按鈕的版面配置: +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:layout_width="fill_parent"
      +    android:layout_height="fill_parent"
      +    android:orientation="horizontal">
      +    <EditText android:id="@+id/edit_message"
      +        android:layout_weight="1"
      +        android:layout_width="0dp"
      +        android:layout_height="wrap_content"
      +        android:hint="@string/edit_message" />
      +    <Button android:id="@+id/button_send"
      +        android:layout_width="wrap_content"
      +        android:layout_height="wrap_content"
      +        android:text="@string/button_send"
      +        android:onClick="sendMessage" />
      +</LinearLayout>
      +
      + +

      每個輸入控制項均支援一組特定輸入事件,方便您處理使用者輸入文字或輕觸按鈕等事件。 +

      + + +

      一般控制項

      +

      下方列出您可在應用程式中使用的部分一般控制項。只要點進清單中的相關連結,即可進一步瞭解如何使用這些控制項。 +

      + +

      注意:本文並未列出 Android 提供的部分控制項。 +如果想查看未列出的控制項,請瀏覽 {@link android.widget} 套件。如果您的應用程式需要特定類型的輸入控制項,您可以自行建置自訂元件。 +

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      控制項類型說明相關類別
      按鈕可供使用者按下或點擊來執行某項動作的按鈕。{@link android.widget.Button Button}
      文字欄位可編輯的文字欄位。您可以使用 AutoCompleteTextView 小工具建立可提供自動完成建議的文字輸入小工具。{@link android.widget.EditText EditText}、{@link android.widget.AutoCompleteTextView}
      核取方塊可供使用者切換的開啟/關閉開關。如果想為使用者提供一組互不相斥的可選取選項時,請使用核取方塊。{@link android.widget.CheckBox CheckBox}
      圓形按鈕功用與核取方塊類似,但會限制使用者只能從一組選項中選取一個選項。{@link android.widget.RadioGroup RadioGroup} +
      {@link android.widget.RadioButton RadioButton}
      切換按鈕附有亮光指標的開啟/關閉按鈕。{@link android.widget.ToggleButton ToggleButton}
      微調按鈕可供使用者從一組選項中選取單一值的下拉式清單。{@link android.widget.Spinner Spinner}
      挑選器可供使用者透過向上/向下按鈕或滑動手勢選取單一值的對話方塊。此外,挑選器還會提供 DatePicker 程式碼小工具和 TimePicker 小工具,分別讓使用者輸入日期值 (年、月、日) 以及時間值 (小時、分鐘、AM/PM);系統會自動根據使用者所在的地區為這些值設定對應的格式。{@link android.widget.DatePicker}、{@link android.widget.TimePicker}
      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd new file mode 100644 index 0000000000000000000000000000000000000000..72755715e3a217d13102ab1a1c244bd0fb4fde81 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/declaring-layout.jd @@ -0,0 +1,492 @@ +page.title=版面配置 +page.tags=view,viewgroup +@jd:body + +
      +
      +

      本文件內容

      +
        +
      1. 編寫 XML
      2. +
      3. 載入 XML 資源
      4. +
      5. 屬性 +
          +
        1. ID
        2. +
        3. 版面配置參數
        4. +
        +
      6. +
      7. 版面配置位置
      8. +
      9. 大小、邊距和邊界
      10. +
      11. 常見版面配置
      12. +
      13. 使用配接器建置版面配置 +
          +
        1. 將資料填入配接器檢視
        2. +
        3. 處理點擊事件
        4. +
        +
      14. +
      + +

      重要類別

      +
        +
      1. {@link android.view.View}
      2. +
      3. {@link android.view.ViewGroup}
      4. +
      5. {@link android.view.ViewGroup.LayoutParams}
      6. +
      + +

      另請參閱

      +
        +
      1. 建置簡易使用者 +介面
      +
      + +

      版面配置會定義使用者介面 (例如 Activity應用程式小工具的使用者介面) 的視覺結構。您可以兩種方式宣告版面配置: +

      +
        +
      • 在 XML 中宣告 UI 元素。Android 提供的 XML 字彙不僅簡單明瞭,還可對應至 View 類別和子類別 (例如小工具和版面配置)。 +
      • +
      • 在執行階段啟動版面配置元素。您的應用程式可透過程式建立 View 和 ViewGroup 物件 (以及操控其屬性)。 +
      • +
      + +

      Android 架構可讓您彈性使用上述任一或兩種方法來宣告及管理應用程式的 UI。例如,您可以在 XML 中宣告應用程式的預設版面配置,包括會顯示在應用程式中的元素及其屬性。接著,您可在執行階段為應用程式加入程式碼來修改螢幕物件的狀態,包括您在 XML 中宣告的物件。

      + + + +

      在 XML 中宣告 UI 可讓您進一步將應用程式的顯示畫面與控制應用程式行為的程式碼區隔開來。這樣可將 UI 說明置於應用程式的程式碼外部,方便您修改或調整使用者介面說明,而不必修改並重新編譯原始碼。例如,您可以針對不同的螢幕方向、裝置螢幕大小和語言建立各種 XML 檔案。此外,在 XML 中宣告版面配置可協助以視覺效果呈現 UI 的結構,方便您進行除錯。針對上述優點,本文件著重於說明如何在 XML 中宣告版面配置。如果您想在執行階段啟動 View 物件,請參閱 {@link android.view.ViewGroup} 和 {@link android.view.View} 類別參考文件。 + +

      + +

      一般來說,用於宣告 UI 元素的 XML 字彙會遵從類別與方法的結構和名稱,其中元素名稱對應至類別名稱,而屬性名稱則對應至方法。事實上,這些對應關係十分直接,您可以輕易猜出某個 XML 屬性對應的哪個類別方法,或某個類別對應的特定 XML 元素為何。不過請注意,並非所有字彙均為相同。在部分情況下,您會發現某些不同的名稱。例如,EditText 元素包含對應至 EditText.setText()text 屬性。 + +

      + +

      提示:如要進一步瞭解各種版面配置類型,請參閱常見版面配置物件。 +您也可以參閱 Hello Views 教學指南,取得一系列說明如何建置各種版面配置的教學課程。 +

      + +

      編寫 XML

      + +

      您可以使用 Android 的 XML 字彙,快速設計 UI 版面配置和其中包含的螢幕元素,方法與採用 HTML 建立網頁相同 — 都需要建置一系列巢狀元素。

      + +

      每個版面配置檔案均需包含 1 個根元素 (必須為 View 或 ViewGroup 物件)。定義根元素後,您就可以將額外的物件或小工具新增為子元素,逐步建置檢檢視階層視階層來定義您的版面配置。例如,以下是使用直向 {@link android.widget.LinearLayout} 以納入 +{@link android.widget.TextView} 和 {@link android.widget.Button} 的 XML 版面配置:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +              android:layout_width="match_parent"
      +              android:layout_height="match_parent"
      +              android:orientation="vertical" >
      +    <TextView android:id="@+id/text"
      +              android:layout_width="wrap_content"
      +              android:layout_height="wrap_content"
      +              android:text="Hello, I am a TextView" />
      +    <Button android:id="@+id/button"
      +            android:layout_width="wrap_content"
      +            android:layout_height="wrap_content"
      +            android:text="Hello, I am a Button" />
      +</LinearLayout>
      +
      + +

      在 XML 中宣告版面配置後,請使用 .xml 副檔名將檔案儲存到 Android 專案的 res/layout/ 目錄中,藉此讓系統妥善加以編譯。 +

      + +

      如要進一步瞭解 XML 版面配置檔案的語法,請參閱版面配置資源

      + +

      載入 XML 資源

      + +

      當您編譯應用程式時,系統會將所有 XML 版面配置檔案編入 +{@link android.view.View} 資源。在這種情況下,您必須透過 {@link android.app.Activity#onCreate(android.os.Bundle) Activity.onCreate()} 回呼,載入應用程式的程式碼中的版面配置資源,方法是呼叫 {@link android.app.Activity#setContentView(int) setContentView()},然後採用以下格式將其參考資源傳送到版面配置資源: +R.layout.layout_file_name。例如,假設您將 XML 版面配置儲存成 main_layout.xml,您就必須針對如下所示的 Activity 載入該 XML: + + + + +

      +
      +public void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +    setContentView(R.layout.main_layout);
      +}
      +
      + +

      Android 架構會在 Activity 啟動時呼叫 Activity 中的 onCreate() 回呼方法 (如需生命週期相關資訊,請參閱 Activity)。 + + +

      + + +

      屬性

      + +

      所有 View 和 ViewGroup 物件均支援本身專用的各種 XML 屬性。部分屬性僅適用於 View 物件 (例如 TextView 可支援 textSize 屬性),不過可能延伸這個類別的 View 物件也會沿用這些屬性。由於某些屬性是沿用自 View 根類別,因此會成為所有 View 物件的常用屬性 (例如 id 屬性)。 + + + +而系統會將其他屬性視為「版面配置參數」,這些屬性可說明 View 物件的特定版面配置方向 (View 由物件的 ViewGroup 上層物件所定義)。 + +

      + +

      ID

      + +

      任何 View 物件都可能包含一個相關聯的整數 ID,可用於識別樹狀結構中的 View。在應用程式編寫期間,雖然這個 ID 通常是在 XML 版面配置檔案的 id 屬性中指派為字串,系統仍會將其解讀為整數。此為所有 View 物件常用的 XML 屬性 (由 {@link android.view.View} 類別所定義);您會經常使用的這項屬性。以下是 XML 標記中的 ID 語法: + + + + +

      +
      android:id="@+id/my_button"
      + +

      字串開頭的 @ 符號可指示 XML 剖析器應剖析或展開 ID 字串的其餘部分,並且將其視為 ID 資源。 +加號符號 (+) 表示此為系統必須建立並加到資源 (R.java 檔案) 中的新資源名稱。 +Android 架構提供了多種其他 ID 資源。 +參照 Android 資源 ID 時,您不必加入 + 符號,但必須加入 android 套件命名空間,如下所示: +

      +
      android:id="@android:id/empty"
      +

      只要加入 android 套件命名空間,系統就會從 android.R 資源類別參照 ID,而不是從本機資源類別。 +

      + +

      以下是透過應用程式建立檢視和參照的一般方法:

      +
        +
      1. 在版面配置檔案中定義檢視/小工具,並為該檔案指派不重複 ID: +
        +<Button android:id="@+id/my_button"
        +        android:layout_width="wrap_content"
        +        android:layout_height="wrap_content"
        +        android:text="@string/my_button_text"/>
        +
        +
      2. +
      3. 接著,建立檢視物件執行個體,並從版面配置中擷取該執行個體 (通常使用 {@link android.app.Activity#onCreate(Bundle) onCreate()} 方法進行擷取): + +
        +Button myButton = (Button) findViewById(R.id.my_button);
        +
        +
      4. +
      +

      請務必在建立 {@link android.widget.RelativeLayout} 時為檢視物件定義 ID。在相關的版面配置中,同層級的檢視可將本身的版面配置與其他同層級的檢視建立關聯,而不重複 ID 正是這些版面配置的參照依據。 + +

      +

      整個樹狀結構的 ID 不必是不重複 ID,但您所搜尋樹狀結構部分的 ID 就必須是不重複 ID (由於您搜尋的部分通常是整個樹狀結構,因此建議您為其定義不重複 ID)。 + +

      + + +

      版面配置參數

      + +

      名為 layout_something 的 XML 版面配置屬性會為適用於所屬 ViewGroup 的 View 定義版面配置參數。 +

      + +

      所有 ViewGroup 類別都會實作可延伸 {@link +android.view.ViewGroup.LayoutParams} 的巢狀類別。這個子類別包含定義下層檢視大小和位置的屬性類型,而這些屬性類型也適用於檢視群組。 + +如圖 1 所示,上層檢視群組為每個下層檢視 (包括下層檢視群組) 定義了版面配置參數。 +

      + + +

      圖 1.檢視層級以及與每個檢視相關聯的版面配置參數。 +

      + +

      請注意,所有 LayoutParams 子類別都包含用於設定值的專屬語法。 +而每個子元素都必須定義適用於其上層元素的 LayoutParams,即便子元素也會為其下層物件定義不同的 LayoutParams。 +

      + +

      所有檢視群組均包含寬度和高度 (layout_width 和 +layout_height),而每個檢視都必須定義這些值。大多數 LayoutParams 還會包含選用的標界和邊框。 +

      + +

      您可以依據測量結果指定寬度和高度 (您通常不必經常指定這些值)。 +在大多數情況下,您需使用以下任一常數設定寬度或高度: +

      + +
        +
      • wrap_content 可指示檢視自行將大小調整成其內容所需的尺寸。 +
      • +
      • match_parent (在 API 級別 8 之前稱為 fill_parent ) 可指示檢視自行將大小擴展成上層檢視群組允許的上限。 +
      • +
      + +

      一般來說,我們不建議您使用絕對單位 (例如像素) 指定版面配置的寬度和高度。 +建議做法是使用相對測量單位 (例如依據密度的像素單位 ( +dp)、 wrap_content或 +match_parent指定這些值,這是因為這樣可協助確保應用程式在各種裝置螢幕大小中能保有最佳顯示效果。如需支援的測量類型的定義,請參閱可用資源。 + + + +

      + + +

      版面配置位置

      +

      + 檢視的形狀為矩形。檢視包含一個位置值 (以一組「水平」和「垂直」座標表示),以及兩個尺寸值 (以寬度和高度表示)。 + +位置和尺寸的單位均為像素。 + +

      + +

      + 您可以呼叫 {@link android.view.View#getLeft()} 和 {@link android.view.View#getTop()} 方法來擷取檢視的位置值。 +第一個方法會傳回顯示檢視的矩形區塊的水平座標 (X 座標); +第二個方法則會傳回顯示檢視的矩形區塊的垂直座標 (Y 座標)。 +這兩個方法都會傳回檢視相對於其上層項目的位置。 +例如,假設 getLeft() 傳回 20,表示該檢視位於其直屬上層項目左側邊緣向右平移 20 像素的位置。 + + +

      + +

      + 我們也提供了 {@link android.view.View#getRight()} 和 {@link android.view.View#getBottom()} 這兩個簡易方法,可用於避免不必要的運算程序。 + + 這些方法會傳回險是檢視的矩形區塊的右下角座標。 +例如,呼叫 {@link android.view.View#getRight()} 的用途與以下運算式類似:getLeft() + getWidth()。 + +

      + + +

      大小、邊距和邊界

      +

      + 檢視的大小是以寬度和高度表示。單一檢視會包含兩組寬度和高度值。 + +

      + +

      + 第一組值稱為「寬度測定值」和「高度測定值」, +可定義檢視在上層項目中的尺寸。 +您可以呼叫 {@link android.view.View#getMeasuredWidth()} 和 {@link android.view.View#getMeasuredHeight()} 來取得尺寸測定值。 + + +

      + +

      + 第二組值簡稱「寬度」和「高度」,或是「寬度描繪值」和「高度描繪值」, +可定義檢視在螢幕中的實際大小 (描繪期間以及版面配置之後)。 + +這些值可能 (但未必) 會與寬度和高度測量值不同。 +您可以呼叫 +{@link android.view.View#getWidth()} 和 {@link android.view.View#getHeight()} 來取得尺寸描繪值。 +

      + +

      + 測量檢視的尺寸時,系統會將檢視的邊距納入考量。邊距是指檢視的上、下、左、右部分 (單位為像素), + + 可用於將檢視內容偏移特定像素。 +例如,2 像素的左側邊距可將檢視內容由左側邊緣向右平移 2 像素。 +您可以使用 +{@link android.view.View#setPadding(int, int, int, int)} 方法來設定邊距,以及呼叫 +{@link android.view.View#getPaddingLeft()}、{@link android.view.View#getPaddingTop()}、 +{@link android.view.View#getPaddingRight()} 和 {@link android.view.View#getPaddingBottom()} 來查詢邊距。 +

      + +

      + 雖然檢視可定義邊距,但無法針對邊界提供任何支援。 +不過,檢視群組可提供這類支援。詳情請參閱 +{@link android.view.ViewGroup} 和 +{@link android.view.ViewGroup.MarginLayoutParams}。 +

      + +

      如要進一步瞭解尺寸,請參閱尺寸值。 + +

      + + + + + + + + + + + +

      常見版面配置

      + +

      {@link android.view.ViewGroup} 類別的所有子類別均可以獨有方式顯示您在當中套疊的檢視。 +以下提供幾個 Android 平台內建的常見版面配置類型。 +

      + +

      注意:雖然您可以將版面配置套疊在一起來符合您的 UI 設計,但建議您盡可能讓版面配置階層維持淺薄。 + +套疊的版面配置越少,版面配置的描繪速度就越快 (建議您使用寬的檢視階層,而不是深的檢視階層)。 +

      + + + + +
      +

      線性版面配置

      + +

      這種版面配置可將其下層物件整合至單一水平或垂直列。如果視窗長度超過螢幕長度,線性版面配置就會建立捲軸方便使用者捲動畫面。 +

      +
      + +
      +

      相對版面配置

      + +

      這種版面配置可讓您指定下層物件之間的相對位置 (指定下層物件 A 位於下層物件 B 的左側),或指定下層物件與上層物件的相對位置 (指定下層物件緊貼上層物件的頂端)。 +

      +
      + +
      +

      網頁檢視

      + +

      這種版面配置可顯示網頁。

      +
      + + + + +

      使用配接器建置版面配置

      + +

      如果您版面配置的內容為動態內容,而不是預先定義的內容,您可以在執行階段將子類別為 {@link android.widget.AdapterView} 的版面配置填入檢視中。 + +{@link android.widget.AdapterView} 類別的子類別會使用 {@link android.widget.Adapter} 將資料繫結至本身的版面配置。 +{@link android.widget.Adapter} 就如同是資料來源與 {@link android.widget.AdapterView} 版面配置的中間人 — {@link android.widget.Adapter} 會從陣列或資料庫查詢等來源擷取資料,然後將這些資料轉換成可加入 {@link android.widget.AdapterView} 版面配置的檢視。 + + +

      + +

      以下是配接器支援的常見版面配置:

      + +
      +

      清單檢視

      + +

      這種版面配置可顯示能夠捲動的單欄清單。

      +
      + +
      +

      格狀檢視

      + +

      這種版面配置可顯示能夠捲動的資料欄和資料列網格。

      +
      + + + +

      將資料填入配接器檢視

      + +

      您可將 {@link android.widget.AdapterView} 執行個體繫結至 +{@link android.widget.Adapter} 以便從外部來源擷取資料,並建立可顯示所有資料的 {@link +android.view.View},藉此填入 {@link android.widget.AdapterView} (例如 {@link android.widget.ListView} 或 {@link android.widget.GridView}。 +

      + +

      Android 提供數個 {@link android.widget.Adapter} 子類別可用於擷取不同資料類型以及為 {@link android.widget.AdapterView} 建置檢視。 +以下是兩種常見的配接器: +

      + +
      +
      {@link android.widget.ArrayAdapter}
      +
      如果您的資料來源是陣列,請使用這個配接器。在預設情況下,{@link +android.widget.ArrayAdapter} 會針對每個陣列項目呼叫 {@link +java.lang.Object#toString()} 並取代 {@link +android.widget.TextView} 中的內容,藉此為每個陣列項目建立檢視。 +

      例如,如果您想在 {@link +android.widget.ListView} 中顯示一系列字串,請使用建構函式為每個字串和字串陣列指定版面配置,藉此初始化新的 {@link android.widget.ArrayAdapter}: +

      +
      +ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
      +        android.R.layout.simple_list_item_1, myStringArray);
      +
      +

      這個建構函式的引數包括:

      +
        +
      • 您的應用程式 {@link android.content.Context}
      • +
      • 含有陣列中所有字串的 {@link android.widget.TextView} 的版面配置
      • +
      • 字串陣列
      • +
      +

      接著,針對您的 {@link android.widget.ListView} 呼叫 +{@link android.widget.ListView#setAdapter setAdapter()}:

      +
      +ListView listView = (ListView) findViewById(R.id.listview);
      +listView.setAdapter(adapter);
      +
      + +

      如要自訂每個項目的外觀,您可以針對陣列中的物件覆寫 {@link +java.lang.Object#toString()} 方法。或者,如果想為 {@link android.widget.TextView} 以外的所有項目建立檢視 (例如,如果您想為所有陣列項目建立 {@link android.widget.ImageView}),請延伸 {@link +android.widget.ArrayAdapter} 類別並覆寫 {@link android.widget.ArrayAdapter#getView +getView()},以傳回您想為所有項目建立的檢視類型。 + +

      + +
      + +
      {@link android.widget.SimpleCursorAdapter}
      +
      如果您的資料來自 {@link android.database.Cursor},請使用這個配接器。使用 +{@link android.widget.SimpleCursorAdapter} 時,您必須指定要用於 {@link android.database.Cursor} 中所有資料列的版面配置,以及指定要將 {@link android.database.Cursor} 中的哪些資料欄插入哪些版面配置檢視。 + +例如,如果您想建立一份列出使用者名稱和電話號碼的清單,請執行查詢來傳回 {@link +android.database.Cursor},其中包含一個列出每位使用者的資料列,以及多個列出名稱和電話號碼的資料欄。 + +接著,請建立一個字串陣列以便從 {@link +android.database.Cursor} 指定您想在版面配置中列出每項結果的資料欄,以及建立一個整數陣列為每個資料欄指定對應的值: +

      +
      +String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
      +                        ContactsContract.CommonDataKinds.Phone.NUMBER};
      +int[] toViews = {R.id.display_name, R.id.phone_number};
      +
      +

      當您啟動 {@link android.widget.SimpleCursorAdapter} 時,請傳送用於顯示所有結果的版面配置、包含結果的 {@link android.database.Cursor},以及下列兩個陣列: +

      +
      +SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
      +        R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
      +ListView listView = getListView();
      +listView.setAdapter(adapter);
      +
      +

      {@link android.widget.SimpleCursorAdapter} 隨即會將每個 {@code +fromColumns} 項目插入相對應的 {@code toViews} 檢視,藉此利用提供的版面配置為 +{@link android.database.Cursor} 中的所有資料列建立專屬檢視。

      .
      +
      + + +

      如果您在應用程式的效期內更改配接器讀取的底層資料,您就必須呼叫 {@link android.widget.ArrayAdapter#notifyDataSetChanged()}。 +這樣會通知附加的檢視由於資料已變更,因此需進行重新整理。 +

      + + + +

      處理點擊事件

      + +

      只要實作 {@link android.widget.AdapterView.OnItemClickListener} 介面,即可回應 {@link android.widget.AdapterView} 中所有項目的點擊事件。 +例如:

      + +
      +// Create a message handling object as an anonymous class.
      +private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
      +    public void onItemClick(AdapterView parent, View v, int position, long id) {
      +        // Do something in response to the click
      +    }
      +};
      +
      +listView.setOnItemClickListener(mMessageClickedHandler);
      +
      + + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd new file mode 100644 index 0000000000000000000000000000000000000000..b0ae12ea3a19874410df1a81a5e68faa4b82bd91 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/dialogs.jd @@ -0,0 +1,798 @@ +page.title=對話方塊 +page.tags=alertdialog,dialogfragment + +@jd:body + + + +
      +
      +

      本文件內容

      +
        +
      1. 建立對話方塊片段
      2. +
      3. 建置快訊對話方塊 +
          +
        1. 加入按鈕
        2. +
        3. 加入清單
        4. +
        5. 建立自訂版面配置
        6. +
        +
      4. +
      5. 將事件傳回對話方塊的主控件
      6. +
      7. 顯示對話方塊
      8. +
      9. 顯示全螢幕對話方塊或內嵌片段 +
          +
        1. 針對大型螢幕將 Activity 顯示為對話方塊
        2. +
        +
      10. +
      11. 關閉對話方塊
      12. +
      + +

      重要類別

      +
        +
      1. {@link android.app.DialogFragment}
      2. +
      3. {@link android.app.AlertDialog}
      4. +
      + +

      另請參閱

      +
        +
      1. 對話方塊設計指南
      2. +
      3. 挑選器 (日期/時間對話方塊)
      4. +
      +
      +
      + +

      對話方塊是一種小型視窗,可提示使用者做出決定或輸入額外資訊。 +對話視窗並不會佔滿整個螢幕,而且通常是供強制回應事件使用,這種事件會要求使用者必須先完成特定動作,才能繼續下一步。 +

      + +
      +

      設計對話方塊

      +

      如要瞭解如何設計對話方塊,包括建議使用的設計語言,請參閱對話方塊設計指南。 +

      +
      + + + +

      {@link android.app.Dialog} 類別是對話方塊的基礎類別,但請避免直接啟動 {@link android.app.Dialog}。建議您改用下列其中一個子類別: + +

      +
      +
      {@link android.app.AlertDialog}
      +
      這種對話方塊可以顯示一個標題、最多三個按鈕、一系列可選取項目或一個自訂版面配置。 +
      +
      {@link android.app.DatePickerDialog} 或 {@link android.app.TimePickerDialog}
      +
      這種對話方塊會提供預先定義的 UI,可讓使用者選取日期或時間。
      +
      + + + +

      上述類別可定義對話方塊的樣式和結構,但建議您使用 {@link android.support.v4.app.DialogFragment} 做為對話方塊的容器。 +{@link android.support.v4.app.DialogFragment} 類別可提供您所需的所有控制項,方便您建立對話方塊及管理對話方塊的外觀,如此您不必對 {@link android.app.Dialog} 物件呼叫方法。 + + +

      + +

      使用 {@link android.support.v4.app.DialogFragment} 管理對話方塊可確保對話方塊能正確控制生命週期事件,例如使用者點擊 [返回] 按鈕或旋轉螢幕。 + +此外,{@link +android.support.v4.app.DialogFragment} 類別還能像傳統的 {@link +android.support.v4.app.Fragment} 一樣讓您重複使用對話方塊的 UI,做為大型 UI 中的可嵌入元件 (例如當您想讓對話方塊使用者介面在大型和小型螢幕上呈現不同外觀時)。 + +

      + +

      本指南的以下各節說明如何搭配 {@link android.app.AlertDialog} 物件使用 {@link +android.support.v4.app.DialogFragment}。 +如果您是想建立日期或時間挑選器,請改為參閱挑選器指南。 +

      + +

      注意:{@link android.app.DialogFragment} 類別最初是在 Android 3.0 (API 級別 11) 中導入,因此本文說明如何使用支援程式庫提供的 {@link +android.support.v4.app.DialogFragment} 類別。 + +只要將這個程式庫加到您的應用程式,即可在搭載 Android 1.6 以上版本的裝置上使用 {@link android.support.v4.app.DialogFragment} 以及多種其他 API。 + +如果您應用程式支援的最低版本為 API 級別 11 以上版本,您就可以使用架構版本的 {@link +android.app.DialogFragment},但請注意,本文中提供的連結是用於取得支援程式庫 API。 + +使用支援程式庫時,請務必匯入 android.support.v4.app.DialogFragment 類別,而「不是」android.app.DialogFragment。 + +

      + + +

      建立對話方塊片段

      + +

      如果想建立多種對話方塊設計 — 包括自訂版面配置和對話方塊設計指南中所述的對話方塊設計 — 只要延伸 +{@link android.support.v4.app.DialogFragment} 然後利用 +{@link android.support.v4.app.DialogFragment#onCreateDialog +onCreateDialog()} 回呼方法建立 {@link android.app.AlertDialog} 即可。 + +

      + +

      例如,假設您利用 {@link android.support.v4.app.DialogFragment} 管理以下的基本 +{@link android.app.AlertDialog}:

      + +
      +public class FireMissilesDialogFragment extends DialogFragment {
      +    @Override
      +    public Dialog onCreateDialog(Bundle savedInstanceState) {
      +        // Use the Builder class for convenient dialog construction
      +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +        builder.setMessage(R.string.dialog_fire_missiles)
      +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
      +                   public void onClick(DialogInterface dialog, int id) {
      +                       // FIRE ZE MISSILES!
      +                   }
      +               })
      +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      +                   public void onClick(DialogInterface dialog, int id) {
      +                       // User cancelled the dialog
      +                   }
      +               });
      +        // Create the AlertDialog object and return it
      +        return builder.create();
      +    }
      +}
      +
      + +
      + +

      圖 1. +包含一段訊息和兩個動作按鈕的對話方塊。

      +
      + +

      在這種情況下,如果您建立這個類別執行個體,然後對該物件呼叫 {@link +android.support.v4.app.DialogFragment#show show()},就會顯示如圖 1 所示的對話方塊。 +

      + +

      如要進一步瞭解如何使用 {@link android.app.AlertDialog.Builder} API 建立對話方塊,請參閱下一節。 +

      + +

      視對話方塊的複雜程度而定,您可以在 {@link android.support.v4.app.DialogFragment} 中實作多種其他回呼方法,包括所有的基本片段生命週期方法。 + + + + + + + +

      建置快訊對話方塊

      + + +

      {@link android.app.AlertDialog} 通常是您需要使用的唯一對話方塊類別,可讓您建置多種對話方塊設計。如圖 2 所示,快訊對話方塊是由 3 個區塊組合而成: + +

      + +
      + +

      圖 2.對話方塊的版面配置。

      +
      + +
        +
      1. 標題 +

        此為選用區塊;只有在內容區域提供詳細訊息、一份清單或自訂版面配置時,您才需要使用標題。 +如果您想提供一段簡短訊息或一個簡易問題 (如圖 1 所示的對話方塊),您就不必使用標題。 +

      2. +
      3. 內容區塊 +

        這個區塊可以顯示一段訊息、一份清單或其他自訂版面配置。

      4. +
      5. 動作按鈕 +

        單一對話方塊最多只能包含 3 個動作按鈕。

      6. +
      + +

      {@link android.app.AlertDialog.Builder} 類別提供的 API 可讓您建立包含這些內容類型 (包括自訂版面配置) 的 {@link android.app.AlertDialog}。 + +

      + +

      如何建置 {@link android.app.AlertDialog}:

      + +
      +// 1. Instantiate an {@link android.app.AlertDialog.Builder} with its constructor
      +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +
      +// 2. Chain together various setter methods to set the dialog characteristics
      +builder.setMessage(R.string.dialog_message)
      +       .setTitle(R.string.dialog_title);
      +
      +// 3. Get the {@link android.app.AlertDialog} from {@link android.app.AlertDialog.Builder#create()}
      +AlertDialog dialog = builder.create();
      +
      + +

      以下主題說明如何定義多種採用 {@link android.app.AlertDialog.Builder} 類別的對話方塊屬性。 +

      + + + + +

      加入按鈕

      + +

      想要加入如圖 2 所示的動作按鈕,請呼叫 {@link android.app.AlertDialog.Builder#setPositiveButton setPositiveButton()} 和 +{@link android.app.AlertDialog.Builder#setNegativeButton setNegativeButton()} 方法: +

      + +
      +AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +// Add the buttons
      +builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
      +           public void onClick(DialogInterface dialog, int id) {
      +               // User clicked OK button
      +           }
      +       });
      +builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      +           public void onClick(DialogInterface dialog, int id) {
      +               // User cancelled the dialog
      +           }
      +       });
      +// Set other dialog properties
      +...
      +
      +// Create the AlertDialog
      +AlertDialog dialog = builder.create();
      +
      + +

      set...Button() 方法會要求字串資源提供的按鈕標題,以及可定義使用者點擊按鈕後需要完成的動作的{@link android.content.DialogInterface.OnClickListener}。 + + +

      + +

      您可以加入的動作按鈕分為 3 種:

      +
      +
      正面
      +
      這種按鈕的用途是接受及繼續進行特定動作 (「確定」按鈕)。
      +
      負面
      +
      這種按鈕的用途是取消動作。
      +
      中立
      +
      如果使用者不想繼續進行特定動作,但並非要取消動作,請使用這種按鈕。 +這種按鈕會顯示在正面和負面按鈕之間。 +範例:[稍後提醒我] 按鈕。
      +
      + +

      您可以將以上其中一種按鈕加入 {@link +android.app.AlertDialog};換句話說,您最多只能加入一個「正面」按鈕。

      + + + +
      + +

      圖 3. +包含一個標題和一份清單的對話方塊。

      +
      + +

      加入清單

      + +

      {@link android.app.AlertDialog} API 可提供以下 3 種清單類型:

      +
        +
      • 傳統的單一選項清單
      • +
      • 永續性的單一選項清單 (圓形按鈕)
      • +
      • 永續性的多重選項清單 (核取方塊)
      • +
      + +

      想要建立如圖 3 所示的單一選項清單,請使用 {@link android.app.AlertDialog.Builder#setItems setItems()} 方法: +

      + +
      +@Override
      +public Dialog onCreateDialog(Bundle savedInstanceState) {
      +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +    builder.setTitle(R.string.pick_color)
      +           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
      +               public void onClick(DialogInterface dialog, int which) {
      +               // The 'which' argument contains the index position
      +               // of the selected item
      +           }
      +    });
      +    return builder.create();
      +}
      +
      + +

      由於清單會出現在對話方塊的內容區塊中,因此對話方塊無法同時顯示訊息和清單,而且您必須使用 {@link android.app.AlertDialog.Builder#setTitle setTitle()} 為對話方塊設定標題。 + +如要指定清單列出的項目,請呼叫 {@link +android.app.AlertDialog.Builder#setItems setItems()} 來傳送陣列。或者,您也可以使用 {@link +android.app.AlertDialog.Builder#setAdapter setAdapter()} 指定清單。 + +如此一來,您就可以使用 {@link android.widget.ListAdapter} 為清單加入動態資料 (例如資料庫中的動態資料)。 +

      + +

      如果您選擇讓清單採用 {@link android.widget.ListAdapter},請一律使用 {@link android.support.v4.content.Loader} 以非同步方式載入內容。 + +詳情請參閱使用配接器建置版面配置載入器指南。 + + +

      + +

      注意:在預設情況下,輕觸某個清單項目會關閉對話方塊,除非您使用以下的永續性選項清單。 +

      + +
      + +

      圖 4. +多重選項清單。

      +
      + + +

      加入永續性的多重選項或單一選項清單

      + +

      如要加入多重選項 (核取方塊) 或單一選項 (圓形按鈕),請使用 +{@link android.app.AlertDialog.Builder#setMultiChoiceItems(Cursor,String,String, +DialogInterface.OnMultiChoiceClickListener) setMultiChoiceItems()} 或 +{@link android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()} 方法。 +

      + +

      例如,以下說明如何建立如圖 4 所示能夠在 {@link java.util.ArrayList} 中儲存所選項目的多重選項清單: + +

      + +
      +@Override
      +public Dialog onCreateDialog(Bundle savedInstanceState) {
      +    mSelectedItems = new ArrayList();  // Where we track the selected items
      +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +    // Set the dialog title
      +    builder.setTitle(R.string.pick_toppings)
      +    // Specify the list array, the items to be selected by default (null for none),
      +    // and the listener through which to receive callbacks when items are selected
      +           .setMultiChoiceItems(R.array.toppings, null,
      +                      new DialogInterface.OnMultiChoiceClickListener() {
      +               @Override
      +               public void onClick(DialogInterface dialog, int which,
      +                       boolean isChecked) {
      +                   if (isChecked) {
      +                       // If the user checked the item, add it to the selected items
      +                       mSelectedItems.add(which);
      +                   } else if (mSelectedItems.contains(which)) {
      +                       // Else, if the item is already in the array, remove it 
      +                       mSelectedItems.remove(Integer.valueOf(which));
      +                   }
      +               }
      +           })
      +    // Set the action buttons
      +           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
      +               @Override
      +               public void onClick(DialogInterface dialog, int id) {
      +                   // User clicked OK, so save the mSelectedItems results somewhere
      +                   // or return them to the component that opened the dialog
      +                   ...
      +               }
      +           })
      +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      +               @Override
      +               public void onClick(DialogInterface dialog, int id) {
      +                   ...
      +               }
      +           });
      +
      +    return builder.create();
      +}
      +
      + +

      雖然傳統清單和包含圓形按鈕的清單都可提供「單選」動作,但如果您想保留使用者的選擇,請使用 {@link +android.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) +setSingleChoiceItems()}。也就是說,如果您想讓對話方塊再次開啟時顯示使用者目前所選的選項,請建立包含圓形按鈕的清單。 + + +

      + + + + + +

      建立自訂版面配置

      + +
      + +

      圖 5.自訂的對話方塊版面配置。

      +
      + +

      如果您想自訂對話方塊的版面配置,請建立所需的版面配置,然後對 {@link +android.app.AlertDialog.Builder} 物件呼叫 {@link +android.app.AlertDialog.Builder#setView setView()},將新建的版面配置加到 {@link android.app.AlertDialog}。 +

      + +

      自訂版面配置預設會佔滿整個對話方塊視窗,但您仍可使用 {@link android.app.AlertDialog.Builder} 方法在其中加入按鈕和標題。 +

      + +

      例如,以下是圖 5 所示的對話方塊版面配置檔案:

      + +

      res/layout/dialog_signin.xml

      +
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +    android:orientation="vertical"
      +    android:layout_width="wrap_content"
      +    android:layout_height="wrap_content">
      +    <ImageView
      +        android:src="@drawable/header_logo"
      +        android:layout_width="match_parent"
      +        android:layout_height="64dp"
      +        android:scaleType="center"
      +        android:background="#FFFFBB33"
      +        android:contentDescription="@string/app_name" />
      +    <EditText
      +        android:id="@+id/username"
      +        android:inputType="textEmailAddress"
      +        android:layout_width="match_parent"
      +        android:layout_height="wrap_content"
      +        android:layout_marginTop="16dp"
      +        android:layout_marginLeft="4dp"
      +        android:layout_marginRight="4dp"
      +        android:layout_marginBottom="4dp"
      +        android:hint="@string/username" />
      +    <EditText
      +        android:id="@+id/password"
      +        android:inputType="textPassword"
      +        android:layout_width="match_parent"
      +        android:layout_height="wrap_content"
      +        android:layout_marginTop="4dp"
      +        android:layout_marginLeft="4dp"
      +        android:layout_marginRight="4dp"
      +        android:layout_marginBottom="16dp"
      +        android:fontFamily="sans-serif"
      +        android:hint="@string/password"/>
      +</LinearLayout>
      +
      + +

      提示:在預設情況下,如果您設定 {@link android.widget.EditText} 元素使用 {@code "textPassword"} 輸入類型,字型系列就會設為等寬字型,因此您必須將該元素的字型系列變更為 {@code "sans-serif"},讓文字欄位採用對應的字型。 + + +

      + +

      如要擴大 {@link android.support.v4.app.DialogFragment} 中的版面配置,請透過 {@link android.app.Activity#getLayoutInflater()} 取得 {@link android.view.LayoutInflater},然後呼叫{@link android.view.LayoutInflater#inflate inflate()} (其中的第一個參數為版面配置資源 ID,第二個參數則是版面配置的上層檢視)。接著,您可以呼叫 {@link android.app.AlertDialog#setView setView()} 來取代對話方塊中的版面配置。 + + + + + +

      + +
      +@Override
      +public Dialog onCreateDialog(Bundle savedInstanceState) {
      +    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +    // Get the layout inflater
      +    LayoutInflater inflater = getActivity().getLayoutInflater();
      +
      +    // Inflate and set the layout for the dialog
      +    // Pass null as the parent view because its going in the dialog layout
      +    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
      +    // Add action buttons
      +           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
      +               @Override
      +               public void onClick(DialogInterface dialog, int id) {
      +                   // sign in the user ...
      +               }
      +           })
      +           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      +               public void onClick(DialogInterface dialog, int id) {
      +                   LoginDialogFragment.this.getDialog().cancel();
      +               }
      +           });      
      +    return builder.create();
      +}
      +
      + +
      +

      提示:如果您想自訂對話方塊,請改為將 {@link android.app.Activity} 顯示為對話方塊,而不是使用 {@link android.app.Dialog} API。 + +方法很簡單,只要建立 Activity 然後在 {@code +<activity>} 宣示說明元素中將其主題設為 +{@link android.R.style#Theme_Holo_Dialog Theme.Holo.Dialog} 即可: +

      + +
      +<activity android:theme="@android:style/Theme.Holo.Dialog" >
      +
      +

      這樣一來,Activity 就會顯示在對話方塊視窗,而不是以全螢幕模式顯示。

      +
      + + + +

      將事件傳回對話方塊的主控件

      + +

      使用者在對話方塊中輕觸任一動作按鈕或從清單中選取一個項目後,您的 {@link android.support.v4.app.DialogFragment} 可能會自行執行必要動作,不過,您通常會想將事件傳送到 Activity 或對話方塊開啟的片段。 + + +如要這麼做,請透過每個點擊事件類型適用的方法定義介面,然後在會接收對話方塊的動作事件的主機元件中實作該介面。 + +

      + +

      例如,以下是定義用於將事件傳回主機 Activity 的介面的 {@link android.support.v4.app.DialogFragment}: +

      + +
      +public class NoticeDialogFragment extends DialogFragment {
      +    
      +    /* The activity that creates an instance of this dialog fragment must
      +     * implement this interface in order to receive event callbacks.
      +     * Each method passes the DialogFragment in case the host needs to query it. */
      +    public interface NoticeDialogListener {
      +        public void onDialogPositiveClick(DialogFragment dialog);
      +        public void onDialogNegativeClick(DialogFragment dialog);
      +    }
      +    
      +    // Use this instance of the interface to deliver action events
      +    NoticeDialogListener mListener;
      +    
      +    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
      +    @Override
      +    public void onAttach(Activity activity) {
      +        super.onAttach(activity);
      +        // Verify that the host activity implements the callback interface
      +        try {
      +            // Instantiate the NoticeDialogListener so we can send events to the host
      +            mListener = (NoticeDialogListener) activity;
      +        } catch (ClassCastException e) {
      +            // The activity doesn't implement the interface, throw exception
      +            throw new ClassCastException(activity.toString()
      +                    + " must implement NoticeDialogListener");
      +        }
      +    }
      +    ...
      +}
      +
      + +

      代管對話方塊的 Activity 會建立包含對話方塊片段的建構函式的對話方塊執行個體,以及透過您實作的 {@code NoticeDialogListener} 介面接收對話方塊的事件: + +

      + +
      +public class MainActivity extends FragmentActivity
      +                          implements NoticeDialogFragment.NoticeDialogListener{
      +    ...
      +    
      +    public void showNoticeDialog() {
      +        // Create an instance of the dialog fragment and show it
      +        DialogFragment dialog = new NoticeDialogFragment();
      +        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
      +    }
      +
      +    // The dialog fragment receives a reference to this Activity through the
      +    // Fragment.onAttach() callback, which it uses to call the following methods
      +    // defined by the NoticeDialogFragment.NoticeDialogListener interface
      +    @Override
      +    public void onDialogPositiveClick(DialogFragment dialog) {
      +        // User touched the dialog's positive button
      +        ...
      +    }
      +
      +    @Override
      +    public void onDialogNegativeClick(DialogFragment dialog) {
      +        // User touched the dialog's negative button
      +        ...
      +    }
      +}
      +
      + +

      由於主機 Activity 會實作 {@code NoticeDialogListener} — 如上所述的 {@link android.support.v4.app.Fragment#onAttach onAttach()} 回呼方法會強制執行這個 Activity — 因此對話方塊片段可以使用介面回呼方法將點擊事件傳送到 Activity: + + +

      + +
      +public class NoticeDialogFragment extends DialogFragment {
      +    ...
      +
      +    @Override
      +    public Dialog onCreateDialog(Bundle savedInstanceState) {
      +        // Build the dialog and set up the button click handlers
      +        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      +        builder.setMessage(R.string.dialog_fire_missiles)
      +               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
      +                   public void onClick(DialogInterface dialog, int id) {
      +                       // Send the positive button event back to the host activity
      +                       mListener.onDialogPositiveClick(NoticeDialogFragment.this);
      +                   }
      +               })
      +               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
      +                   public void onClick(DialogInterface dialog, int id) {
      +                       // Send the negative button event back to the host activity
      +                       mListener.onDialogNegativeClick(NoticeDialogFragment.this);
      +                   }
      +               });
      +        return builder.create();
      +    }
      +}
      +
      + + + +

      顯示對話方塊

      + +

      如果想顯示對話方塊,請建立 {@link +android.support.v4.app.DialogFragment} 執行個體並呼叫 {@link android.support.v4.app.DialogFragment#show +show()} 來傳送對話方塊片段的 {@link android.support.v4.app.FragmentManager} 和標籤名稱。 +

      + +

      如要取得 {@link android.support.v4.app.FragmentManager},請呼叫 +{@link android.support.v4.app.FragmentActivity} 中的 +{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()} 或 {@link +android.support.v4.app.Fragment} 中的 {@link +android.support.v4.app.Fragment#getFragmentManager()}。例如:

      + +
      +public void confirmFireMissiles() {
      +    DialogFragment newFragment = new FireMissilesDialogFragment();
      +    newFragment.show(getSupportFragmentManager(), "missiles");
      +}
      +
      + +

      第二個引數 {@code "missiles"} 是不重複的標籤名稱,可供系統視需要用於儲存及還原片段狀態。 +此外,該標籤還可讓您呼叫 + {@link android.support.v4.app.FragmentManager#findFragmentByTag +findFragmentByTag()} 來取得片段的控點。

      + + + + +

      顯示全螢幕對話方塊或內嵌片段

      + +

      您可能會想建立 UI 設計,讓 UI 片段在某些情況下於該設計中顯示為對話方塊,而不是其他設計中的全螢幕或內嵌片段 (例如視裝置採用螢幕大小而定)。 + +{@link android.support.v4.app.DialogFragment} 類別能為您提供這樣的彈性,這是因為該類別仍可做為可嵌入的 {@link +android.support.v4.app.Fragment} 使用。 +

      + +

      不過,您無法使用 {@link android.app.AlertDialog.Builder AlertDialog.Builder} 或其他 {@link android.app.Dialog} 物件來建置這種對話方塊。 +如果您想建立可嵌入的 {@link android.support.v4.app.DialogFragment},請務必在版面配置中定義對話方塊的 UI,然後透過 {@link android.support.v4.app.DialogFragment#onCreateView +onCreateView()} 回呼載入版面配置。 + + +

      + +

      以下的 {@link android.support.v4.app.DialogFragment} 範例可顯示為對話方塊或可嵌入片段 (使用名為 purchase_items.xml 的版面配置): +

      + +
      +public class CustomDialogFragment extends DialogFragment {
      +    /** The system calls this to get the DialogFragment's layout, regardless
      +        of whether it's being displayed as a dialog or an embedded fragment. */
      +    @Override
      +    public View onCreateView(LayoutInflater inflater, ViewGroup container,
      +            Bundle savedInstanceState) {
      +        // Inflate the layout to use as dialog or embedded fragment
      +        return inflater.inflate(R.layout.purchase_items, container, false);
      +    }
      +  
      +    /** The system calls this only when creating the layout in a dialog. */
      +    @Override
      +    public Dialog onCreateDialog(Bundle savedInstanceState) {
      +        // The only reason you might override this method when using onCreateView() is
      +        // to modify any dialog characteristics. For example, the dialog includes a
      +        // title by default, but your custom layout might not need it. So here you can
      +        // remove the dialog title, but you must call the superclass to get the Dialog.
      +        Dialog dialog = super.onCreateDialog(savedInstanceState);
      +        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
      +        return dialog;
      +    }
      +}
      +
      + +

      以下的程式碼可根據螢幕大小,決定要將片段顯示為對話方塊或全螢幕 UI: +

      + +
      +public void showDialog() {
      +    FragmentManager fragmentManager = getSupportFragmentManager();
      +    CustomDialogFragment newFragment = new CustomDialogFragment();
      +    
      +    if (mIsLargeLayout) {
      +        // The device is using a large layout, so show the fragment as a dialog
      +        newFragment.show(fragmentManager, "dialog");
      +    } else {
      +        // The device is smaller, so show the fragment fullscreen
      +        FragmentTransaction transaction = fragmentManager.beginTransaction();
      +        // For a little polish, specify a transition animation
      +        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
      +        // To make it fullscreen, use the 'content' root view as the container
      +        // for the fragment, which is always the root view for the activity
      +        transaction.add(android.R.id.content, newFragment)
      +                   .addToBackStack(null).commit();
      +    }
      +}
      +
      + +

      如要進一步瞭解如何進行片段交易,請參閱片段指南。 +

      + +

      在本範例中,mIsLargeLayout 布林值可指定是否要讓目前的裝置採用應用程式的大型版面配置設計 (藉此將這個片段顯示為對話方塊,而不是全螢幕)。 + +設定這種布林值的最佳做法,是使用替代資源值為不同裝置大小宣告布林資源值。 + +例如,以下是適用於不同螢幕大小的 2 種布林資源版本: +

      + +

      res/values/bools.xml

      +
      +<!-- Default boolean values -->
      +<resources>
      +    <bool name="large_layout">false</bool>
      +</resources>
      +
      + +

      res/values-large/bools.xml

      +
      +<!-- Large screen boolean values -->
      +<resources>
      +    <bool name="large_layout">true</bool>
      +</resources>
      +
      + +

      接著,您可以在呼叫 Activity 的 {@link android.app.Activity#onCreate onCreate()} 方法時初始化 {@code mIsLargeLayout} 值: +

      + +
      +boolean mIsLargeLayout;
      +
      +@Override
      +public void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +    setContentView(R.layout.activity_main);
      +
      +    mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);
      +}
      +
      + + + +

      針對大型螢幕將 Activity 顯示為對話方塊

      + +

      您可以針對大型螢幕將 {@link android.app.Activity} 顯示為對話方塊,而不是將對話方塊顯示為全螢幕 UI,藉此達到相同結果。 + +您要採用的方法取決於您的應用程式設計,但如果您的應用程式是針對小型螢幕進行設計,將 Activity 顯示為對話方塊通常就能獲得良好效果,而如果您想針對平板電腦改善使用者體驗,請將生命週期較短的 Activity 顯示為對話方塊。 + + +

      + +

      如果只想針對大型螢幕將 Activity 顯示為對話方塊,請為 {@code +<activity>} 宣示元素套用 {@link android.R.style#Theme_Holo_DialogWhenLarge Theme.Holo.DialogWhenLarge} 主題: + +

      + +
      +<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
      +
      + +

      如要進一步瞭解如何運用主題設定 Activity 的樣式,請參閱樣式和主題指南。

      + + + +

      關閉對話方塊

      + +

      只要使用者輕觸任何用 {@link android.app.AlertDialog.Builder} 建立的動作按鈕,系統就會為您關閉對話方塊。 +

      + +

      此外,系統也會在使用者輕觸對話方塊清單中的項目時,關閉對話方塊,但如果清單採用圓形按鈕或核取方塊,系統就不會關閉對話方塊。 +不過,您可以對 {@link +android.support.v4.app.DialogFragment} 呼叫 {@link android.support.v4.app.DialogFragment#dismiss()},藉此手動關閉對話方塊。 +

      + +

      如果您需要在對話方塊關閉時執行特定動作,請在 {@link +android.support.v4.app.DialogFragment} 中實作 {@link +android.support.v4.app.DialogFragment#onDismiss onDismiss()} 方法。 +

      + +

      此外,您還可以「取消」對話方塊。這種特殊事件可用於指明使用者尚未完成工作便關閉對話方塊。 +如果使用者按下 [返回] 按鈕、輕觸對話方塊以外的螢幕畫面,或如果您對 {@link +android.app.Dialog} 呼叫 {@link android.app.Dialog#cancel()} (例如藉此回應對話方塊中的 [取消] 按鈕),就會發生這個事件。 + +

      + +

      如上方範例所示,您可以在 {@link +android.support.v4.app.DialogFragment} 類別中實作 {@link android.support.v4.app.DialogFragment#onCancel onCancel()} 來回應取消事件。 +

      + +

      注意:系統會在發生會呼叫 {@link android.support.v4.app.DialogFragment#onCancel onCancel()} 回呼的事件時呼叫 +{@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}。 +不過,如果您呼叫 {@link android.app.Dialog#dismiss Dialog.dismiss()} 或 {@link +android.support.v4.app.DialogFragment#dismiss DialogFragment.dismiss()},則系統會呼叫 {@link android.support.v4.app.DialogFragment#onDismiss onDismiss()}「而不是」{@link android.support.v4.app.DialogFragment#onCancel onCancel()}。 + + +因此,我們通常建議您在使用者點擊您對話方塊中的「正面」按鈕以便將對話方塊從檢視移除時,呼叫 {@link android.support.v4.app.DialogFragment#dismiss dismiss()}。 + +

      + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd new file mode 100644 index 0000000000000000000000000000000000000000..be1fa7f0dfdab6f9e4efb9881d424524a4f5be57 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/menus.jd @@ -0,0 +1,1031 @@ +page.title=選單 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +
      +
      +

      本文件內容

      +
        +
      1. 在 XML 中定義選單
      2. +
      3. 建立選項選單 +
          +
        1. 處理點擊事件
        2. +
        3. 在執行階段變更選單項目
        4. +
        +
      4. +
      5. 建立內容關聯選單 +
          +
        1. 建立浮動內容選單
        2. +
        3. 使用內容關聯動作模式
        4. +
        +
      6. +
      7. 建立彈出式選單 +
          +
        1. 處理點擊事件
        2. +
        +
      8. +
      9. 建立選單群組 +
          +
        1. 使用可核取的選單項目
        2. +
        +
      10. +
      11. 根據意圖新增選單項目 +
          +
        1. 允許將 Activity 新增至其他選單
        2. +
        +
      12. +
      + +

      重要類別

      +
        +
      1. {@link android.view.Menu}
      2. +
      3. {@link android.view.MenuItem}
      4. +
      5. {@link android.view.ContextMenu}
      6. +
      7. {@link android.view.ActionMode}
      8. +
      + +

      另請參閱

      +
        +
      1. 動作列
      2. +
      3. 選單資源
      4. +
      5. 和選單按鈕說再見 +
      6. +
      +
      +
      + +

      選單在許多類型的應用程式中都是常見的使用者介面元件。為提供熟悉且一致的使用者體驗,您應該使用 +{@link android.view.Menu} API 來呈現 Activity 中的使用者動作與其他選項。 +

      + +

      從 Android 3.0 (API 級別 11) 開始,提供 Android 的裝置不再需要提供專屬的「選單」按鈕。 +有此變更之後,Android 應用程式應可脫離對傳統有 6 個項目的選單面板的依賴,改為提供動作列來呈現一般的使用者動作。 + +

      + +

      雖然有些選單項目的設計與使用者體驗有所變更,但依然是根據 +{@link android.view.Menu} API 來定義一組動作與選項的語意。本指南說明如何在所有版本的 Android 上,建立三個基本類型的選單或動作呈現方式: + +

      + +
      +
      選項選單和動作列
      +
      選項選單是 Activity 的主要選單項目集合。 +您應該將對應用程式有全域影響的動作放置在此,例如「搜尋」、「撰寫電子郵件」及「設定」。 + +

      如果您是為 Android 2.3 以下版本進行開發,使用者可按下「選單」按鈕來顯示選項選單面板。 +

      +

      在 Android 3.0 以上版本,選項選單的項目是當成螢幕上的動作項目與溢出選項組合,以動作列呈現。 +從 Android 3.0 開始,「選單」按鈕已淘汰 (有些裝置甚至沒有),因此您應轉向使用動作列來存取動作和其他選項。 + + +

      +

      請參閱建立選項選單

      +
      + +
      內容選單和內容關聯動作模式
      + +
      內容選單是會在使用者長按某元素時顯示的浮動選單。 +它提供的動作會影響所選取內容或內容畫面。 + +

      針對 Android 3.0 以上版本進行開發時,您應該改為使用關聯比對動作模式,以啟用所選取內容的動作。此模式顯示的動作項目會影響畫面頂端列中選取的內容,並允許使用者選取多個項目。 + +

      +

      請參閱建立內容關聯選單

      +
      + +
      彈出式選單
      +
      彈出式選單顯示的項目清單會以垂直清單的方式,錨定在呼叫該選單的檢視。 +它很適合用來提供與特定內容有關的動作溢出,或針對第二部分的命令提供選項。 +彈出式選單中的動作「不」應直接影響對應內容,應由內容關聯動作直接影響。 + +彈出式選單主要用於您 Activity 中與內容區域相關的延伸動作。 + +

      請參閱建立彈出式選單

      +
      +
      + + + +

      在 XML 中定義選單

      + +

      對於所有選單類型,Android 提供標準 XML 格式來定義選單項目。 +您應該在 XML 選單資源中定義選單與其相關項目,而不是在您 Activity 的程式碼中建置選單。 +接著,您可以擴大 Activity 或片段中的選單資源 (當成 +{@link android.view.Menu} 物件載入)。 +

      + +

      建議使用選單資源的幾個理由如下:

      +
        +
      • 較容易視覺化 XML 中的選單結構。
      • +
      • 可將選單內容和您應用程式的行為程式碼分開。
      • +
      • 可讓您運用應用程式資源架構,為不同的平台版本、螢幕大小及其他設定,建立替代選單設定。 +
      • +
      + +

      如要定義選單,可在專案的 res/menu/ 目錄內建立 XML 檔案,然後利用下列元素建置選單: +

      +
      +
      <menu>
      +
      定義選單項目的容器 {@link android.view.Menu}。<menu> 元素必須是檔案的根節點,並可保留一或多個 +<item><group> 元素。 +
      + +
      <item>
      +
      建立代表選單中單一項目的 {@link android.view.MenuItem}。此元素可以包含巢狀 +<menu> 元素以建立子選單。
      + +
      <group>
      +
      可供 {@code <item>} 元素選用的不可見容器。它可讓您將選單項目分類,以便分享屬性,例如有效狀態與可見度。 +如需詳細資訊,請參閱建立選單群組。 +
      +
      + + +

      稱為 game_menu.xml 的範例選單如下:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<menu xmlns:android="http://schemas.android.com/apk/res/android">
      +    <item android:id="@+id/new_game"
      +          android:icon="@drawable/ic_new_game"
      +          android:title="@string/new_game"
      +          android:showAsAction="ifRoom"/>
      +    <item android:id="@+id/help"
      +          android:icon="@drawable/ic_help"
      +          android:title="@string/help" />
      +</menu>
      +
      + +

      <item> 元素支援數個屬性,您可以用來定義項目的外觀與行為。 +上述選單中的項目包括下列屬性:

      + +
      +
      {@code android:id}
      +
      項目獨有的資源 ID,當使用者選取該項目時可讓應用程式辨識出來。 +
      +
      {@code android:icon}
      +
      要當成項目圖示使用的可繪項目參考資料。
      +
      {@code android:title}
      +
      要當成項目標題使用的字串參考資料。
      +
      {@code android:showAsAction}
      +
      指定此項目應何時且如何顯示為動作列中的動作項目。
      +
      + +

      這些都是您應該使用的最重要屬性,但不侷限於上述這幾個。 +如需所有支援屬性的詳細資訊,請參閱選單資源

      + +

      您可以藉由將 {@code <menu>} 元素新增為 {@code <item>} 的子項,將子選單新增至任何選單 (子選單除外) 的項目。 +當您的應用程式有很多可按主題分類的功能,例如 PC 應用程式選單列中的項目 (檔案、編輯、檢視等等) 時,子選單相當有用。 + +例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<menu xmlns:android="http://schemas.android.com/apk/res/android">
      +    <item android:id="@+id/file"
      +          android:title="@string/file" >
      +        <!-- "file" submenu -->
      +        <menu>
      +            <item android:id="@+id/create_new"
      +                  android:title="@string/create_new" />
      +            <item android:id="@+id/open"
      +                  android:title="@string/open" />
      +        </menu>
      +    </item>
      +</menu>
      +
      + +

      如要在 Activity 中使用選單,您必須使用 {@link android.view.MenuInflater#inflate(int,Menu) +MenuInflater.inflate()} 擴大選單資源 (將 XML 轉換成可程式化的物件)。 +下列各節將說明如何擴大各種 +選單類型的選單。

      + + + +

      建立選項選單

      + +
      + +

      圖 1.Android 2.3 裝置上瀏覽器中的選項選單。 +

      +
      + +

      您應該在選項選單中包括與目前 Activity 內容關聯動作和其他選項,例如「搜尋」、「撰寫電子郵件」及「設定」。 +

      + +

      選項選單中的項目會顯示在螢幕上的哪個位置,取決於您為其開發應用程式的版本。 +

      + +
        +
      • 如果您為「Android 2.3.x (API 級別 10) 以下版本」開發應用程式,當使用者按下[選單] 按鈕,選項選單的內容會顯示在螢幕底部,如圖 1 所示。 + +開啟時,第一個可見部分就是最多可保留六個選單項目的圖示選單。 + +如果您的選單包含六個以上的項目,Android 會將第六個與其餘的項目放入溢出選單,使用者可透過選取[更多] 來開啟。 + +
      • + +
      • 如果您為「Android 3.0 (API 級別 11) 以上版本」開發應用程式,選項選單中的項目會顯示在動作列。 +在預設情況下,系統會將所有項目放入動作溢出,使用者可以利用動作列右側的動作溢出圖示來顯示 (或按裝置的「選單」按鈕,如果有的話)。 + +為了能夠快速存取重要的動作,您可以將 {@code android:showAsAction="ifRoom"} 新增至對應的 {@code <item>} 元素,將要顯示在動作列的幾個項目升階 (請參閱圖 2)。 + + + +

        如需動作項目與其他動作列行為的詳細資訊,請參閱動作列指南。

        +

        注意:即使您「並非」為 Android 3.0 以上版本進行開發,您仍可建置自己的動作列版面配置,達到類似的效果。 +如需如何支援舊版Android 提供動作列的範例,請參閱動作列相容性範例。 + +

        +
      • +
      + + +

      圖 2.Honeycomb 圖片庫應用程式的動作列,顯示導覽索引標籤與相機動作項目 (再加上動作溢出按鈕)。 +

      + +

      您可以從 {@link android.app.Activity} 子類別或 {@link android.app.Fragment} 子類別宣告選項選單的項目。 +如果您的 Activity 與片段都宣告選項選單的項目,兩者會在 UI 中結合。 + +先顯示 Activity 的項目,接著會依片段新增至 Activity 的順序顯示各片段的項目。 +您可以視需要在您想移動的 {@code <item>} 中,利用其 {@code android:orderInCategory}屬性重新排列選單項目的順序。 +

      + +

      如要指定 Activity 的選項選單,可覆寫 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} (片段會提供自己的 +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} 回呼)。在這種方法中,您可以將選單資源(在 XML 中完成定義) 擴大回呼中提供的 {@link +android.view.Menu}。 +例如:

      + +
      +@Override
      +public boolean onCreateOptionsMenu(Menu menu) {
      +    MenuInflater inflater = {@link android.app.Activity#getMenuInflater()};
      +    inflater.inflate(R.menu.game_menu, menu);
      +    return true;
      +}
      +
      + +

      透過 {@link android.view.MenuItem} API,您也可以使用 {@link android.view.Menu#add(int,int,int,int) +add()} 來新增選單項目,以及利用 {@link android.view.Menu#findItem findItem()} 擷取項目來修訂它們的屬性。 +

      + +

      如果您是為 Android 2.3.x 以下版本開發應用程式,當使用者初次開啟選單時,系統會呼叫 {@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()},以建立選項選單。 +如果您是為 Android 3.0 以上版本開發應用程式,啟動 Activity 時,系統會呼叫 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()},以在動作列顯示項目。 +

      + + + +

      處理點擊事件

      + +

      使用者從選項選單 (包括動作列中的動作項目) 中選取項目時,系統會呼叫您 Activity 的 +{@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} 方法。此方法會傳送所選取的 {@link android.view.MenuItem}。您可以呼叫 +{@link android.view.MenuItem#getItemId()} 傳回選單項目的唯一 ID (由選單資源中的 +{@code android:id} 屬性或透過指定給 +{@link android.view.Menu#add(int,int,int,int) add()} 方法的整數來定義) 來識別該項目。您可以將此 ID 和已知選單項目比對,以執行適當動作。 +例如:

      + +
      +@Override
      +public boolean onOptionsItemSelected(MenuItem item) {
      +    // Handle item selection
      +    switch (item.getItemId()) {
      +        case R.id.new_game:
      +            newGame();
      +            return true;
      +        case R.id.help:
      +            showHelp();
      +            return true;
      +        default:
      +            return super.onOptionsItemSelected(item);
      +    }
      +}
      +
      + +

      當您成功處理選單項目時會傳回 {@code true}。如果您不處理選單項目,應該呼叫 +{@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} 的超級類別實作 (預設實作會傳回 false)。 +

      + +

      如果您的 Activity 包含片段,系統會先呼叫 Activity 的 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()},再依片段的新增順序呼叫各片段,直到其中之一傳回 +{@code true} 或所有片段均已呼叫。 +

      + +

      提示:Android 3.0 新增的功能可讓您在 XML 中使用 +{@code android:onClick} 屬性定義選單項目的點擊行為。該屬性值必須是使用選單的 Activity 所定義的方法名稱。 +它必須是公用方法且接受單一 +{@link android.view.MenuItem} 參數,當系統呼叫此方法時,它會傳送選取的選單項目。 +如需詳細資訊與範例,請參閱選單資源文件。

      + +

      提示:如果您的應用程式包含多個 Activity 且有一些提供相同的選項選單,可考慮建立只實作 +{@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 與 {@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()} 方法的 Activity。 +接著,針對應共用相同選項選單的 Activity 擴充此類別。 +如此一來,您可以管理一組處理選單動作的程式碼,以及每一個繼承選單行為的子系類別。 + + +如果您想要將選單項目新增至其中一個子系 Activity,請覆寫該 Activity 中的 {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()}。呼叫 {@code super.onCreateOptionsMenu(menu)} 以建立原始的選單項目,然後利用 +{@link +android.view.Menu#add(int,int,int,int) menu.add()} 來新增選單項目。您也可以覆寫個別選單項目的超級類別行為。 +

      + + +

      在執行階段變更選單項目

      + +

      系統呼叫 {@link android.app.Activity#onCreateOptionsMenu(Menu) +onCreateOptionsMenu()} 之後,會保留您填入的 {@link android.view.Menu} 執行個體,除非該選單因某種理由而變無效,否則不會再次呼叫 +{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}。 +不過,{@link +android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} 應該只用來建立初始選單狀態,您不要在 Activity 生命週期間用來進行變更。 +

      + +

      如果您想要根據 Activity 生命週期當中發生的事件來修改選項選單,您可以使用 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} 方法這麼做。 +這個方法會將目前存在的 +{@link android.view.Menu} 物件傳送給您加以修改,例如新增、移除或停用項目 +(片段也會提供 {@link +android.app.Fragment#onPrepareOptionsMenu onPrepareOptionsMenu()} 回呼)。

      + +

      在 Android 2.3.x 以下版本,每當使用者開啟選項選單 (按下 [選單] +按鈕) 時,系統都會呼叫 {@link +android.app.Activity#onPrepareOptionsMenu(Menu) +onPrepareOptionsMenu()}。

      + +

      在 Android 3.0 以上版本,當選單項目顯示在動作列時會一律將選項選單視為開啟。 +當事件發生且您想要執行選單更新時,您必須呼叫 +{@link android.app.Activity#invalidateOptionsMenu invalidateOptionsMenu()},以要求系統呼叫 +{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}。

      + +

      注意:您不應該根據目前處於焦點狀態的 +{@link android.view.View},變更選項選單中的項目。 +處於輕觸模式 (使用者未使用軌跡球或導覽用的方向鍵) 時,檢視無法成為焦點,因此您不應該使用焦點當成修改選項選單中項目時的依據。 + +如果您想要在 {@link +android.view.View} 提供具內容相關性的選單項目,請使用內容選單

      + + + + +

      建立內容關聯選單

      + +
      + +

      圖 3.浮動內容選單的螢幕擷取畫面 (左) 與內容關聯動作列 (右)。 +

      +
      + +

      內容關聯選單提供的動作會影響 UI 中的特定項或內容畫面。 + +您可以為任何檢視提供內容選單,但它們通常用於使用者能直接對各項目執行動作的 {@link +android.widget.ListView}、{@link android.widget.GridView} 或其他檢視集合中的項目。

      + +

      您有兩種方式可提供內容關聯動作:

      +
        +
      • 浮動內容選單中。當使用者長按 (按住不放) 某個宣告支援內容選單的檢視時,選單會顯示為浮動的選單項目清單 (類似於對話方塊)。 + +使用者一次可對一個項目執行內容關聯動作。 +
      • + +
      • 內容關聯動作模式中。此模式是 +{@link android.view.ActionMode} 的系統實作,在畫面頂端將會影響將所選項目的動作項目顯示在「內容關聯動作列」。 +此模式處於使用中時,使用者能一次對多個項目執行某一動作 (如果您的應用程式允許的話)。 +
      • +
      + +

      注意:Android 3.0 (API 級別 11) 以上版本可以使用內容關聯動作模式,此模式同時是可以顯示內容關聯動作時的偏好技術。 + +如果您的應用程式支援 3.0 以下版本,您應該在這類裝置上切換為浮動內容選單。 +

      + + +

      建立浮動內容選單

      + +

      如何提供浮動內容選單:

      +
        +
      1. 呼叫 {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} 並將 {@link android.view.View} 傳送給它,向應該建立關聯的內容選單註冊 {@link android.view.View}。 + + +

        如果您的 Activity 使用 {@link android.widget.ListView} 或 {@link android.widget.GridView},且您希望每個項目都提供相同的內容選單,可將 {@link android.widget.ListView} 或 {@link android.widget.GridView} 傳送至 {@link +android.app.Activity#registerForContextMenu(View) registerForContextMenu()} 來註冊內容選單的所有項目。 + +

        +
      2. + +
      3. 在您的 {@link android.app.Activity} 或 {@link android.app.Fragment} 中實作 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} 方法。 + +

        當註冊的檢視收到長按事件時,系統會呼叫 {@link +android.view.View.OnCreateContextMenuListener#onCreateContextMenu onCreateContextMenu()} 方法。 +您可以在這裡定義選單項目,方法通常是擴大選單資源。例如: +

        +
        +@Override
        +public void onCreateContextMenu(ContextMenu menu, View v,
        +                                ContextMenuInfo menuInfo) {
        +    super.onCreateContextMenu(menu, v, menuInfo);
        +    MenuInflater inflater = getMenuInflater();
        +    inflater.inflate(R.menu.context_menu, menu);
        +}
        +
        + +

        {@link android.view.MenuInflater} 可讓您從選單資源擴大內容選單。回呼方法參數包括使用者選取的 +{@link android.view.View},以及提供其他所選取項目相關資訊的 {@link android.view.ContextMenu.ContextMenuInfo} 物件。 + +如果 Activity 有數個分別提供不同內容選單的檢視,您可以使用這些參數來決定要擴大的內容選單。 + +

        +
      4. + +
      5. 實作 {@link android.app.Activity#onContextItemSelected(MenuItem) +onContextItemSelected()}。 +

        當使用者選取選單項目時,系統會呼叫此方法,以便您執行適當的動作。 +例如:

        + +
        +@Override
        +public boolean onContextItemSelected(MenuItem item) {
        +    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        +    switch (item.getItemId()) {
        +        case R.id.edit:
        +            editNote(info.id);
        +            return true;
        +        case R.id.delete:
        +            deleteNote(info.id);
        +            return true;
        +        default:
        +            return super.onContextItemSelected(item);
        +    }
        +}
        +
        + +

        {@link android.view.MenuItem#getItemId()} 方法會查詢選定選單項目的 ID,使用 +{@code +android:id} 屬性在 XML 中指派給各個選單項目,如在 XML 中定義選單一節所述。 +

        + +

        當您成功處理選單項目時會傳回 {@code true}。如果您不處理選單項目,請將選單項目傳送至超級類別實作。 +如果您的 Activity 包括片段,該 Activity 會先收到此回呼。 +在不處理時呼叫超級類別,系統就可以一次一個 (依片段的新增順序) 將事件傳送至各片段中的個別回呼方法,直到傳回 +{@code true} 或 {@code false}。 +( +{@link android.app.Activity} 與 {@code android.app.Fragment} 的預設實作會傳回 {@code +false},因此當您不處理時,務必要呼叫超級類別。)

        +
      6. +
      + + +

      使用內容關聯動作模式

      + +

      內容關聯動作模式是 {@link android.view.ActionMode} 的系統實作,執行內容關聯動作時著重於與使用者互動。 +當使用者選取項目而啟用此模式時,「內容關聯動作列」會出現在畫面頂端,呈現使用者可在目前所選項目上執行的動作。 + +此模式啟用時,使用者能選取多個項目 (如果您允許的話)、取消選取項目,還可以在 Activity 內繼續瀏覽 (在您允許的範圍內)。 + +當使用者取消選取所有項目、按下 [後退] 按鈕,或選取該列左側的[完成] 按鈕時,動作模式會停用且內容 關聯 動作列也會消失。 + +

      + +

      注意:內容關聯動作列不一定要與動作列關聯。 +即使內容相關動作列在視覺上覆蓋動作列的位置,兩者依然獨立運作。 + +

      + +

      如果您在為 Android 3.0 (API 級別 11) 以上版本進行開發,您應使用內容關聯動作模式來呈現內容動作,而不是浮動內容選單。 +

      + +

      針對提供內容相關動作的檢視,您應該就下列兩種事件之一 (或兩者) 呼叫內容關聯動作模式: +

      +
        +
      • 使用者長按檢視。
      • +
      • 使用者選取檢視內的核取方塊或類似 UI 元件。
      • +
      + +

      應用程式如何呼叫內容關聯動作模式以及如何定義每個動作的行為,視您的設計而定。 +基本上可分為兩種設計:

      +
        +
      • 在個別的任意檢視上執行內容關聯動作。
      • +
      • 針對 {@link +android.widget.ListView} 或 {@link android.widget.GridView} (允許使用者選取多個項目並對全部執行同一動作) 中的項目群組批次執行內容關聯動作。 +
      • +
      + +

      下列各節說明每種情況所需的設定。

      + + +

      為個別的檢視啟用內容關離動作模式

      + +

      如果您只有在使用者選取特定檢視時,才想要呼叫內容關聯動作模式,建議您採取以下動作: +

      +
        +
      1. 實作 {@link android.view.ActionMode.Callback} 介面。在它的回呼方法中,您可以指定內容相關動作列的動作、回應動作項目的點擊事件,以及處理動作模式的其他生命週期事件。 + +
      2. +
      3. 當您想要顯示該列時 (例如當使用者長按檢視時),可呼叫 {@link android.app.Activity#startActionMode startActionMode()}。 +
      4. +
      + +

      例如:

      + +
        +
      1. 實作 {@link android.view.ActionMode.Callback ActionMode.Callback} 介面: +
        +private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
        +
        +    // Called when the action mode is created; startActionMode() was called
        +    @Override
        +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        +        // Inflate a menu resource providing context menu items
        +        MenuInflater inflater = mode.getMenuInflater();
        +        inflater.inflate(R.menu.context_menu, menu);
        +        return true;
        +    }
        +
        +    // Called each time the action mode is shown. Always called after onCreateActionMode, but
        +    // may be called multiple times if the mode is invalidated.
        +    @Override
        +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        +        return false; // Return false if nothing is done
        +    }
        +
        +    // Called when the user selects a contextual menu item
        +    @Override
        +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        +        switch (item.getItemId()) {
        +            case R.id.menu_share:
        +                shareCurrentItem();
        +                mode.finish(); // Action picked, so close the CAB
        +                return true;
        +            default:
        +                return false;
        +        }
        +    }
        +
        +    // Called when the user exits the action mode
        +    @Override
        +    public void onDestroyActionMode(ActionMode mode) {
        +        mActionMode = null;
        +    }
        +};
        +
        + +

        請注意,這些事件回呼幾乎和選項選單的回呼一模一樣,只是每一個都會傳送與事件關聯的 {@link +android.view.ActionMode} 物件。您可以使用 {@link +android.view.ActionMode} API 對 CAB 進行各種變更,例如使用 +{@link android.view.ActionMode#setTitle setTitle()} 與 {@link +android.view.ActionMode#setSubtitle setSubtitle()} 修改標題與子標題 (指出已選取的項目數量相當有用)。 +

        + +

        同時請注意,當動作模式終結時,上述範例會將 {@code mActionMode} 變數設為 null。 +在下個步驟中,您將看到它是如何初始化,以及如何儲存 Activity 或片段中的成員變數,非常實用。 +

        +
      2. + +
      3. 呼叫 {@link android.app.Activity#startActionMode startActionMode()} (可以的話) 以啟用內容關聯動作模式,例如回應長按 {@link +android.view.View}。 +

        + +
        +someView.setOnLongClickListener(new View.OnLongClickListener() {
        +    // Called when the user long-clicks on someView
        +    public boolean onLongClick(View view) {
        +        if (mActionMode != null) {
        +            return false;
        +        }
        +
        +        // Start the CAB using the ActionMode.Callback defined above
        +        mActionMode = getActivity().startActionMode(mActionModeCallback);
        +        view.setSelected(true);
        +        return true;
        +    }
        +});
        +
        + +

        當您呼叫 {@link android.app.Activity#startActionMode startActionMode()} 時,系統會傳回建立的 +{@link android.view.ActionMode}。只要將此儲存在成員變數中,您就可以變更內容關聯動作列以回應其他事件。 +在上述範例中, +{@link android.view.ActionMode} 是用來確保不會重新建立已在使用中的 {@link android.view.ActionMode} 執行個體,方法是在將動作模式啟動之前,檢查成員是否為 null。 + +

        +
      4. +
      + + + +

      啟用 ListView 或 GridView 中的批次內容相關動作

      + +

      如果您在 {@link android.widget.ListView} 或 {@link +android.widget.GridView} (或 {@link android.widget.AbsListView} 的另一個延伸) 中有一系列項目,且想要允許使用者執行批次動作,請進行以下動作: +

      + +
        +
      • 實作 {@link android.widget.AbsListView.MultiChoiceModeListener} 介面並使用 +{@link android.widget.AbsListView#setMultiChoiceModeListener +setMultiChoiceModeListener()} 加以設定以供檢視群組使用。在接聽器的回呼方法中,您可以指定內容相關動作列的動作、回應動作項目的點擊事件,以及處理從 {@link android.view.ActionMode.Callback} 介面繼承的其他回呼。 + +
      • + +
      • 使用 {@link +android.widget.AbsListView#CHOICE_MODE_MULTIPLE_MODAL} 引數呼叫 {@link android.widget.AbsListView#setChoiceMode setChoiceMode()}。
      • +
      + +

      例如:

      + +
      +ListView listView = getListView();
      +listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
      +listView.setMultiChoiceModeListener(new MultiChoiceModeListener() {
      +
      +    @Override
      +    public void onItemCheckedStateChanged(ActionMode mode, int position,
      +                                          long id, boolean checked) {
      +        // Here you can do something when items are selected/de-selected,
      +        // such as update the title in the CAB
      +    }
      +
      +    @Override
      +    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
      +        // Respond to clicks on the actions in the CAB
      +        switch (item.getItemId()) {
      +            case R.id.menu_delete:
      +                deleteSelectedItems();
      +                mode.finish(); // Action picked, so close the CAB
      +                return true;
      +            default:
      +                return false;
      +        }
      +    }
      +
      +    @Override
      +    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
      +        // Inflate the menu for the CAB
      +        MenuInflater inflater = mode.getMenuInflater();
      +        inflater.inflate(R.menu.context, menu);
      +        return true;
      +    }
      +
      +    @Override
      +    public void onDestroyActionMode(ActionMode mode) {
      +        // Here you can make any necessary updates to the activity when
      +        // the CAB is removed. By default, selected items are deselected/unchecked.
      +    }
      +
      +    @Override
      +    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
      +        // Here you can perform updates to the CAB due to
      +        // an {@link android.view.ActionMode#invalidate} request
      +        return false;
      +    }
      +});
      +
      + +

      這樣一來,當使用者以長按的方式選取項目時,系統就會呼叫 {@link +android.widget.AbsListView.MultiChoiceModeListener#onCreateActionMode onCreateActionMode()} +方法,並顯示包含指定動作的內容相關動作列。可以看見內容關聯動作列時,使用者即可選取其他項目。 +

      + +

      在某些情況下,內容關聯動作提供一般動作項目,因為使用者可能未發現有長按行為,所以您可能希望新增核取方塊或類似的 UI 元素,讓他們選取項目。 + +當使用者選取核取方塊時,您可以使用 {@link android.widget.AbsListView#setItemChecked setItemChecked()} 將個別的清單項目設定成已核取狀態,以呼叫內容關聯動作模式。 + +

      + + + + +

      建立彈出式選單

      + +
      + +

      圖 4.Gmail 應用程式中的彈出式選單錨定於右上角的溢出按鈕。 +

      +
      + +

      {@link android.widget.PopupMenu} 是錨定於 {@link android.view.View} 的強制回應選單。 +有空間的畫會顯示在錨定檢視下方或檢視上方。適合用途:

      +
        +
      • 為與特定內容關聯動作提供溢出樣式選單 (例如 Gmail 的電子郵件標頭,如圖 4 所示)。 + +

        注意:內容選單一般用於會「影響」所選取內容的動作,與這並不相同。 +對於會影響所選取內容的動作,請使用內容關聯動作模式浮動內容選單。 +

      • +
      • 提供命令句的第二部分 (例如,標示為「新增」的按鈕會產生包含不同「新增」選項的彈出式選單)。 +
      • +
      • 提供類似於 {@link android.widget.Spinner} (不保留永續性選擇) 的下拉式清單。 +
      • +
      + + +

      注意: API 級別 11 以上版本才可以使用 {@link android.widget.PopupMenu}。 +

      + +

      如果您在 XML 中定義選單,以下說明如何顯示彈出式選單:

      +
        +
      1. 透過其建構函式將 {@link android.widget.PopupMenu} 具現化,使用應錨定選單的目前應用程式 +{@link android.content.Context} 與 {@link android.view.View}。 +
      2. +
      3. 使用 {@link android.view.MenuInflater} 將您的選單資源擴大成 +{@link +android.widget.PopupMenu#getMenu() PopupMenu.getMenu()} 所傳回的 {@link android.view.Menu} 物件。針對 14 以上的 API 級別,您可以改用 +{@link android.widget.PopupMenu#inflate PopupMenu.inflate()}。
      4. +
      5. 呼叫 {@link android.widget.PopupMenu#show() PopupMenu.show()}。
      6. +
      + +

      例如,以下是含有 {@link android.R.attr#onClick android:onClick} 屬性可顯示彈出式選單的按鈕。 +

      + +
      +<ImageButton
      +    android:layout_width="wrap_content" 
      +    android:layout_height="wrap_content" 
      +    android:src="@drawable/ic_overflow_holo_dark"
      +    android:contentDescription="@string/descr_overflow_button"
      +    android:onClick="showPopup" />
      +
      + +

      接著,該 Activity 可以顯示彈出式選單,如下所示:

      + +
      +public void showPopup(View v) {
      +    PopupMenu popup = new PopupMenu(this, v);
      +    MenuInflater inflater = popup.getMenuInflater();
      +    inflater.inflate(R.menu.actions, popup.getMenu());
      +    popup.show();
      +}
      +
      + +

      在 14 以上的 API 級別中,您可以利用 {@link +android.widget.PopupMenu#inflate PopupMenu.inflate()} 將兩行結合來擴大選單。

      + +

      當使用者選取某項目或輕觸選單區域外時會關閉選單。 +您可以使用 {@link +android.widget.PopupMenu.OnDismissListener} 來接聽關閉事件。

      + +

      處理點擊事件

      + +

      如要在使用者選取選單項目時執行動作,您必須呼叫 {@link android.widget.PopupMenu#setOnMenuItemClickListener +setOnMenuItemclickListener()} 來實作 {@link +android.widget.PopupMenu.OnMenuItemClickListener} 介面並向您的 {@link +android.widget.PopupMenu} 註冊。 +當使用者選取項目時,系統會在您的介面中呼叫 {@link +android.widget.PopupMenu.OnMenuItemClickListener#onMenuItemClick onMenuItemClick()} 回呼。 +

      + +

      例如:

      + +
      +public void showMenu(View v) {
      +    PopupMenu popup = new PopupMenu(this, v);
      +
      +    // This activity implements OnMenuItemClickListener
      +    popup.setOnMenuItemClickListener(this);
      +    popup.inflate(R.menu.actions);
      +    popup.show();
      +}
      +
      +@Override
      +public boolean onMenuItemClick(MenuItem item) {
      +    switch (item.getItemId()) {
      +        case R.id.archive:
      +            archive(item);
      +            return true;
      +        case R.id.delete:
      +            delete(item);
      +            return true;
      +        default:
      +            return false;
      +    }
      +}
      +
      + + +

      建立選單群組

      + +

      選單群組是指某些特性相同的選單項目集合。您可以利用群組: +

      +
        +
      • 透過 {@link android.view.Menu#setGroupVisible(int,boolean) +setGroupVisible()} 來顯示或隱藏所有項目
      • +
      • 透過 {@link android.view.Menu#setGroupEnabled(int,boolean) +setGroupEnabled()} 來啟用或停用所有項目
      • +
      • 透過 {@link +android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()} 指定是否可核取所有項目
      • +
      + +

      您可以將 {@code <item>} 元素堆疊在選單資源中的 {@code <group>}元素內,或使用 {@link +android.view.Menu#add(int,int,int,int) add()} 方法指定群組 ID 來建立群組。 +

      + +

      以下是包含群組的範例選單資源:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<menu xmlns:android="http://schemas.android.com/apk/res/android">
      +    <item android:id="@+id/menu_save"
      +          android:icon="@drawable/menu_save"
      +          android:title="@string/menu_save" />
      +    <!-- menu group -->
      +    <group android:id="@+id/group_delete">
      +        <item android:id="@+id/menu_archive"
      +              android:title="@string/menu_archive" />
      +        <item android:id="@+id/menu_delete"
      +              android:title="@string/menu_delete" />
      +    </group>
      +</menu>
      +
      + +

      群組中的項目和第一個項目都會顯示在同一層,選單中的這三個項目都屬於同層級。 +不過,您可以參考群組 ID 並使用上方所述的方法,修改群組當中兩個項目的特性。 +系統也絕不會將群組的項目分離。 +例如,如果您針對每個項目宣告 {@code +android:showAsAction="ifRoom"},它們都會顯示在動作列中或顯示在動作溢出中。 +

      + + +

      使用可勾選的選單項目

      + +
      + +

      圖 5.包含可勾選項目的子選單螢幕擷取畫面。 +

      +
      + +

      對於獨立選項使用核取方塊,或對互斥選項群組使用選項按鈕,將選單當成開啟或關閉選項的介面,相當實用。 + +圖 5 顯示的子選單包含能以圓形按鈕勾選的項目。 +

      + +

      注意:圖示選單 (出自選項選單) 中的選單項目無法顯示核取方塊或選項按鈕。 +如果您選擇要將圖示選單中的項目設為可勾選,每當狀態變更時,您都必須切換圖示和/或文字,手動指出勾選狀態。 + +

      + +

      您可以使用 {@code <item>} 元素中的 {@code +android:checkable} 屬性,為個別的選單項目定義可勾選行為,或利用 {@code <group>} 元素中的 {@code android:checkableBehavior} 屬性來為整個群組定義。 +例如,此選單群組中的所有項目都可以利用選項按鈕勾選。 +

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<menu xmlns:android="http://schemas.android.com/apk/res/android">
      +    <group android:checkableBehavior="single">
      +        <item android:id="@+id/red"
      +              android:title="@string/red" />
      +        <item android:id="@+id/blue"
      +              android:title="@string/blue" />
      +    </group>
      +</menu>
      +
      + +

      {@code android:checkableBehavior} 屬性可接受其中一項: +

      +
      {@code single}
      +
      只能勾選群組中的一個項目 (圓形按鈕)
      +
      {@code all}
      +
      可勾選所有項目 (核取方塊)
      +
      {@code none}
      +
      沒有任何項目可供勾選
      +
      + +

      您可以使用 +{@code <item>} 元素中的 {@code android:checked} 屬性將預設的勾選狀態套用至項目,並使用 {@link +android.view.MenuItem#setChecked(boolean) setChecked()} 方法在程式碼中變更它。

      + +

      已選取可勾選項目時,系統會呼叫所選取個別項目的回呼方法 (例如 {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()})。 +您必須在這裡設定核取方塊的狀態,原因是核取方塊或選項按鈕並不會自動變更其狀態。 + +您可以利用 +{@link android.view.MenuItem#isChecked()} 來查詢項目的目前狀態 (使用者選取它之前的狀態),然後利用 +{@link android.view.MenuItem#setChecked(boolean) setChecked()} 來設定勾選狀態。例如:

      + +
      +@Override
      +public boolean onOptionsItemSelected(MenuItem item) {
      +    switch (item.getItemId()) {
      +        case R.id.vibrate:
      +        case R.id.dont_vibrate:
      +            if (item.isChecked()) item.setChecked(false);
      +            else item.setChecked(true);
      +            return true;
      +        default:
      +            return super.onOptionsItemSelected(item);
      +    }
      +}
      +
      + +

      如果您不以這種方式設定已勾選狀態,將無法在使用者選取時變更項目 (核取方塊或選項按鈕) 的可見狀態。 + +當您確實設定狀態,Activity 會保留項目的已勾選狀態,當使用者稍後開啟選單時,即可看見您設定的已勾選狀態。 + +

      + +

      注意:可勾選的選單項目只能在各工作階段上使用,並不會在應用程式終結後儲存。 + +如果想為使用者儲存特定應用程式設定,請使用共用偏好設定。 +

      + + + +

      根據意圖新增選單項目

      + +

      有時候您會希望選單項目使用 {@link android.content.Intent}來啟動 Activity (不論是您的應用程式或另一應用程式中的 Activity)。 +當您知道想要使用的意圖,同時有應繼承該意圖的特定選單項目時,您可在使用者選取項目時才回呼的適當方法實作期間使用 +{@link android.app.Activity#startActivity(Intent) startActivity()} 執行該意圖 (例如 {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} callback) 回呼)。 + +

      + +

      不過,如果您不確定使用者的裝置是否包含可處理該意圖的應用程式,而新增可呼叫它的選單項目會導致選單項目無法運作,原因可能是該意圖不會解析成 Activity。 + + +為解決這個問題,當 Android 在處理您意圖的裝置上找到 Activity 時,Android 會讓您將選單項目動態新增至選單。 +

      + +

      如何根據接受意圖的可用 Activity 來新增選單項目:

      +
        +
      1. 定義類別為 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} 和/或 +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 的意圖,再加上其他需求。
      2. +
      3. 呼叫 {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +Menu.addIntentOptions()}。Android 接著會搜尋可執行該意圖的任何應用程式,並將其新增至您的選單。 +
      4. +
      + +

      如果未安裝可滿足該意圖的應用程式,則不會新增任何選單項目。 +

      + +

      注意: +{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 是用來處理畫面上目前選取的元素。 +因此,請只在 {@link +android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) +onCreateContextMenu()} 中建立選單的情況下才使用。

      + +

      例如:

      + +
      +@Override
      +public boolean onCreateOptionsMenu(Menu menu){
      +    super.onCreateOptionsMenu(menu);
      +
      +    // Create an Intent that describes the requirements to fulfill, to be included
      +    // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
      +    Intent intent = new Intent(null, dataUri);
      +    intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
      +
      +    // Search and populate the menu with acceptable offering applications.
      +    menu.addIntentOptions(
      +         R.id.intent_group,  // Menu group to which new items will be added
      +         0,      // Unique item ID (none)
      +         0,      // Order for the items (none)
      +         this.getComponentName(),   // The current activity name
      +         null,   // Specific items to place first (none)
      +         intent, // Intent created above that describes our requirements
      +         0,      // Additional flags to control items (none)
      +         null);  // Array of MenuItems that correlate to specific items (none)
      +
      +    return true;
      +}
      + +

      找到的各個 Activity 提供的意圖篩選器如果與定義的意圖相符,就會新增選單項目,使用意圖篩選器的 android:label 中的值當成選單項目標題,並將應用程式圖示當成選單項目圖示。 + +此外, +{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} 方法還會傳回新增的選單項目數量。

      + +

      注意:當您呼叫 {@link +android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) +addIntentOptions()} 時,它會覆寫第一個引數中所指定選單群組中的任何或所有選單項目。 +

      + + +

      允許將 Activity 新增至其他選單

      + +

      您也能向其他應用程式提供 Activity 的服務,這樣即可在其他應用程式的選單中包含您的應用程式 (與上述的角色顛倒)。 +

      + +

      如要包含在其他應用程式選單中,您必須照常定義意圖篩選器,但務必為意圖篩選器類別納入 +{@link android.content.Intent#CATEGORY_ALTERNATIVE} 和/或 {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} 值。 + +例如:

      +
      +<intent-filter label="@string/resize_image">
      +    ...
      +    <category android:name="android.intent.category.ALTERNATIVE" />
      +    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
      +    ...
      +</intent-filter>
      +
      + +

      如要進一步瞭解如何編寫意圖篩選器,請參閱意圖和意圖篩選器。 +

      + +

      如需採用此技術的範例應用程式,請參閱 NotePad 範例程式碼。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd new file mode 100644 index 0000000000000000000000000000000000000000..b8537445cff01b27877887ac5823a84a2c01e9ab --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/notifiers/notifications.jd @@ -0,0 +1,979 @@ +page.title=通知 +@jd:body + + +

      + 通知是您應用程式的一般 UI 以外,可以向使用者顯示的訊息。當您告訴系統發出通知時,它會先在「通知區域」顯示為圖示。 + +使用者可開啟「通知匣」來查看通知的詳細資料。 +通知區域與通知匣都是使用者可隨時查看的系統控制區域。 + +

      + +

      + 圖 1.通知區域中的通知。 +

      + +

      + 圖 2.通知匣中的通知。 +

      + +

      注意:除非另外註明,否則本指南參照支援程式庫 4 版中的 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 類別。類別 {@link android.app.Notification.Builder Notification.Builder} 是在 Android 3.0 (API 級別 11) 新增。 + + +

      + +

      設計注意事項

      + +

      做為 Android 使用者介面重要部分,通知有自己的設計指導方針。 +在 Android 5.0 (API 級別 21) 導入的質感設計變更,更是特別重要。 +如需詳情,請參閱質感設計。 +如要瞭解如何設計通知與其互動,請參閱通知設計指南。 +

      + +

      建立通知

      + +

      您會為 +{@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 物件中的通知指定 UI 資訊與動作。 +如要建立通知本身,您可以呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#build NotificationCompat.Builder.build()},其傳回的 +{@link android.app.Notification} 物件會包含您的規格。如要發出通知,您可以呼叫 {@link android.app.NotificationManager#notify NotificationManager.notify()} 將 {@link android.app.Notification} 物件傳送至系統。 + +

      + +

      必要的通知內容

      +

      + {@link android.app.Notification} 物件「必須」包含下列項目: +

      +
        +
      • + 小圖示,由 +{@link android.support.v4.app.NotificationCompat.Builder#setSmallIcon setSmallIcon()} 設定 +
      • +
      • + 標題,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} 設定 +
      • +
      • + 詳情文字,由 +{@link android.support.v4.app.NotificationCompat.Builder#setContentText setContentText()} 設定 +
      • +
      +

      選用的通知內容和設定

      +

      + 所有其他的通知設定與內容均為選用。如需詳情,請參閱 +{@link android.support.v4.app.NotificationCompat.Builder} 的參考文件。 +

      + +

      通知動作

      +

      + 動作雖屬選用性質,但您至少必須將一個動作新增至通知。 + 動作可讓使用者從通知直接移至您應用程式中的 +{@link android.app.Activity},他們能在那裡查看一或多個事件或執行進一步工作。 + +

      +

      + 通知可以提供多個動作。您應該一律定義會在使用者按一下通知時觸發的動作。 +這個動作通常會開啟您應用程式中的 +{@link android.app.Activity}。您也可以將按鈕新增至通知來執行其他動作,例如延遲鬧鐘或立即回應文字訊息。自 Android 4.1 起才提供此功能。 + +如果您使用其他動作按鈕,也務必要在您應用程式的 {@link android.app.Activity} 中提供此功能。 + +如需詳情,請參閱處理相容性。 +

      +

      + 在 {@link android.app.Notification} 內,動作本身是由 +{@link android.app.PendingIntent} 完成定義,其中包含的 +{@link android.content.Intent} 會啟動您應用程式中的 +{@link android.app.Activity}。如要將 +{@link android.app.PendingIntent} 與手勢關聯,可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder} 的適當方法。例如,如果當使用者按一下通知匣中的通知文字時,您希望啟動 +{@link android.app.Activity},可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent setContentIntent()} 來新增 {@link android.app.PendingIntent}。 + +

      +

      + 最常見的動作情況就是在使用者按一下通知時啟動 {@link android.app.Activity}。 +您也可以在使用者關閉通知時啟動 {@link android.app.Activity}。 +在 Android 4.1 以上版本中,您可以透過動作按鈕啟動 +{@link android.app.Activity}。如需詳情,請參閱 +{@link android.support.v4.app.NotificationCompat.Builder} 的參考指南。 +

      + +

      通知優先順序

      +

      + 如有需要,您可以設定通知的優先順序。優先順序就像是提示一樣,可讓裝置 UI 知道該如何顯示通知。 + + 如要設定通知的優先順序,可呼叫 {@link +android.support.v4.app.NotificationCompat.Builder#setPriority(int) +NotificationCompat.Builder.setPriority()},然後傳入其中一個 {@link +android.support.v4.app.NotificationCompat} 優先順序常數。共有五個優先順序級別,範圍從 +{@link +android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) 到 {@link +android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2)。如未加以設定,優先順序會預設為 +{@link +android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0)。 +

      +

      如要瞭解如何設定適當的優先順序級別,請參閱通知設計指南中的「正確地設定及管理通知優先順序」。 + + +

      + +

      建立簡易通知

      +

      + 下列程式碼片段說明簡易的通知,指定要在使用者按一下通知時開啟的 Activity。 +請注意,以下程式碼會建立 +{@link android.support.v4.app.TaskStackBuilder} 物件,並使用此物件建立動作的 +{@link android.app.PendingIntent}。如要進一步瞭解這種模式,請參閱啟動 Activity 時保留導覽: + + +

      +
      +NotificationCompat.Builder mBuilder =
      +        new NotificationCompat.Builder(this)
      +        .setSmallIcon(R.drawable.notification_icon)
      +        .setContentTitle("My notification")
      +        .setContentText("Hello World!");
      +// Creates an explicit intent for an Activity in your app
      +Intent resultIntent = new Intent(this, ResultActivity.class);
      +
      +// The stack builder object will contain an artificial back stack for the
      +// started Activity.
      +// This ensures that navigating backward from the Activity leads out of
      +// your application to the Home screen.
      +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
      +// Adds the back stack for the Intent (but not the Intent itself)
      +stackBuilder.addParentStack(ResultActivity.class);
      +// Adds the Intent that starts the Activity to the top of the stack
      +stackBuilder.addNextIntent(resultIntent);
      +PendingIntent resultPendingIntent =
      +        stackBuilder.getPendingIntent(
      +            0,
      +            PendingIntent.FLAG_UPDATE_CURRENT
      +        );
      +mBuilder.setContentIntent(resultPendingIntent);
      +NotificationManager mNotificationManager =
      +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +// mId allows you to update the notification later on.
      +mNotificationManager.notify(mId, mBuilder.build());
      +
      +

      這樣一來,您的使用者就會收到通知。

      + +

      將擴充版面配置套用至通知

      +

      + 如要讓通知顯示在擴充的檢視中,請先建立含有您所需一般檢視選項的 +{@link android.support.v4.app.NotificationCompat.Builder} 物件。 +接著,將擴充版面配置物件當成引數,呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setStyle +Builder.setStyle()}。 +

      +

      + 請記住,Android 4.1 以下版本平台不支援擴充通知。如要進一步瞭解如何處理 Android 4.1 以下版本平台的通知,請參閱處理相容性。 + + +

      +

      + 例如,下列程式碼片段示範如何更改上述程式碼片段中建立的通知,以使用擴充版面配置: + +

      +
      +NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
      +    .setSmallIcon(R.drawable.notification_icon)
      +    .setContentTitle("Event tracker")
      +    .setContentText("Events received")
      +NotificationCompat.InboxStyle inboxStyle =
      +        new NotificationCompat.InboxStyle();
      +String[] events = new String[6];
      +// Sets a title for the Inbox in expanded layout
      +inboxStyle.setBigContentTitle("Event tracker details:");
      +...
      +// Moves events into the expanded layout
      +for (int i=0; i < events.length; i++) {
      +
      +    inboxStyle.addLine(events[i]);
      +}
      +// Moves the expanded layout object into the notification object.
      +mBuilder.setStyle(inBoxStyle);
      +...
      +// Issue the notification here.
      +
      + +

      處理相容性

      + +

      + 即使支援程式庫類別 {@link android.support.v4.app.NotificationCompat.Builder NotificationCompat.Builder} 中有設定這些功能的方法,特定版本也不一定能使用所有通知功能。 + + + 例如,取決於擴充通知的動作按鈕,只能在 Android 4.1 以上版本中顯示,原因是擴充通知本身只能在 Android 4.1 以上版本中使用。 + + +

      +

      + 為確保能有最佳相容性,可利用 +{@link android.support.v4.app.NotificationCompat NotificationCompat} 與其子類別來建立通知,特別是 +{@link android.support.v4.app.NotificationCompat.Builder +NotificationCompat.Builder}。此外,實作通知時,請按照下列程序操作: +

      +
        +
      1. + 不論使用者所用的版本為何,請向所有使用者提供所有所有通知功能。 +如要這麼做,請驗證可從您應用程式的 +{@link android.app.Activity} 使用所有功能。您可能會想要新增 +{@link android.app.Activity} 來執行此動作。 +

        + 例如,如果您想要使用 +{@link android.support.v4.app.NotificationCompat.Builder#addAction addAction()} + 以提供可停止和開始媒體播放的控制項,可先在您應用程式的 +{@link android.app.Activity} 中實作此控制項。 +

        +
      2. +
      3. + 藉由在使用者按一下通知時啟動功能,可確保所有使用者都能使用 {@link android.app.Activity} 中的功能。 +如要這樣做,請為 {@link android.app.Activity} 建立 {@link android.app.PendingIntent}。 + +呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 以將 {@link android.app.PendingIntent} 新增至通知。 +
      4. +
      5. + 現在,可以將您想要使用的擴充通知功能新增至通知。請記住,您新增的任何功能也必須能在使用者點選通知時所啟動的 +{@link android.app.Activity} 中使用。 + +
      6. +
      + + + + +

      管理通知

      +

      + 當您必須對相同類型的事件發出多次通知時,請避免建立全新的通知。 +您應該考慮變更或新增 (或兩者) 就有通知的某些值來加以更新。 + +

      +

      + 例如,Gmail 藉由新增其未讀訊息計數並將每封電子郵件的摘要新增至通知,藉此通知使用者新電子郵件已送達。 +而這稱為「堆疊」通知。詳情請參閱通知設計指南。 + + +

      +

      + 注意:此 Gmail 功能需要「收件匣」擴充版面配置,也屬於 Android 4.1 以上版本才能使用的擴充通知功能。 + +

      +

      + 以下各節說明如何更新及移除通知。 +

      +

      更新通知

      +

      + 藉由呼叫 +{@link android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()} 連同通知 ID 一起發出,即可將通知設定成可以更新。 + 如要在發出此通知後加以更新,可以更新或建立 +{@link android.support.v4.app.NotificationCompat.Builder} 物件,從中建置 +{@link android.app.Notification} 物件,並將 +{@link android.app.Notification} 連同您先前使用的相同 ID 一起發出。如果仍可看見先前的通知,系統會更新 {@link android.app.Notification} 物件內容中的通知。 + +如果已關閉先前的通知,會改為建立新的通知。 + +

      +

      + 以下程式碼片段示範如何更新通知以反映發生的事件數目。 +以堆疊通知的方式顯示摘要: +

      +
      +mNotificationManager =
      +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +// Sets an ID for the notification, so it can be updated
      +int notifyID = 1;
      +mNotifyBuilder = new NotificationCompat.Builder(this)
      +    .setContentTitle("New Message")
      +    .setContentText("You've received new messages.")
      +    .setSmallIcon(R.drawable.ic_notify_status)
      +numMessages = 0;
      +// Start of a loop that processes data and then notifies the user
      +...
      +    mNotifyBuilder.setContentText(currentText)
      +        .setNumber(++numMessages);
      +    // Because the ID remains unchanged, the existing notification is
      +    // updated.
      +    mNotificationManager.notify(
      +            notifyID,
      +            mNotifyBuilder.build());
      +...
      +
      + + +

      移除通知

      +

      + 下列其中一個情況發生之前,都會看見通知: +

      +
        +
      • + 使用者個別關閉通知,或透過「全部清除」的方式關閉通知 (如果可以清除通知的話)。 + +
      • +
      • + 使用者按一下通知,而您在建立通知時呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setAutoCancel setAutoCancel()}。 + +
      • +
      • + 您對特定通知 ID 呼叫 {@link android.app.NotificationManager#cancel(int) cancel()}。這種方法也會刪除進行中的通知。 + +
      • +
      • + 您呼叫 {@link android.app.NotificationManager#cancelAll() cancelAll()},將先前發出的所有通知移除。 + +
      • +
      + + +

      啟動 Activity 時保留導覽

      +

      + 當您從通知啟動 {@link android.app.Activity} 時,您必須保留使用者預期的導覽體驗。 +按一下 [返回] 應可將使用者從應用程式的一般工作流程帶回主螢幕,而按一下 + [近期記錄] 應會將 +{@link android.app.Activity} 顯示為個別的工作。如要保留導覽體驗,請以全新的工作啟動 +{@link android.app.Activity}。您該如何設定 +{@link android.app.PendingIntent} 以取得全新工作,取決於即將啟動的 +{@link android.app.Activity} 屬性。一般有兩種情況: +

      +
      +
      + 一般 Activity +
      +
      + 您即將啟動的 {@link android.app.Activity} 屬於應用程式的一般工作流程。 +在這種情況下,設定 {@link android.app.PendingIntent}以啟動全新工作,再連同返回堆疊一起提供 {@link android.app.PendingIntent},可再次產生應用程式的一般 + + 返回 行為。 +

      + Gmail 應用程式的通知可示範這種情況。當您點選某一封電子郵件訊息的通知後,您會看到訊息本身。 +輕觸 [返回]可將您從 Gmail 帶回主螢幕,就像您剛才從主螢幕進入 Gmail,而不是從通知進入。 + + +

      +

      + 不論您所在的應用程式為何,當您輕觸通知時,都會發生這種情況。 +例如,如果您在 Gmail 撰寫訊息,而您點選某一封電子郵件的通知,就會立即前往該封電子郵件。 +輕觸 [返回] + 會將您帶往收件匣,然後再到主螢幕,而不是到您正在撰寫的訊息。 + +

      +
      +
      + 特殊 Activity +
      +
      + 只有從通知啟動這個 {@link android.app.Activity} 時,使用者才會看到它。 + 在某種意義上,{@link android.app.Activity}藉由提供難以在通知本身顯示的資訊來擴充通知。 +針對這種情況,請設定 +{@link android.app.PendingIntent} 以全新工作啟動。您不必建立返回堆疊,原因是啟動的 +{@link android.app.Activity} 不屬於應用程式的 Activity 流程。 +按一下 [返回] 還是會將使用者帶回主螢幕。 + +
      +
      + +

      設定一般 Activity PendingIntent

      +

      + 如要設定會啟動直接項目 +{@link android.app.Activity} 的 {@link android.app.PendingIntent},請按照下列步驟操作: +

      +
        +
      1. + 在宣示說明中定義您應用程式的 {@link android.app.Activity} 階層。 +
          +
        1. + 新增 Android 4.0.3 以下版本的支援。如要這樣做,請將 +<meta-data> 元素新增為 +<activity> 的下層物件,以指定您要啟動 +{@link android.app.Activity} 的上層物件。 + +

          + 針對此元素設定 +android:name="android.support.PARENT_ACTIVITY"。 + 設定 +android:value="<parent_activity_name>",其中 <parent_activity_name> 是上層 +<activity> 元素的 +android:name 值。 + + +如需範例,請參閱下列 XML。 +

          +
        2. +
        3. + 此外您還需要新增 Android 4.1 以上版本的支援。為此,請將 +android:parentActivityName 屬性新增到您要啟動 {@link android.app.Activity} 的 +<activity> 元素。 + + +
        4. +
        +

        + 最後的 XML 看起來會如下所示: +

        +
        +<activity
        +    android:name=".MainActivity"
        +    android:label="@string/app_name" >
        +    <intent-filter>
        +        <action android:name="android.intent.action.MAIN" />
        +        <category android:name="android.intent.category.LAUNCHER" />
        +    </intent-filter>
        +</activity>
        +<activity
        +    android:name=".ResultActivity"
        +    android:parentActivityName=".MainActivity">
        +    <meta-data
        +        android:name="android.support.PARENT_ACTIVITY"
        +        android:value=".MainActivity"/>
        +</activity>
        +
        +
      2. +
      3. + 根據啟動 +{@link android.app.Activity} 的 {@link android.content.Intent} 建立返回堆疊: +
          +
        1. + 建立 {@link android.content.Intent} 以啟動 {@link android.app.Activity}。 +
        2. +
        3. + 呼叫 {@link android.app.TaskStackBuilder#create +TaskStackBuilder.create()} 來建立堆疊建立器。 +
        4. +
        5. + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()} 將返回堆疊新增至堆疊建立器。 + 對於您在宣示說明中所定義階層中的每個 {@link android.app.Activity} +而言,返回堆疊包含會啟動 +{@link android.app.Activity} 的 {@link android.content.Intent} 物件。此方法也會新增以全新工作啟動堆疊的旗標。 + +

          + 注意:雖然 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}的引數是參考啟動的 {@link android.app.Activity},但呼叫的方法不會新增會啟動 +{@link android.app.Activity} 的 +{@link android.content.Intent}, +這項工作是在下個步驟中完成。 +

          +
        6. +
        7. + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()} 來新增會從通知啟動 {@link android.app.Activity} 的 {@link android.content.Intent}。 + + 將您在第一個步驟中建立的 {@link android.content.Intent} 當成 +{@link android.support.v4.app.TaskStackBuilder#addNextIntent addNextIntent()} 的引數傳送。 + +
        8. +
        9. + 如有必要,呼叫 +{@link android.support.v4.app.TaskStackBuilder#editIntentAt +TaskStackBuilder.editIntentAt()} 在堆疊上將引數新增至 {@link android.content.Intent} 物件。當使用者使用以下方式導覽時,有必要確保目標 +{@link android.app.Activity} 可顯示有意義的資料: + 返回。 +
        10. +
        11. + 呼叫 +{@link android.support.v4.app.TaskStackBuilder#getPendingIntent getPendingIntent()} 來取得 {@link android.app.PendingIntent} 以用於此返回堆疊。 + 接著,您可以使用這個 {@link android.app.PendingIntent} 當成 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 的引數。 +
        12. +
        +
      4. +
      +

      + 以下程式碼片段會示範程序: +

      +
      +...
      +Intent resultIntent = new Intent(this, ResultActivity.class);
      +TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
      +// Adds the back stack
      +stackBuilder.addParentStack(ResultActivity.class);
      +// Adds the Intent to the top of the stack
      +stackBuilder.addNextIntent(resultIntent);
      +// Gets a PendingIntent containing the entire back stack
      +PendingIntent resultPendingIntent =
      +        stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
      +...
      +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
      +builder.setContentIntent(resultPendingIntent);
      +NotificationManager mNotificationManager =
      +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +mNotificationManager.notify(id, builder.build());
      +
      + +

      設定特殊 Activity PendingIntent

      +

      + 下一節說明如何設定特殊 Activity +{@link android.app.PendingIntent}。 +

      +

      + 特殊 {@link android.app.Activity} 不需要返回堆疊,因此您不必在宣示說明中定義其 +{@link android.app.Activity} 階層,也不用呼叫 +{@link android.support.v4.app.TaskStackBuilder#addParentStack addParentStack()}來建置返回堆疊。 + +而是使用宣示說明設定 {@link android.app.Activity} 工作選項,並呼叫 +{@link android.app.PendingIntent#getActivity getActivity()} 來建立 {@link android.app.PendingIntent}: + +

      +
        +
      1. + 在您的宣示說明中,將下列屬性新增至 {@link android.app.Activity} 的 +<activity> 元素 + +
        +
        +android:name="activityclass" +
        +
        + Activity 的完整類別名稱。 +
        +
        +android:taskAffinity="" +
        +
        + 結合您在程式碼中設定的 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} 旗標,可確保此 {@link android.app.Activity} 不會前往應用程式的預設工作。 + +具有應用程式預設親和性的任何現有工作均不受影響。 + +
        +
        +android:excludeFromRecents="true" +
        +
        + 從 [近期記錄] 中排除新的工作,,避免使用者意外返回瀏覽。 + +
        +
        +

        + 以下程式碼片段顯示該元素: +

        +
        +<activity
        +    android:name=".ResultActivity"
        +...
        +    android:launchMode="singleTask"
        +    android:taskAffinity=""
        +    android:excludeFromRecents="true">
        +</activity>
        +...
        +
        +
      2. +
      3. + 建置並發出通知: +
          +
        1. + 建立會啟動 {@link android.app.Activity} 的{@link android.content.Intent}。 + +
        2. +
        3. + 使用旗標 +{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_NEW_TASK} 與 +{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK FLAG_ACTIVITY_CLEAR_TASK} 呼叫 +{@link android.content.Intent#setFlags setFlags()} 來設定 {@link android.app.Activity} 以全新的空白工作啟動。 + +
        4. +
        5. + 為 {@link android.content.Intent} 設定您需要的任何其他選項。 +
        6. +
        7. + 呼叫 {@link android.app.PendingIntent#getActivity getActivity()} 從{@link android.content.Intent} 中建立 {@link android.app.PendingIntent}。 + + 接著,您可以使用這個 {@link android.app.PendingIntent} 當成 +{@link android.support.v4.app.NotificationCompat.Builder#setContentIntent +setContentIntent()} 的引數。 +
        8. +
        +

        + 以下程式碼片段會示範程序: +

        +
        +// Instantiate a Builder object.
        +NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        +// Creates an Intent for the Activity
        +Intent notifyIntent =
        +        new Intent(this, ResultActivity.class);
        +// Sets the Activity to start in a new, empty task
        +notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
        +                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        +// Creates the PendingIntent
        +PendingIntent notifyPendingIntent =
        +        PendingIntent.getActivity(
        +        this,
        +        0,
        +        notifyIntent,
        +        PendingIntent.FLAG_UPDATE_CURRENT
        +);
        +
        +// Puts the PendingIntent into the notification builder
        +builder.setContentIntent(notifyPendingIntent);
        +// Notifications are issued by sending them to the
        +// NotificationManager system service.
        +NotificationManager mNotificationManager =
        +    (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        +// Builds an anonymous Notification object from the builder, and
        +// passes it to the NotificationManager
        +mNotificationManager.notify(id, builder.build());
        +
        +
      4. +
      + + +

      在通知中顯示進度

      +

      + 通知可包含動畫進度指示器,向使用者顯示進行中操作的狀態。 +如果您隨時可預估操作花費的時間以及完成程度,可以使用形式「明確」的指示器 (進度列)。 + +如果您無法預估操作的時間長度,請使用形式「不明確」的指示器 (Activity 指示器)。 + +

      +

      + 進度指示器是使用平台的 +{@link android.widget.ProgressBar} 類別實作來顯示。 +

      +

      + 從 Android 4.0 開始,只要呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 即可在平台上使用進度指示器。對於先前的版本,您必須建立自己的自訂通知版面配置,當中還要包含 {@link android.widget.ProgressBar} 檢視。 + + +

      +

      + 下列各節說明如何使用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 在通知中顯示進度。 +

      + +

      顯示時間長度固定的進度指示器

      +

      + 如要顯示明確的進度列,請呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress +setProgress(max, progress, false)} 將該列新增至您的通知,然後發出通知。隨著您的操作進行, +progress 會增加並更新通知。操作結束時, +progress 應該等於 max。呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress()} 的常見方法是將 max 設定為 100,然後新增 progress 做為操作的「完成百分比」值。 + + +

      +

      + 當操作完成時,您可以繼續顯示進度列或將其移除。不論是任何一種情況,都務必更新通知文字來指出操作已完成。 + + 如要移除進度列,請呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress +setProgress(0, 0, false)}。例如: +

      +
      +...
      +mNotifyManager =
      +        (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
      +mBuilder = new NotificationCompat.Builder(this);
      +mBuilder.setContentTitle("Picture Download")
      +    .setContentText("Download in progress")
      +    .setSmallIcon(R.drawable.ic_notification);
      +// Start a lengthy operation in a background thread
      +new Thread(
      +    new Runnable() {
      +        @Override
      +        public void run() {
      +            int incr;
      +            // Do the "lengthy" operation 20 times
      +            for (incr = 0; incr <= 100; incr+=5) {
      +                    // Sets the progress indicator to a max value, the
      +                    // current completion percentage, and "determinate"
      +                    // state
      +                    mBuilder.setProgress(100, incr, false);
      +                    // Displays the progress bar for the first time.
      +                    mNotifyManager.notify(0, mBuilder.build());
      +                        // Sleeps the thread, simulating an operation
      +                        // that takes time
      +                        try {
      +                            // Sleep for 5 seconds
      +                            Thread.sleep(5*1000);
      +                        } catch (InterruptedException e) {
      +                            Log.d(TAG, "sleep failure");
      +                        }
      +            }
      +            // When the loop is finished, updates the notification
      +            mBuilder.setContentText("Download complete")
      +            // Removes the progress bar
      +                    .setProgress(0,0,false);
      +            mNotifyManager.notify(ID, mBuilder.build());
      +        }
      +    }
      +// Starts the thread by calling the run() method in its Runnable
      +).start();
      +
      + + +

      顯示持續性 Activity 指示器

      +

      + 如要顯示不明確的 Activity 指示器,可利用 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, true)}(可忽略前兩個引數) 將這類指示器新增至通知,然後發出通知。 +結果是指示器會有和進度列相同的樣式,只不過其中的動畫會持續播放。 + +

      +

      + 在操作一開始發出通知。動畫會持續播放,直到您修改通知為止。 +操作完成時,可呼叫 +{@link android.support.v4.app.NotificationCompat.Builder#setProgress setProgress(0, 0, false)},然後更新通知以移除 Activity 指示器。 + + 請務必這樣做,不然即使操作完成後,動畫還是會繼續播放。此外,請務必變更通知文字來指出操作已完成。 + +

      +

      + 如要查看 Activity 指示器如何運作,請參考上方的程式碼片段。找出下列幾行程式碼: +

      +
      +// Sets the progress indicator to a max value, the current completion
      +// percentage, and "determinate" state
      +mBuilder.setProgress(100, incr, false);
      +// Issues the notification
      +mNotifyManager.notify(0, mBuilder.build());
      +
      +

      + 用下列幾行取代您找到的程式碼: +

      +
      + // Sets an activity indicator for an operation of indeterminate length
      +mBuilder.setProgress(0, 0, true);
      +// Issues the notification
      +mNotifyManager.notify(0, mBuilder.build());
      +
      + +

      通知中繼資料

      + +

      通知會按照您以下列 +{@link android.support.v4.app.NotificationCompat.Builder} 方法指派的中繼資料加以排序:

      + +
        +
      • 當裝置處於「優先順序」模式時 (例如,如果您的通知代表來電、即時訊息或鬧鐘),{@link android.support.v4.app.NotificationCompat.Builder#setCategory(java.lang.String) setCategory()} 會指示系統如何處理您的應用程式通知。 + +
      • +
      • {@link android.support.v4.app.NotificationCompat.Builder#setPriority(int) setPriority()} 會將具有優先順序欄位的通知設定為 {@code PRIORITY_MAX} 或 {@code PRIORITY_HIGH},而如果通知也有聲音或振動,就會以小型浮動視窗顯示。 + +
      • +
      • {@link android.support.v4.app.NotificationCompat.Builder#addPerson(java.lang.String) addPerson()} 可讓您將人員名單新增至通知。 +您的應用程式可用這種方式通知系統,應該將來自指定人員的通知歸在同一組,或是將較重要人員發出的通知排序。 + +
      • +
      + +
      + +

      + 圖 3.顯示抬頭通知的全螢幕 Activity +

      +
      + +

      抬頭通知

      + +

      針對 Android 5.0 (API 級別 21),當裝置處於使用中 (也就是裝置已解鎖且螢幕處於開啟) 時,通知能以小型浮動視窗顯示 (也稱為「抬頭」通知)。 + +這些通知類似於精簡版的通知,只不過抬頭通知也會顯示動作按鈕。 + +使用者不需要離開目前的應用程式,就可以執行動作或關閉抬頭通知。 +

      + +

      可能觸發抬頭通知的範例情況包括:

      + +
        +
      • 使用者的 Activity 處於全螢幕模式 (應用程式使用 +{@link android.app.Notification#fullScreenIntent});或者
      • +
      • 通知擁有高優先順序,並使用鈴聲或振動 +
      • +
      + +

      鎖定螢幕通知

      + +

      隨著 Android 5.0 (API 級別 21) 版發行,通知現在可以顯示在鎖定螢幕上。 +您的應用程式可使用此功能,提供媒體播放控制項和其他常見動作。 +使用者可以選擇是否透過 [設定] 在鎖定螢幕上顯示通知,您還可以指定是否能在鎖定螢幕上看見來自您應用程式的通知。 +

      + +

      設定可見度

      + +

      您的應用程式可控制安全鎖定螢幕上所顯示通知的詳細資料可見程度。 +您可以呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setVisibility(int) setVisibility()},然後指定下列其中一個值: +

      + +
        +
      • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC}:顯示通知的完整內容。 +
      • +
      • {@link android.support.v4.app.NotificationCompat#VISIBILITY_SECRET}:不在鎖定螢幕上顯示此通知的任何部分。 +
      • +
      • {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE}:顯示基本資訊,例如通知的圖示與內容標題,但隱藏通知的完整內容。 +
      • +
      + +

      已設定 {@link android.support.v4.app.NotificationCompat#VISIBILITY_PRIVATE} 時,您還可提供可隱藏特定詳細資料的通知內容替代版本。 +例如,SMS 應用程式可能會顯示「您有 3 則簡訊」,但隱藏訊息內容與寄件者。 + +如要提供此替代通知,可先使用 {@link android.support.v4.app.NotificationCompat.Builder} 來建立替代通知。 +建立私人通知物件時,透過 {@link android.support.v4.app.NotificationCompat.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} 方法附加上替代通知。 + + +

      + +

      在鎖定螢幕上控制媒體播放

      + +

      在 Android 5.0 (API 級別 21) 中,鎖定螢幕不會再依據 {@link android.media.RemoteControlClient} (現已淘汰) 顯示媒體控制項。 +利用 {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} 方法使用 {@link android.app.Notification.MediaStyle} 範本,將動作轉換成可點擊的圖示。 + + +

      + +

      注意:支援程式庫不包含範本與 {@link android.app.Notification.Builder#addAction(android.app.Notification.Action) addAction()} 方法,因此這些功能只能在 Android 5.0 以上版本中執行。 + +

      + +

      如要在 Android 5.0 的鎖定螢幕上顯示媒體播放控制項,請將可見度設定為 {@link android.support.v4.app.NotificationCompat#VISIBILITY_PUBLIC},如上所述。 +接著,新增動作並設定 {@link android.app.Notification.MediaStyle} 範本,如以下程式碼範例所示: + +

      + +
      +Notification notification = new Notification.Builder(context)
      +    // Show controls on lock screen even when user hides sensitive content.
      +    .setVisibility(Notification.VISIBILITY_PUBLIC)
      +    .setSmallIcon(R.drawable.ic_stat_player)
      +    // Add media control buttons that invoke intents in your media service
      +    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0
      +    .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent)  // #1
      +    .addAction(R.drawable.ic_next, "Next", nextPendingIntent)     // #2
      +    // Apply the media style template
      +    .setStyle(new Notification.MediaStyle()
      +    .setShowActionsInCompactView(1 /* #1: pause button */)
      +    .setMediaSession(mMediaSession.getSessionToken())
      +    .setContentTitle("Wonderful music")
      +    .setContentText("My Awesome Band")
      +    .setLargeIcon(albumArtBitmap)
      +    .build();
      +
      + +

      注意:進一步實作控制媒體的 {@link android.media.RemoteControlClient} 已失效。 +如要進一步瞭解管理媒體工作階段與控制播放的新 API,請參閱媒體播放控制項。 + +

      + + + +

      自訂通知版面配置

      +

      + 通知架構可讓您定義自訂通知版面配置,藉此定義 {@link android.widget.RemoteViews} 物件中的通知外觀。 + + 自訂配置通知類似於一般通知,只不過這類通知是以 XML 版面配置檔案中定義的 {@link android.widget.RemoteViews} 為基礎。 + +

      +

      + 自訂通知版面配置的可用高度取決於通知檢視。一般檢視的版面配置以 64 dp 為限,而擴充檢視的版面配置以 256 dp 為限。 + +

      +

      + 如要定義自訂通知版面配置,請從具現化可擴大 XML 配置檔案的 +{@link android.widget.RemoteViews} 物件著手。接著,改為呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()},而不是呼叫像是 {@link android.support.v4.app.NotificationCompat.Builder#setContentTitle setContentTitle()} 的方法。 + + +如要設定自訂通知中的內容詳細資料,可使用 {@link android.widget.RemoteViews} 中的方法來設定檢視下層物件的值: + + +

      +
        +
      1. + 以個別檔案建立通知的 XML 版面配置。您可以使用任何檔案名稱,但副檔名必須為 .xml。 + +
      2. +
      3. + 在您的應用程式中,使用 {@link android.widget.RemoteViews} 方法來定義通知的圖示與文字。 +藉由呼叫 {@link android.support.v4.app.NotificationCompat.Builder#setContent setContent()} 將這個 {@link android.widget.RemoteViews} 物件放入您的 {@link android.support.v4.app.NotificationCompat.Builder}。 + +避免在您的 {@link android.widget.RemoteViews} 物件上設定背景 {@link android.graphics.drawable.Drawable},這是因為這樣做可能會使您的文字色彩變得無法閱讀。 + + +
      4. +
      +

      + {@link android.widget.RemoteViews} 類別還包含您可用來將 {@link android.widget.Chronometer} 或 {@link android.widget.ProgressBar} 輕鬆新增至通知版面配置的方法。 + +如要進一步瞭解如何為您的通知建立自訂版面配置,請參閱 {@link android.widget.RemoteViews} 參考文件。 + +

      +

      + 注意:當您使用自訂通知版面配置時,請特別注意,確保您的自訂版面配置適用於不同的裝置方向或解析度。 +雖然此建議適用於所有檢視版面配置,但對於通知而言特別重要,原因是通知匣的空間十分有限。 + +不要建立過於複雜的自訂版面配置,而且一定要在不同的設定中加以測試。 + +

      + +

      針對自訂通知文字使用樣式資源

      +

      + 自訂通知文字一律使用樣式資源。在不同的裝置與版本間可能會有不同的通知背景色彩,因此使用樣式資源可協助您將這點列入考量。 + +從 Android 2.3 開始,系統已定義了標準通知版面配置資料的樣式。 +如果您在適用於 Android 2.3 以上版本的應用程式中使用相同的樣式,將可確保能在顯示背景上看見您的文字。 + +

      diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd new file mode 100644 index 0000000000000000000000000000000000000000..44d05a82ebc2ea7ba8feaadc97a631f72c5878df --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/overview.jd @@ -0,0 +1,71 @@ +page.title=UI 總覽 +@jd:body + + +

      Android 應用程式中的所有使用者介面元素都是使用 {@link android.view.View} 與 +{@link android.view.ViewGroup} 物件。{@link android.view.View} 物件可在畫面上繪製使用者能與之互動的項目。 +{@link android.view.ViewGroup} 物件可保留其他 +{@link android.view.View} (與 {@link android.view.ViewGroup}) 物件,以定義介面的版面配置。 +

      + +

      Android 提供 {@link android.view.View} 與{@link +android.view.ViewGroup} 子類別集合,提供一般輸入控制項 (例如按鈕與文字欄位) 及各種版面配置模型 (例如線性或相對)。 +

      + + +

      使用者介面版面配置

      + +

      應用程式每個元件的使用者介面是使用 {@link +android.view.View} 與 {@link android.view.ViewGroup} 物件的階層來定義,如圖 1 所示。每個檢視群組都是不可見容器,用以組織子檢視,而子檢視可能是輸入控制項或其他繪製部分 UI 的小工具。這個階層樹狀結構可依您的需求簡單或複雜化 (但簡化才會有最佳效能)。 + + + +

      + + +

      圖 1.定義 UI 版面配置的檢視階層圖例 +

      + +

      如要宣告版面配置,您可以在程式碼中將 {@link android.view.View} 物件具現化,然後開始建置樹狀結構,但最簡單也最有效的方法是使用 XML 檔案來定義您的版面配置。 + +XML 提供類似於 HTML 且人類看得懂的版面配置結構。

      + +

      檢視的 XML 元素名稱相當於它個別代表的 Android 類別。因此, +<TextView> 元素可在您的 UI 中建立 {@link android.widget.TextView} 小工具,而 +<LinearLayout> 元素可以建立 {@link android.widget.LinearLayout} 檢視群組。 +

      + +

      例如,上面有一個文字檢視與按鈕的簡單垂直版面配置,看起來像這樣:

      +
      +<?xml version="1.0" encoding="utf-8"?>
      +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      +              android:layout_width="fill_parent" 
      +              android:layout_height="fill_parent"
      +              android:orientation="vertical" >
      +    <TextView android:id="@+id/text"
      +              android:layout_width="wrap_content"
      +              android:layout_height="wrap_content"
      +              android:text="I am a TextView" />
      +    <Button android:id="@+id/button"
      +            android:layout_width="wrap_content"
      +            android:layout_height="wrap_content"
      +            android:text="I am a Button" />
      +</LinearLayout>
      +
      + +

      當您載入應用程式中的版面配置資源時,Android 會將每個版面配置節點初始化成執行階段物件,您再用來定義其他行為、查詢物件狀態或修改版面配置。 + +

      + +

      如需建立 UI 版面配置的完整指南,請參閱 XML 版面配置。 + + + +

      使用者介面元件

      + +

      您不必使用 {@link android.view.View} 與 {@link +android.view.ViewGroup} 物件來建置您的所有 UI。Android 提供的數個應用程式元件會提供標準 UI 版面配置,您只需要定義內容即可。 +這些 UI 元件各自有一組獨特的 API,如其各自的文件中所述,例如動作列對話方塊狀態通知。 +

      + + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd new file mode 100644 index 0000000000000000000000000000000000000000..91ac929e0fa12b8f4ae4bf27b19a9ae70d2209ea --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/settings.jd @@ -0,0 +1,1202 @@ +page.title=設定 +page.tags=偏好設定、偏好設定 Activity、偏好設定片段 + +@jd:body + + +
      +
      + +

      本文件內容

      +
        +
      1. 總覽 +
          +
        1. 偏好設定
        2. +
        +
      2. +
      3. 在 XML 中定義偏好設定 +
          +
        1. 建立設定群組
        2. +
        3. 使用意圖
        4. +
        +
      4. +
      5. 建立偏好設定 Activity
      6. +
      7. 使用偏好設定片段
      8. +
      9. 設定預設值
      10. +
      11. 使用偏好設定標頭 +
          +
        1. 建立標頭檔案
        2. +
        3. 顯示標頭
        4. +
        5. 使用偏好設定標頭支援舊版
        6. +
        +
      12. +
      13. 讀取偏好設定 +
          +
        1. 接聽偏好設定變更
        2. +
        +
      14. +
      15. 管理網路使用量
      16. +
      17. 建置自訂偏好設定 +
          +
        1. 指定使用者介面
        2. +
        3. 儲存設定值
        4. +
        5. 初始化目前值
        6. +
        7. 提供預設值
        8. +
        9. 儲存和還原偏好設定狀態
        10. +
        +
      18. +
      + +

      重要類別

      +
        +
      1. {@link android.preference.Preference}
      2. +
      3. {@link android.preference.PreferenceActivity}
      4. +
      5. {@link android.preference.PreferenceFragment}
      6. +
      + + +

      另請參閱

      +
        +
      1. 設定設計指南
      2. +
      +
      +
      + + + + +

      應用程式通常會包含可讓使用者修改應用程式功能和行為的設定。例如,有些應用程式可讓使用者指定是否啟用通知,或指定應用程式與雲端同步資料的頻率。 + +

      + +

      如果您想要為應用程式提供設定,應該使用 Android {@link android.preference.Preference} API 建置與其他 Android 應用程式使用者體驗一致的介面 (包含系統設定)。 + +本文件說明如何使用 {@link android.preference.Preference} API 建置應用程式設定。 +

      + +
      +

      設定設計

      +

      如要瞭解如何設計設定,請參閱設定設計指南。

      +
      + + + +

      圖 1.Android 簡訊應用程式設定的螢幕擷取畫面。 +選取一個 {@link android.preference.Preference} 定義的項目可以開啟介面變更設定。 +

      + + + + +

      總覽

      + +

      現在不使用 {@link android.view.View} 物件建置使用者介面,而是使用您在 XML 檔案中所宣告 {@link android.preference.Preference} 類別的各種子類別來建置設定。 + +

      + +

      一個 {@link android.preference.Preference} 物件是單一設定的建置區塊。 +每個 {@link android.preference.Preference} 都以項目的形式在清單中顯示,並為使用者提供適當的 UI 以修改設定。 +例如,{@link +android.preference.CheckBoxPreference} 可以建立顯示核取方塊的清單項目,而 {@link +android.preference.ListPreference} 可以建立開啟對話方塊 (其中包含選擇清單) 的項目。

      + +

      每個您新增的 {@link android.preference.Preference} 都會有一個對應的鍵值配對,系統可使用此鍵值對將設定儲存到您應用程式設定的預設 {@link android.content.SharedPreferences} 檔案。 + +當使用者變更設定,系統會為您更新 {@link android.content.SharedPreferences} 檔案中的對應值。 +您唯一需要與關聯的 {@link android.content.SharedPreferences} 檔案直接互動的時候,是當您需要讀取值才能根據使用者設定判斷應用程式行為時。 + +

      + +

      儲存在 {@link android.content.SharedPreferences} 的每個設定值可以是下列其中一個資料類型: +

      + +
        +
      • 布林值
      • +
      • 浮動
      • +
      • 整數
      • +
      • 長整數
      • +
      • 字串
      • +
      • 字串 {@link java.util.Set}
      • +
      + +

      由於您的應用程式設定 UI 是使用 {@link android.preference.Preference} 物件,而不是 {@link android.view.View} 物件建置,因此您需要使用專門的 {@link android.app.Activity} 或 {@link android.app.Fragment} 子類別來顯示清單設定: + + +

      + +
        +
      • 如果應用程式支援的 Android 版本早於 3.0 (API 級別 10 及較早版本),您必須以 {@link android.preference.PreferenceActivity} 類別延伸的形式建置 Activity。 +
      • +
      • 在 Android 3.0 及更新版本上,您應該使用傳統 {@link android.app.Activity},它託管了顯示應用程式設定的 {@link android.preference.PreferenceFragment}。 + +然而,當您有多個設定群組時,還可以使用 {@link android.preference.PreferenceActivity} 在大螢幕建立兩個面板的版面配置。 +
      • +
      + +

      如何設定 {@link android.preference.PreferenceActivity} 和 {@link +android.preference.PreferenceFragment} 執行個體在建立偏好設定 Activity使用偏好設定片段小節中有相關說明。 +

      + + +

      偏好設定

      + +

      應用程式的每個設定都會以 {@link +android.preference.Preference} 類別的特定子類別代表。每個子類別包含一組核心屬性,可讓您為設定指定標題等項目和預設值。 +每個子類別還提供自己專屬的屬性和使用者介面。 +例如,圖 1 顯示簡訊應用程式設定的螢幕擷取畫面。 +設定畫面中的每個清單項目都由不同的 {@link +android.preference.Preference} 物件支援。

      + +

      下列為幾個最常見的偏好設定:

      + +
      +
      {@link android.preference.CheckBoxPreference}
      +
      為啟用或停用的設定顯示含有核取方塊的項目。儲存的值是布林值 (如果選取 true)。 +
      + +
      {@link android.preference.ListPreference}
      +
      開啟含有選項按鈕清單的對話方塊。儲存的值可以是任何支援的值類型 (如上所列)。 +
      + +
      {@link android.preference.EditTextPreference}
      +
      開啟含有 {@link android.widget.EditText} 小工具的對話方塊。儲存的值是 {@link +java.lang.String}。
      +
      + +

      請參閱 {@link android.preference.Preference} 類別,以取得所有其他子類別及其對應屬性的清單。 +

      + +

      當然,內建類別無法滿足所有需要,您的應用程式可能需要更專門的功能。 +例如,平台目前無法提供 {@link +android.preference.Preference} 類別以選取數字或日期。因此,您可能需要定義自己的 {@link android.preference.Preference} 子類別。 +如需執行此操作的協助,請參閱建置自訂偏好設定

      + + + +

      在 XML 中定義偏好設定

      + +

      雖然您可以在執行階段將新的 {@link android.preference.Preference} 物件具現化,但您仍應該在 XML 中定義設定清單,並在其中包含 {@link android.preference.Preference} 物件的階層。 + +使用 XML 檔案定義設定集合是較建議的做法,因為該檔案提供的易讀結構很容易更新。 +而且,雖然您仍然可以在執行階段修改集合,但您的應用程式設定通常是預先定義好的。 +

      + +

      每個 {@link android.preference.Preference} 子類別都可透過與類別名稱相符的 XML 元素進行宣告,例如 {@code <CheckBoxPreference>}。 +

      + +

      您必須將 XML 檔案儲存在 {@code res/xml/} 目錄中。雖然您可以自由命名檔案,但在傳統上會命名為 {@code preferences.xml}。 +您通常只需要一個檔案,因為階層 (會開啟自己的設定清單) 中的子目錄是透過 {@link android.preference.PreferenceScreen} 的巢狀執行個體進行宣告。 + +

      + +

      注意:如果您想要為設定建立多個面板的版面配置,則您需要為每個片段準備個別的 XML 檔案。 +

      + +

      XML 檔案的根節點必須是 {@link android.preference.PreferenceScreen +<PreferenceScreen>} 元素。您要將每個 {@link +android.preference.Preference} 新增到此元素內。您在 {@link android.preference.PreferenceScreen <PreferenceScreen>} 元素內新增的每個子項會在設定清單中顯示為單一項目。 + +

      + +

      例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
      +    <CheckBoxPreference
      +        android:key="pref_sync"
      +        android:title="@string/pref_sync"
      +        android:summary="@string/pref_sync_summ"
      +        android:defaultValue="true" />
      +    <ListPreference
      +        android:dependency="pref_sync"
      +        android:key="pref_syncConnectionType"
      +        android:title="@string/pref_syncConnectionType"
      +        android:dialogTitle="@string/pref_syncConnectionType"
      +        android:entries="@array/pref_syncConnectionTypes_entries"
      +        android:entryValues="@array/pref_syncConnectionTypes_values"
      +        android:defaultValue="@string/pref_syncConnectionTypes_default" />
      +</PreferenceScreen>
      +
      + +

      此範例中有一個 {@link android.preference.CheckBoxPreference} 和一個 {@link +android.preference.ListPreference}。這兩個項目都包含下列三個屬性:

      + +
      +
      {@code android:key}
      +
      需要這個屬性才能保留資料值的偏好設定。它會指定當系統將此設定值儲存於 {@link +android.content.SharedPreferences} 時要使用的唯一索引鍵 (字串)。 + +

      只有在下列情況下不需要此屬性:偏好設定為 {@link android.preference.PreferenceCategory} 或 {@link android.preference.PreferenceScreen},或者偏好設定指定 {@link android.content.Intent} 進行呼叫 (搭配 {@code <intent>} 元素) 或 {@link android.app.Fragment} 進行顯示 (搭配 {@code +android:fragment} 屬性)。 + +

      +
      +
      {@code android:title}
      +
      這可為設定提供使用者可見的名稱。
      +
      {@code android:defaultValue}
      +
      這可指定系統在 {@link +android.content.SharedPreferences} 檔案中應設定的初始值。您應該為所有設定提供預設值。 +
      +
      + +

      如需所有其他支援的屬性相關資訊,請參閱 {@link +android.preference.Preference} (及個別子類別) 文件。

      + + +
      + +

      圖 2.設定含有標題的類別。 +
      1.類別以 {@link +android.preference.PreferenceCategory <PreferenceCategory>} 元素指定。
      2.標題以 {@code android:title} 屬性指定。 +

      +
      + + +

      如果您的設定清單超過約 10 個項目,您可以新增標題來定義設定群組,或在分開的畫面顯示這些群組。 + +這些選項在下列幾節有詳細的描述。

      + + +

      建立設定群組

      + +

      如果您的清單有 10 個以上的項目,掃描、理解和處理這些項目對使用者而言可能會很困難。 +如要解決這個問題,可將部分或所有設定分成不同的群組,有效地將一長串清單分成多個簡短的清單。 + +一組包含相關設定的群組可以下列其中一種方式顯示:

      + + + +

      您可以使用上述一種或兩種分組技巧來整理您的應用程式設定。決定要使用哪種方法或如何分割設定時,您應該要依照 Android 設計設定指南中的指導方針進行。 + +

      + + +

      使用標題

      + +

      如果您想要在設定群組間使用標題區分 (如圖 2 所示),將 {@link android.preference.Preference} 物件的每個群組放入 {@link +android.preference.PreferenceCategory}。 +

      + +

      例如:

      + +
      +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
      +    <PreferenceCategory 
      +        android:title="@string/pref_sms_storage_title"
      +        android:key="pref_key_storage_settings">
      +        <CheckBoxPreference
      +            android:key="pref_key_auto_delete"
      +            android:summary="@string/pref_summary_auto_delete"
      +            android:title="@string/pref_title_auto_delete"
      +            android:defaultValue="false"... />
      +        <Preference 
      +            android:key="pref_key_sms_delete_limit"
      +            android:dependency="pref_key_auto_delete"
      +            android:summary="@string/pref_summary_delete_limit"
      +            android:title="@string/pref_title_sms_delete"... />
      +        <Preference 
      +            android:key="pref_key_mms_delete_limit"
      +            android:dependency="pref_key_auto_delete"
      +            android:summary="@string/pref_summary_delete_limit"
      +            android:title="@string/pref_title_mms_delete" ... />
      +    </PreferenceCategory>
      +    ...
      +</PreferenceScreen>
      +
      + + +

      使用子畫面

      + +

      如果您想將設定群組放入子畫面 (如圖 3 所示),將 {@link android.preference.Preference} 物件的群組放入 {@link +android.preference.PreferenceScreen}。 +

      + + +

      圖 3.設定子畫面。{@code +<PreferenceScreen>} 元素建立的項目會在選取時開啟獨立的清單以顯示巢狀設定。 +

      + +

      例如:

      + +
      +<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
      +    <!-- opens a subscreen of settings -->
      +    <PreferenceScreen
      +        android:key="button_voicemail_category_key"
      +        android:title="@string/voicemail"
      +        android:persistent="false">
      +        <ListPreference
      +            android:key="button_voicemail_provider_key"
      +            android:title="@string/voicemail_provider" ... />
      +        <!-- opens another nested subscreen -->
      +        <PreferenceScreen
      +            android:key="button_voicemail_setting_key"
      +            android:title="@string/voicemail_settings"
      +            android:persistent="false">
      +            ...
      +        </PreferenceScreen>
      +        <RingtonePreference
      +            android:key="button_voicemail_ringtone_key"
      +            android:title="@string/voicemail_ringtone_title"
      +            android:ringtoneType="notification" ... />
      +        ...
      +    </PreferenceScreen>
      +    ...
      +</PreferenceScreen>
      +
      + + +

      使用意圖

      + +

      在某些情況下,您可能會希望偏好設定項目開啟不同的 Activity 而不是設定畫面,例如,開啟網路瀏覽器以檢視網頁。 +如要在使用者選取偏好設定項目時呼叫 {@link +android.content.Intent},請新增 {@code <intent>} 元素做為對應 {@code <Preference>} 元素的子項。 +

      + +

      例如,您可以使用下列方法透過偏好設定項目開啟網頁:

      + +
      +<Preference android:title="@string/prefs_web_page" >
      +    <intent android:action="android.intent.action.VIEW"
      +            android:data="http://www.example.com" />
      +</Preference>
      +
      + +

      您可以使用下列屬性建立隱含和明確意圖:

      + +
      +
      {@code android:action}
      +
      根據 {@link android.content.Intent#setAction setAction()} 方法指派的動作。 +
      +
      {@code android:data}
      +
      根據 {@link android.content.Intent#setData setData()} 方法指派的資料。
      +
      {@code android:mimeType}
      +
      根據 {@link android.content.Intent#setType setType()} 方法指派的 MIME 類型。 +
      +
      {@code android:targetClass}
      +
      根據 {@link android.content.Intent#setComponent +setComponent()} 方法指派的元件名稱類別部分。
      +
      {@code android:targetPackage}
      +
      根據 {@link +android.content.Intent#setComponent setComponent()} 方法指派的元件名稱封裝部分。
      +
      + + + +

      建立偏好設定 Activity

      + +

      如要在 Activity 中顯示設定,延伸 {@link +android.preference.PreferenceActivity} 類別。這是傳統 {@link +android.app.Activity} 類別的延伸,可根據 {@link +android.preference.Preference} 物件的階層顯示設定清單。當使用者進行變更時,{@link android.preference.PreferenceActivity} 會自動保留與每個 {@link +android.preference.Preference} 關聯的設定。 +

      + +

      注意:如果您針對 Android 3.0 及更新版本開發應用程式,您應該改為使用 {@link android.preference.PreferenceFragment}。 +前往下一個使用偏好設定片段章節。 +

      + +

      最需要注意的一件事就是,不要在 {@link +android.preference.PreferenceActivity#onCreate onCreate()} 呼叫期間載入檢視的版面配置。您應該要呼叫 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()},將您在 XML 檔案中宣告的偏好設定新增到 Activity 中。 +例如,下列為功能 {@link android.preference.PreferenceActivity} 所需的基本程式碼: +

      + +
      +public class SettingsActivity extends PreferenceActivity {
      +    @Override
      +    public void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +        addPreferencesFromResource(R.xml.preferences);
      +    }
      +}
      +
      + +

      事實上,這個程式碼對某些應用程式而言已經足夠,因為使用者修改偏好設定後,系統會將變更儲存到預設 {@link android.content.SharedPreferences} 檔案,當您需要檢查使用者設定時,您的其他應用程式元件就能進行讀取。 + +但是,許多應用程式需要更多一點程式碼,以接聽對偏好設定進行的變更。 +如需在 {@link android.content.SharedPreferences} 檔案接聽變更的相關資訊,請參閱讀取偏好設定。 + +

      + + + + +

      使用偏好設定片段

      + +

      如果您針對 Android 3.0 (API 級別 11) 及更新版本進行開發,您應該使用 {@link +android.preference.PreferenceFragment} 顯示 {@link android.preference.Preference} 物件清單。 +您可以將 {@link android.preference.PreferenceFragment} 新增到任何 Activity — 您不需要使用 {@link android.preference.PreferenceActivity}。 +

      + +

      片段單就 Activity 而言,可為您的應用程式提供更有彈性的架構,無論您建置哪一種 Activity 都一樣。 + +因此,我們建議您盡可能使用 {@link +android.preference.PreferenceFragment} 來控制設定的顯示,而不要使用 {@link +android.preference.PreferenceActivity}。

      + +

      實作 {@link android.preference.PreferenceFragment} 可以很簡單,只要定義 {@link android.preference.PreferenceFragment#onCreate onCreate()} 方法,透過 {@link android.preference.PreferenceFragment#addPreferencesFromResource +addPreferencesFromResource()} 載入偏好設定檔案。 + +例如:

      + +
      +public static class SettingsFragment extends PreferenceFragment {
      +    @Override
      +    public void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +
      +        // Load the preferences from an XML resource
      +        addPreferencesFromResource(R.xml.preferences);
      +    }
      +    ...
      +}
      +
      + +

      之後,您可以將此片段新增至 {@link android.app.Activity},做法與任何其他 +{@link android.app.Fragment} 一樣。例如:

      + +
      +public class SettingsActivity extends Activity {
      +    @Override
      +    protected void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +
      +        // Display the fragment as the main content.
      +        getFragmentManager().beginTransaction()
      +                .replace(android.R.id.content, new SettingsFragment())
      +                .commit();
      +    }
      +}
      +
      + +

      注意:{@link android.preference.PreferenceFragment} 沒有自己的 {@link android.content.Context} 物件。 +如果您需要 {@link android.content.Context} 物件,可以呼叫 {@link android.app.Fragment#getActivity()}。 +不過,必須小心,只能在片段附加到 Activity 時才能呼叫 {@link android.app.Fragment#getActivity()}。 +如果未附加片段,或是在生命週期期間中斷連結,{@link +android.app.Fragment#getActivity()} 將傳回 null。 +

      + + +

      設定預設值

      + +

      您建立的偏好設定可能為應用程式定義了一些重要的行為,因此當使用者第一次開啟您的應用程式時,務必使用每個 {@link android.preference.Preference} 預設值來初始化關聯的 {@link android.content.SharedPreferences} 檔案。 + + +

      + +

      您必須要做的第一件事,就是使用 {@code android:defaultValue} 屬性指定 XML 檔案中每個 {@link +android.preference.Preference} 物件的預設值。 +值可以是適用於對應 {@link android.preference.Preference} 物件的任何資料類型。 +例如: +

      + +
      +<!-- default value is a boolean -->
      +<CheckBoxPreference
      +    android:defaultValue="true"
      +    ... />
      +
      +<!-- default value is a string -->
      +<ListPreference
      +    android:defaultValue="@string/pref_syncConnectionTypes_default"
      +    ... />
      +
      + +

      之後,從您應用程式主 Activity — 以及使用者第一次進入您應用程式所使用的任何其他 Activity — 的 {@link android.app.Activity#onCreate onCreate()} 方法呼叫 {@link android.preference.PreferenceManager#setDefaultValues +setDefaultValues()}: + +

      + +
      +PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);
      +
      + +

      在 {@link android.app.Activity#onCreate onCreate()} 期間呼叫它,可確保您的應用程式正確地以預設值初始化,您的應用程式可能需要讀取這些預設值,才能判斷一些行為 (例如,使用行動網路時是否可下載資料)。 + + +

      + +

      這個方法採用三種引數:

      +
        +
      • 您的應用程式 {@link android.content.Context}。
      • +
      • 您要設定預設值之偏好設定 XML 檔案的資源 ID。
      • +
      • 布林值指出預設值是否要設定一次以上。 +

        如果為 false,系統只會在過去從未呼叫此方法時設定預設值 (或者預設值共用偏好設定檔案的 {@link android.preference.PreferenceManager#KEY_HAS_SET_DEFAULT_VALUES} 為 false)。 + +

      • +
      + +

      只要將第三個引數設為 false,您可以在每次 Activity 啟動時很安全地呼叫此方法,而不會將使用者儲存的偏好設定重設為預設值。 + +不過,如果您將它設為 true,會將任何之前的值覆寫為預設值。 +

      + + + +

      使用偏好設定標頭

      + +

      在少數情況下,您可能會想將設定設計為只在第一個畫面顯示子畫面清單(與系統設定應用程式一樣,如圖 4 和 5 所示)。 + +當您為 Android 3.0 及更新版本開發這類設計時,您應該使用 Android 3.0 的新「標頭」功能,而不是使用巢狀 {@link android.preference.PreferenceScreen} 元素建置子畫面。 + +

      + +

      如要使用標頭建置設定,您必須:

      +
        +
      1. 將每個設定群組分成不同的 {@link +android.preference.PreferenceFragment} 執行個體。也就是說,每個設定群組需要一個獨立的 XML 檔案。 +
      2. +
      3. 建立一個 XML 標頭檔案,其中列出每個設定群組以及宣告哪些片段包含對應的設定清單。 +
      4. +
      5. 延伸 {@link android.preference.PreferenceActivity} 類別以託管您的設定。
      6. +
      7. 實作 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回呼以指定標頭檔案。 +
      8. +
      + +

      使用這個設計最大的好處在於在大螢幕執行時, +{@link android.preference.PreferenceActivity} 會自動顯示兩個面板的版面配置,如圖 4 所示。

      + +

      即使應用程式支援的 Android 版本較 3.0 舊,您還是可以建置應用程式在新版的裝置上使用 {@link android.preference.PreferenceFragment} 顯示兩個面板,同時仍然支援舊版裝置上的傳統多畫面階層 (請參閱使用偏好設定標頭支援舊版)。 + + + +

      + + +

      圖 4.含標頭的兩個面板版面配置。
      1.標頭以 XML 標頭檔案定義。 +
      2.每個設定群組是由標頭檔案 {@code <header>} 元素指定的{@link android.preference.PreferenceFragment} 所定義。 + +

      + + +

      圖 5.含設定標頭的手機裝置。選取項目後,關聯的 {@link android.preference.PreferenceFragment} 會取代標頭。 + +

      + + +

      建立標頭檔案

      + +

      您標頭清單中的每個設定群組是由根 {@code <preference-headers>} 元素中的單一 {@code <header>} 元素指定。 +例如:

      + +
      +<?xml version="1.0" encoding="utf-8"?>
      +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
      +    <header 
      +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
      +        android:title="@string/prefs_category_one"
      +        android:summary="@string/prefs_summ_category_one" />
      +    <header 
      +        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
      +        android:title="@string/prefs_category_two"
      +        android:summary="@string/prefs_summ_category_two" >
      +        <!-- key/value pairs can be included as arguments for the fragment. -->
      +        <extra android:name="someKey" android:value="someHeaderValue" />
      +    </header>
      +</preference-headers>
      +
      + +

      有了 {@code android:fragment} 屬性,每個標頭會宣告當使用者選取標頭時,應該開啟的 {@link +android.preference.PreferenceFragment} 執行個體。

      + +

      {@code <extras>} 元素可讓您將鍵值配對傳送到 {@link +android.os.Bundle} 中的片段。片段可以透過呼叫 {@link +android.app.Fragment#getArguments()} 擷取引數。您可以因各種理由將引數傳送到片段,但其中一個最好的理由是針對每個群組重複使用 {@link +android.preference.PreferenceFragment} 中相同的子類別,並使用引數指定應載入片段的偏好設定 XML 檔案。 + +

      + +

      例如,當每個標頭使用 {@code "settings"} 索引鍵定義 {@code <extra>} 引數時,下列片段可在多個設定群組重複使用: +

      + +
      +public static class SettingsFragment extends PreferenceFragment {
      +    @Override
      +    public void onCreate(Bundle savedInstanceState) {
      +        super.onCreate(savedInstanceState);
      +
      +        String settings = getArguments().getString("settings");
      +        if ("notifications".equals(settings)) {
      +            addPreferencesFromResource(R.xml.settings_wifi);
      +        } else if ("sync".equals(settings)) {
      +            addPreferencesFromResource(R.xml.settings_sync);
      +        }
      +    }
      +}
      +
      + + + +

      顯示標頭

      + +

      如要顯示偏好設定標頭,必須實作 {@link +android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()} 回呼方法,並呼叫 {@link android.preference.PreferenceActivity#loadHeadersFromResource +loadHeadersFromResource()}。 +例如:

      + +
      +public class SettingsActivity extends PreferenceActivity {
      +    @Override
      +    public void onBuildHeaders(List<Header> target) {
      +        loadHeadersFromResource(R.xml.preference_headers, target);
      +    }
      +}
      +
      + +

      當使用者從標頭清單選取項目時,系統會開啟關聯的 {@link +android.preference.PreferenceFragment}。

      + +

      注意:使用偏好設定標頭時,{@link +android.preference.PreferenceActivity} 的子類別不需要實作 {@link +android.preference.PreferenceActivity#onCreate onCreate()} 方法,這是因為 Activity 唯一需要做的工作就是載入標頭。 +

      + + +

      使用偏好設定標頭支援舊版

      + +

      如果您應用程式支援的 Android 版本比 3.0 舊,您仍然可以在 Android 3.0 及更新版本執行時,使用標頭提供兩個面板的版面配置。 +您只需要建立一個額外的偏好設定 XML 檔案,該檔案要使用行為與標頭項目 (供舊版 Android 使用) 一樣的基本 {@link android.preference.Preference +<Preference>} 元素。 + +

      + +

      但是,不會開啟新的 {@link android.preference.PreferenceScreen},每個 {@link +android.preference.Preference <Preference>} 元素會傳送一個 {@link android.content.Intent} 到 {@link android.preference.PreferenceActivity},以指定要載入的偏好設定 XML 檔案。 + +

      + +

      例如,下列為使用 Android 3.0及更新版本的偏好設定標頭 XML 檔案 ({@code res/xml/preference_headers.xml}): +

      + +
      +<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
      +    <header 
      +        android:fragment="com.example.prefs.SettingsFragmentOne"
      +        android:title="@string/prefs_category_one"
      +        android:summary="@string/prefs_summ_category_one" />
      +    <header 
      +        android:fragment="com.example.prefs.SettingsFragmentTwo"
      +        android:title="@string/prefs_category_two"
      +        android:summary="@string/prefs_summ_category_two" />
      +</preference-headers>
      +
      + +

      而,這裡有個偏好設定檔案為 Android 3.0 以下版本提供相同的標頭 ({@code res/xml/preference_headers_legacy.xml}): +

      + +
      +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
      +    <Preference 
      +        android:title="@string/prefs_category_one"
      +        android:summary="@string/prefs_summ_category_one"  >
      +        <intent 
      +            android:targetPackage="com.example.prefs"
      +            android:targetClass="com.example.prefs.SettingsActivity"
      +            android:action="com.example.prefs.PREFS_ONE" />
      +    </Preference>
      +    <Preference 
      +        android:title="@string/prefs_category_two"
      +        android:summary="@string/prefs_summ_category_two" >
      +        <intent 
      +            android:targetPackage="com.example.prefs"
      +            android:targetClass="com.example.prefs.SettingsActivity"
      +            android:action="com.example.prefs.PREFS_TWO" />
      +    </Preference>
      +</PreferenceScreen>
      +
      + +

      因為 Android 3.0 已加入對 {@code <preference-headers>} 的支援,系統只會在 Androd 3.0 或更新版本執行時,才會呼叫 {@link +android.preference.PreferenceActivity} 中的 {@link android.preference.PreferenceActivity#onBuildHeaders onBuildHeaders()}。 +如要載入「舊版」標頭檔案 ({@code preference_headers_legacy.xml}),您必須檢查 Android 版本,如果版本比 Android 3.0 更舊 ({@link +android.os.Build.VERSION_CODES#HONEYCOMB}),呼叫 {@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 以載入舊版標頭檔案。 + + +例如:

      + +
      +@Override
      +public void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +    ...
      +
      +    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
      +        // Load the legacy preferences headers
      +        addPreferencesFromResource(R.xml.preference_headers_legacy);
      +    }
      +}
      +
      +// Called only on Honeycomb and later
      +@Override
      +public void onBuildHeaders(List<Header> target) {
      +   loadHeadersFromResource(R.xml.preference_headers, target);
      +}
      +
      + +

      最後一件要做的事,是處理傳送到 Activity 的 {@link android.content.Intent},以識別要載入的偏好設定檔案。 +擷取意圖的動作,並將它與偏好設定 XML {@code <intent>} 標籤中使用的已知動作字串進行比對: +

      + +
      +final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
      +...
      +
      +@Override
      +public void onCreate(Bundle savedInstanceState) {
      +    super.onCreate(savedInstanceState);
      +
      +    String action = getIntent().getAction();
      +    if (action != null && action.equals(ACTION_PREFS_ONE)) {
      +        addPreferencesFromResource(R.xml.preferences);
      +    }
      +    ...
      +
      +    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
      +        // Load the legacy preferences headers
      +        addPreferencesFromResource(R.xml.preference_headers_legacy);
      +    }
      +}
      +
      + +

      請注意,{@link +android.preference.PreferenceActivity#addPreferencesFromResource addPreferencesFromResource()} 的連續呼叫會將所有偏好設定堆疊在單一清單中,因此請確定將條件鏈結到 else-if 陳述式時只會呼叫它一次。 + +

      + + + + + +

      讀取偏好設定

      + +

      根據預設,透過呼叫靜態方法 {@link +android.preference.PreferenceManager#getDefaultSharedPreferences +PreferenceManager.getDefaultSharedPreferences()},您應用程式中的所有偏好設定會儲存可從應用程式內任何地方存取的檔案中。 +這會傳回 {@link +android.content.SharedPreferences} 物件,其中包含與您 {@link +android.preference.PreferenceActivity} 使用之 {@link android.preference.Preference} 物件關聯的所有鍵值配對。 +

      + +

      例如,下列說明如何從應用程式中的任何其他 Activity 讀取其中一個偏好設定值: +

      + +
      +SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
      +String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");
      +
      + + + +

      接聽偏好設定變更

      + +

      使用者變更其中一個偏好設定後,您想要立即收到通知的原因有好幾個。 +如要在任何偏好設定發生變更時收到回呼,實作 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener +SharedPreference.OnSharedPreferenceChangeListener} 介面,並呼叫 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} 為 {@link android.content.SharedPreferences} 物件註冊接聽器。 + +

      + +

      介面只有一個回呼方式 {@link +android.content.SharedPreferences.OnSharedPreferenceChangeListener#onSharedPreferenceChanged +onSharedPreferenceChanged()},而且在 Activity 中實作介面可能對您來說會更為容易。 +例如:

      + +
      +public class SettingsActivity extends PreferenceActivity
      +                              implements OnSharedPreferenceChangeListener {
      +    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
      +    ...
      +
      +    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
      +        String key) {
      +        if (key.equals(KEY_PREF_SYNC_CONN)) {
      +            Preference connectionPref = findPreference(key);
      +            // Set summary to be the user-description for the selected value
      +            connectionPref.setSummary(sharedPreferences.getString(key, ""));
      +        }
      +    }
      +}
      +
      + +

      在此範例中,方法會檢查是否是針對已知的偏好設定索引鍵設定進行變更。它會呼叫 {@link android.preference.PreferenceActivity#findPreference findPreference()} 以取得變更的 {@link android.preference.Preference} 物件,以便將項目摘要修改為使用者選取的描述。 + + +也就是說,當設定為 {@link +android.preference.ListPreference} 或其他多選擇設定時,如果設定變更為顯示目前狀態 (如圖 5 顯示的休眠設定),您應該呼叫 {@link +android.preference.Preference#setSummary setSummary()}。 +

      + +

      注意:如 Android 設計文件中有關設定的說明所述,我們建議您在每次使用者變更偏好設定時更新 {@link android.preference.ListPreference} 的摘要,以描述目前的設定。 + +

      + +

      為了在 Activity 中正確管理生命週期,我們建議您分別在 {@link +android.app.Activity#onResume} 和 {@link android.app.Activity#onPause} 回呼期間,註冊和解決註冊您的 {@link android.content.SharedPreferences.OnSharedPreferenceChangeListener}: +

      + +
      +@Override
      +protected void onResume() {
      +    super.onResume();
      +    getPreferenceScreen().getSharedPreferences()
      +            .registerOnSharedPreferenceChangeListener(this);
      +}
      +
      +@Override
      +protected void onPause() {
      +    super.onPause();
      +    getPreferenceScreen().getSharedPreferences()
      +            .unregisterOnSharedPreferenceChangeListener(this);
      +}
      +
      + +

      注意:當您呼叫 {@link +android.content.SharedPreferences#registerOnSharedPreferenceChangeListener +registerOnSharedPreferenceChangeListener()} 時,偏好設定管理員目前不會在接聽器儲存強式參照。 +您必須將強式參照儲存到接聽器,否則它會很容易受記憶體回收的影響。 +我們建議您在物件的執行個體資料中保留接聽器參照,如此一來您可以在需要接聽器時隨時使用。 + +

      + +

      例如,在下列程式碼中,呼叫器沒有保留接聽器參照。 +因此,接聽器將受到記憶體回收的支配,而且會在未來不明確的時間發生失敗: +

      + +
      +prefs.registerOnSharedPreferenceChangeListener(
      +  // Bad! The listener is subject to garbage collection!
      +  new SharedPreferences.OnSharedPreferenceChangeListener() {
      +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
      +    // listener implementation
      +  }
      +});
      +
      + +

      將接聽器參照儲存在物件的執行個體資料欄位中,可以在需要接聽器時隨時使用: +

      + +
      +SharedPreferences.OnSharedPreferenceChangeListener listener =
      +    new SharedPreferences.OnSharedPreferenceChangeListener() {
      +  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
      +    // listener implementation
      +  }
      +};
      +prefs.registerOnSharedPreferenceChangeListener(listener);
      +
      + +

      管理網路使用量

      + + +

      從 Android 4.0 開始,系統的設定應用程式允許使用者查看應用程式在前景和背景時使用了多少網路資料。 +然後,使用者可以停用個別應用程式的背景資料。 +如要避免使用者停用您的應用程式從背景存取資料,您應該有效率地使用資料連線,並允許使用者透過應用程式設定精簡您的應用程式資料使用量。 + +

      + +

      例如,您可以讓使用者控制同步資料的頻率,無論您的應用程式只在 Wi-Fi 上執行上傳/下載、應用程式在漫遊使用資料等。 +使用者有了這些控制項,就比較不會在快要達到在系統設定中設定的限制時停用您應用程式的資料存取,因為他們可以準確地控制您應用程式使用的資料量。 + + +

      + +

      您在 {@link android.preference.PreferenceActivity} 新增所需的偏好設定以控制應用程式的資料習慣後,您應該在宣示說明檔新增 {@link +android.content.Intent#ACTION_MANAGE_NETWORK_USAGE} 的意圖篩選器。 +例如:

      + +
      +<activity android:name="SettingsActivity" ... >
      +    <intent-filter>
      +       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
      +       <category android:name="android.intent.category.DEFAULT" />
      +    </intent-filter>
      +</activity>
      +
      + +

      這個意圖篩選器向系統指出,是 Activity 在控制您的應用程式資料使用量。 +因此,當使用者從系統設定應用程式檢查您的應用程式使用了多少資料量時,可以使用 [檢視應用程式設定] 按鈕啟動您的 {@link android.preference.PreferenceActivity},讓使用者精簡您應用程式使用的資料量。 + + +

      + + + + + + + +

      建置自訂偏好設定

      + +

      Android 架構包含各式各樣的 {@link android.preference.Preference} 子類別,可讓您建置所種不同設定類型的 UI。不過,您可能會發現內建解決方案中可能沒有您所需的設定,例如數字挑選器或日期挑選器。 + + +在這類情況下,您需要延伸 {@link android.preference.Preference} 類別或其中一個其他子類別,以建立自訂的偏好設定。 +

      + +

      當您延伸 {@link android.preference.Preference} 類別時,需要執行幾個重要工作: +

      + +
        +
      • 指定使用者選取設定時要顯示的使用者介面。
      • +
      • 視需要儲存設定值。
      • +
      • 顯示 {@link android.preference.Preference} 時,使用目前 (或預設) 值加以初始化。 +
      • +
      • 在系統要求時提供預設值。
      • +
      • 如果 {@link android.preference.Preference} 提供自己的 UI (例如對話方塊),儲存並還原狀態以處理生命週期變更 (例如,當使用者旋轉螢幕時)。 +
      • +
      + +

      以下各節說明如何完成這些工作。

      + + + +

      指定使用者介面

      + +

      如果您直接延伸 {@link android.preference.Preference} 類別,必須實作 {@link android.preference.Preference#onClick()} 以定義使用者選取項目時的動作。 + +不過,大多數自訂設定會延伸 {@link android.preference.DialogPreference} 來顯示對話方塊,以簡化程序。 +延伸 {@link +android.preference.DialogPreference} 時,您必須在類別建構函式期間呼叫 {@link +android.preference.DialogPreference#setDialogLayoutResource setDialogLayoutResourcs()},以指定對話方塊的版面配置。 +

      + +

      例如,下列為自訂 {@link +android.preference.DialogPreference} 的建構函式,該偏好設定宣告版面配置並指定預設正值和負值對話方塊按鈕的文字: +

      + +
      +public class NumberPickerPreference extends DialogPreference {
      +    public NumberPickerPreference(Context context, AttributeSet attrs) {
      +        super(context, attrs);
      +        
      +        setDialogLayoutResource(R.layout.numberpicker_dialog);
      +        setPositiveButtonText(android.R.string.ok);
      +        setNegativeButtonText(android.R.string.cancel);
      +        
      +        setDialogIcon(null);
      +    }
      +    ...
      +}
      +
      + + + +

      儲存設定值

      + +

      您可以呼叫其中一個 {@link +android.preference.Preference} 類別的 {@code persist*()} 方法隨時儲存設定值,例如,如果設定值是整數,使用 {@link +android.preference.Preference#persistInt persistInt()},或者使用 {@link android.preference.Preference#persistBoolean persistBoolean()} 儲存布林值。 +

      + +

      注意:每個 {@link android.preference.Preference} 只能儲存一個資料類型,因此您必須使用適合您自訂 {@link android.preference.Preference} 使用之資料類型的 {@code persist*()} 方法。 + +

      + +

      當您選擇保留設定時,可以依據您延伸的 {@link +android.preference.Preference} 類別加以設定。如果您延伸 {@link +android.preference.DialogPreference},則應該只在對話方塊因正值結果 (使用者選取 [確定] 按鈕) 關閉時保留該值。 +

      + +

      當 {@link android.preference.DialogPreference} 關閉時,系統會呼叫 {@link +android.preference.DialogPreference#onDialogClosed onDialogClosed()} 方法。這個方法包括一個布林值引數,指定使用者結果是否為「正值」— 如果值為 +true,則表示使用者選取正值按鈕,而您應該儲存新值。 +例如: +

      + +
      +@Override
      +protected void onDialogClosed(boolean positiveResult) {
      +    // When the user selects "OK", persist the new value
      +    if (positiveResult) {
      +        persistInt(mNewValue);
      +    }
      +}
      +
      + +

      在此範例中,mNewValue 是保留設定目前值的類別成員。 +呼叫 {@link android.preference.Preference#persistInt persistInt()} 可將值儲存到 {@link android.content.SharedPreferences} 檔案 (自動使用 XML 檔案中為此 {@link android.preference.Preference} 指定的索引鍵)。 + +

      + + +

      初始化目前值

      + +

      當系統將您的 {@link android.preference.Preference} 新增到畫面時,會呼叫 {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} 以通知您設定是否有持續值。 + +如果沒有持續值,這個呼叫會提供您預設值。 +

      + +

      {@link android.preference.Preference#onSetInitialValue onSetInitialValue()} 方法會傳送布林值 restorePersistedValue,以指出該設定的值是否已經持續。 + +如果是 true,則您應該呼叫其中一個 {@link +android.preference.Preference} 類別的 {@code getPersisted*()} 方法以擷取持續值,例如,如果為整數值,可使用 {@link +android.preference.Preference#getPersistedInt getPersistedInt()}。 +您通常都會想要擷取持續值,如此您可以正確的更新 UI 以反映之前儲存的值。 + +

      + +

      如果 restorePersistedValuefalse,則您應該使用在第二引數中傳送的預設值。 +

      + +
      +@Override
      +protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
      +    if (restorePersistedValue) {
      +        // Restore existing state
      +        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
      +    } else {
      +        // Set default state from the XML attribute
      +        mCurrentValue = (Integer) defaultValue;
      +        persistInt(mCurrentValue);
      +    }
      +}
      +
      + +

      在實際上沒有持續值或沒有索引鍵的情況下,每個 {@code getPersisted*()} 方法會採用指定要使用之預設值的引數。 +在上述範例中,會使用本機常數來指定預設值,以避免 {@link +android.preference.Preference#getPersistedInt getPersistedInt()} 無法傳回持續值。 +

      + +

      注意:無法使用 defaultValue 作為 {@code getPersisted*()} 方法中的預設值,因為當 restorePersistedValuetrue 時,其值永遠是 null。 + +

      + + +

      提供預設值

      + +

      如果 {@link android.preference.Preference} 類別的執行個體指定預設值 (包含 {@code android:defaultValue} 屬性),則系統會在將物件具現化時呼叫 {@link android.preference.Preference#onGetDefaultValue +onGetDefaultValue()},以擷取值。 + +您必須實作此方法,才能讓系統將預設值儲存在 {@link +android.content.SharedPreferences} 中。 +例如:

      + +
      +@Override
      +protected Object onGetDefaultValue(TypedArray a, int index) {
      +    return a.getInteger(index, DEFAULT_VALUE);
      +}
      +
      + +

      方法引數可以提供您所需的所有物件:屬性陣列,以及您必須擷取之 {@code android:defaultValue} 的索引位置。 +您必須實作此方法以便從屬性擷取預設值的原因,是因為您必須在未定義值的情況為屬性指定一個本機預設值。 + +

      + + + +

      儲存和還原偏好設定狀態

      + +

      如同版面配置中的 {@link android.view.View},您的 {@link android.preference.Preference} 子類別負責在 Activity 或片段重新啟動 (例如,當使用者旋轉螢幕時) 時,儲存和還原其狀態。 + +如要正確的儲存和還原 {@link android.preference.Preference} 類別的狀態,您必須實作生命週期回呼方法 {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()}。 + +

      + +

      您 {@link android.preference.Preference} 的狀態由實作 {@link android.os.Parcelable} 介面的物件定義。 +Android 架構為您提供這類物件作為定義狀態物件的起始點:{@link +android.preference.Preference.BaseSavedState} 類別。 +

      + +

      如要定義 {@link android.preference.Preference} 類別儲存狀態的方法,您應該延伸 {@link android.preference.Preference.BaseSavedState} 類別。 +您只需要覆寫幾個方法,然後定義 {@link android.preference.Preference.BaseSavedState#CREATOR}物件。 + +

      + +

      對於大多數應用程式而言,如果您的 {@link android.preference.Preference} 子類別儲存整數以外的資料類型,您可以複製下列實作,然後變更處理 {@code value} 的行即可。 + +

      + +
      +private static class SavedState extends BaseSavedState {
      +    // Member that holds the setting's value
      +    // Change this data type to match the type saved by your Preference
      +    int value;
      +
      +    public SavedState(Parcelable superState) {
      +        super(superState);
      +    }
      +
      +    public SavedState(Parcel source) {
      +        super(source);
      +        // Get the current preference's value
      +        value = source.readInt();  // Change this to read the appropriate data type
      +    }
      +
      +    @Override
      +    public void writeToParcel(Parcel dest, int flags) {
      +        super.writeToParcel(dest, flags);
      +        // Write the preference's value
      +        dest.writeInt(value);  // Change this to write the appropriate data type
      +    }
      +
      +    // Standard creator object using an instance of this class
      +    public static final Parcelable.Creator<SavedState> CREATOR =
      +            new Parcelable.Creator<SavedState>() {
      +
      +        public SavedState createFromParcel(Parcel in) {
      +            return new SavedState(in);
      +        }
      +
      +        public SavedState[] newArray(int size) {
      +            return new SavedState[size];
      +        }
      +    };
      +}
      +
      + +

      將上述 {@link android.preference.Preference.BaseSavedState} 實作新增到您的應用程式 (通常做為 {@link android.preference.Preference} 子類別的子類別) 之後,您需要針對 {@link android.preference.Preference} 子類別實作 {@link android.preference.Preference#onSaveInstanceState +onSaveInstanceState()} 和 {@link +android.preference.Preference#onRestoreInstanceState onRestoreInstanceState()} 方法。 + + +

      + +

      例如:

      + +
      +@Override
      +protected Parcelable onSaveInstanceState() {
      +    final Parcelable superState = super.onSaveInstanceState();
      +    // Check whether this Preference is persistent (continually saved)
      +    if (isPersistent()) {
      +        // No need to save instance state since it's persistent,
      +        // use superclass state
      +        return superState;
      +    }
      +
      +    // Create instance of custom BaseSavedState
      +    final SavedState myState = new SavedState(superState);
      +    // Set the state's value with the class member that holds current
      +    // setting value
      +    myState.value = mNewValue;
      +    return myState;
      +}
      +
      +@Override
      +protected void onRestoreInstanceState(Parcelable state) {
      +    // Check whether we saved the state in onSaveInstanceState
      +    if (state == null || !state.getClass().equals(SavedState.class)) {
      +        // Didn't save the state, so call superclass
      +        super.onRestoreInstanceState(state);
      +        return;
      +    }
      +
      +    // Cast state to custom BaseSavedState and pass to superclass
      +    SavedState myState = (SavedState) state;
      +    super.onRestoreInstanceState(myState.getSuperState());
      +    
      +    // Set this Preference's widget to reflect the restored state
      +    mNumberPicker.setValue(myState.value);
      +}
      +
      + diff --git a/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd b/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd new file mode 100644 index 0000000000000000000000000000000000000000..68714e8b2b5918e649d50e443bf7bb48fe4d9900 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/guide/topics/ui/ui-events.jd @@ -0,0 +1,291 @@ +page.title=輸入事件 +parent.title=使用者介面 +parent.link=index.html +@jd:body + +
      +
      +

      本文件內容

      +
        +
      1. 事件接聽器
      2. +
      3. 事件處理常式
      4. +
      5. 輕觸模式
      6. +
      7. 處理焦點
      8. +
      + +
      +
      + +

      在 Android,有多種方法可以攔截使用者與應用程式互動的事件。 +如果考慮的是使用者介面內的事件,方法就是從與使用者互動的特定檢視物件中擷取事件。 +檢視類別提供執行此動作的方法。

      + +

      在您用來撰寫版面配置的各種檢視類別中,您會發現多種公用回呼方法,對 UI 事件似乎相當實用。 +當該物件發生個別動作時,Android 架構會呼叫這些方法。 +例如,輕觸檢視 (例如按鈕) 時,會在該物件上呼叫 onTouchEvent() 方法。 +不過,為了攔截這個事件,您必須延伸類別並覆寫方法。 +然而,延伸每個檢視物件以便處理這類事件並不實際。 +這就是檢視類別也包含巢狀介面與回呼的集合的原因,這樣您就能更輕鬆地定義。 +這些介面稱為事件接聽器,就是用來擷取使用者與您 UI 互動的票券。 +

      + +

      雖然您更常使用事件接聽器來接聽使用者互動,未來您可能會想要延伸檢視類別以建置自訂元件。 +或許您想要延伸 {@link android.widget.Button} 類別讓一些項目更為出色。 + +在這種情況下,您能夠使用類別事件處理常式來為您的類別定義預設事件行為。 +

      + + +

      事件接聽器

      + +

      事件接聽器是 {@link android.view.View} 類別中的一個介面,該類別包含單一回呼方法。 +當使用者與 UI 中的項目互動並觸發接聽器所註冊的檢視時,Android 架構就會呼叫這些方法。 +

      + +

      事件接聽器介面包含下列回呼方法:

      + +
      +
      onClick()
      +
      從 {@link android.view.View.OnClickListener}。使用者輕觸項目 (處於輕觸模式時),或將焦點放在具有導覽鍵或軌跡球的項目上,然後按適當的 "enter" 鍵或按下軌跡球時,就會呼叫。 + + +
      +
      onLongClick()
      +
      從 {@link android.view.View.OnLongClickListener}。使用者輕觸並按住項目 (處於輕觸模式時),或將焦點放在具有導覽鍵或軌跡球的項目上,然後按住適當的 "enter" 鍵或按住軌跡球 (一秒) 時,就會呼叫。 + + +
      +
      onFocusChange()
      +
      從 {@link android.view.View.OnFocusChangeListener}。使用者使用導覽鍵或軌跡球導覽至項目或離開項目時,就會呼叫。 +
      +
      onKey()
      +
      從 {@link android.view.View.OnKeyListener}。使用者將焦點放在項目上,然後按下或放開裝置上的硬體鍵時,就會呼叫。 +
      +
      onTouch()
      +
      從 {@link android.view.View.OnTouchListener}。當使用者執行符合輕觸事件資格的動作時,包括按下、放開或在螢幕上的任何移動手勢 (在項目邊界內),就會呼叫。 + +
      +
      onCreateContextMenu()
      +
      從 {@link android.view.View.OnCreateContextMenuListener}。建置操作選單時 (產生持續「長按」的效果),就會呼叫。 +請參閱選單開發人員指南的操作選單相關討論。 + +
      +
      + +

      這些方法是其個別介面的唯一要素。如要定義其中一種方法並處理您的事件,可在您的 Activity 中實作巢狀介面或將它定義為匿名類別。然後,將您的實作執行個體傳送至個別的 View.set...Listener() 方法。 + + +(例如,呼叫 +{@link android.view.View#setOnClickListener(View.OnClickListener) setOnClickListener()},將您的 {@link android.view.View.OnClickListener OnClickListener} 實作傳送給它。) +

      + +

      下列範例說明如何為某個按鈕註冊 on-click 接聽器。

      + +
      +// Create an anonymous implementation of OnClickListener
      +private OnClickListener mCorkyListener = new OnClickListener() {
      +    public void onClick(View v) {
      +      // do something when the button is clicked
      +    }
      +};
      +
      +protected void onCreate(Bundle savedValues) {
      +    ...
      +    // Capture our button from layout
      +    Button button = (Button)findViewById(R.id.corky);
      +    // Register the onClick listener with the implementation above
      +    button.setOnClickListener(mCorkyListener);
      +    ...
      +}
      +
      + +

      您也會發現在 Activity 中實作 OnClickListener 會更為方便。這樣可避免額外的類別載入和物件配置。 +例如:

      +
      +public class ExampleActivity extends Activity implements OnClickListener {
      +    protected void onCreate(Bundle savedValues) {
      +        ...
      +        Button button = (Button)findViewById(R.id.corky);
      +        button.setOnClickListener(this);
      +    }
      +
      +    // Implement the OnClickListener callback
      +    public void onClick(View v) {
      +      // do something when the button is clicked
      +    }
      +    ...
      +}
      +
      + +

      請注意,上述範例的 onClick() 回呼不會傳回任何值,但某些其他事件接聽器方法必須傳回布林值。 +這個原因取決於事件。 +少數執行此事件的原因如下:

      +
        +
      • {@link android.view.View.OnLongClickListener#onLongClick(View) onLongClick()} - 這會傳回一個布林值,指出您是否已使用此事件,未來不應該繼續執行。 +亦即,傳回 true 指出您已處理事件,應該在這裡停止;如果您未處理事件和/或事件應該繼續在任何其他 on-click 接聽器上執行,則會傳回 false。 + + +
      • +
      • {@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent) onKey()} - 這會傳回一個布林值,指出您是否已使用此事件,未來不應該繼續執行。 + + 亦即,傳回 true 指出您已處理事件,應該在這裡停止;如果您未處理事件和/或事件應該繼續在任何其他 on-key 接聽器上執行,則會傳回 false。 + +
      • +
      • {@link android.view.View.OnTouchListener#onTouch(View,MotionEvent) onTouch()} - 這會傳回一個布林值,指出您的接聽器是否已使用此事件。 +最重要的是,這個事件可以處理彼此接續的多個動作。 +因此,收到向下動作事件時如果您傳回 false,就表示您尚未使用事件,而且對於這個事件的後續動作也不感興趣。 + +因此,您不需要在事件內執行任何其他動作,例如手指手勢或最終動作事件。 +
      • +
      + +

      請記住,硬體按鍵事件一律會傳送至目前焦點中的檢視。它們會從檢視階層的最上層開始發送,然後往下直到到達適當的目的地為止。 +如果焦點現在位於您的檢視 (或檢視的子項),則您可以透過 {@link android.view.View#dispatchKeyEvent(KeyEvent) +dispatchKeyEvent()} 方法查看事件過程。 +作為透過您的檢視擷取按鍵事件的替代方法,您也可以使用 {@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()}{@link android.app.Activity#onKeyUp(int,KeyEvent) onKeyUp()} 接收您 Activity 內的所有事件。 + +

      + +

      此外,當您思考應用程式的文字輸入時,請記住,許多裝置只有軟體輸入方法。 +這類方法不需要以按鍵為基礎;有些可能會使用語音輸入、手寫等方式。即使輸入方法出現類似鍵盤的介面,也通常不會觸發事件的 +{@link android.app.Activity#onKeyDown(int,KeyEvent) onKeyDown()} 系列。 +您不應該建置需要按下特定按鍵才能控制的 UI,除非您想將應用程式限制為使用硬體鍵盤的裝置。 + +尤其是當使用者按 Return 鍵時,不要藉助這些方法來驗證輸入;改為使用像 {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} 的動作向輸入方法示意您應用程式預期的反應方式,讓它能夠以有意義的方式變更其 UI。 + +避免假設軟體輸入方法應該會如何運作,只要相信它能為您的應用程式提供既有的格式化文字。 +

      + +

      注意:Android 會先呼叫事件處理常式,然後再從類別定義呼叫適當的預設處理常式。 +因此,從這些事件接聽器傳回 true 將會停止將事件傳播到其他事件接聽器,也會封鎖對檢視中預設事件處理常式的回呼。 + +因此,要確定您傳回 true 時要終止事件。

      + + +

      事件處理常式

      + +

      如果您正從檢視建置自訂元件,則您就能定義多種回呼方法做為預設事件處理常式。在自訂元件的相關文件中,您將瞭解事件處理常式的一些常見回呼,包括: + + + +

      +
        +
      • {@link android.view.View#onKeyDown} - 當發生新的按鍵事件時就會進行呼叫。
      • +
      • {@link android.view.View#onKeyUp} - 當發生向上鍵事件時就會進行呼叫。
      • +
      • {@link android.view.View#onTrackballEvent} - 當發生軌跡球動作事件時就會進行呼叫。
      • +
      • {@link android.view.View#onTouchEvent} - 當發生觸控螢幕動作事件時就會進行呼叫。
      • +
      • {@link android.view.View#onFocusChanged} - 當檢視獲得或失去焦點時就會進行呼叫。
      • +
      +

      您還必須注意其他一些方法,這些方法不屬於檢視類別,但會直接影響您能夠處理事件的方式。 +因此,在版面配置中管理更複雜的事件時,請考量下列其他方法: +

      +
        +
      • {@link android.app.Activity#dispatchTouchEvent(MotionEvent) + Activity.dispatchTouchEvent(MotionEvent)} - 這讓您的 {@link +android.app.Activity} 能在發送至視窗之前攔截所有輕觸事件。
      • +
      • {@link android.view.ViewGroup#onInterceptTouchEvent(MotionEvent) + ViewGroup.onInterceptTouchEvent(MotionEvent)} - 這讓 {@link +android.view.ViewGroup} 在發送至子檢視時能監控事件。
      • +
      • {@link android.view.ViewParent#requestDisallowInterceptTouchEvent(boolean) + ViewParent.requestDisallowInterceptTouchEvent(boolean)} - 呼叫這個父檢視以指出不應該使用 {@link + android.view.ViewGroup#onInterceptTouchEvent(MotionEvent)} 攔截輕觸事件。 +
      • +
      + +

      輕觸模式

      +

      +使用者以方向鍵或軌跡球瀏覽使用者介面時,必須對可動作的項目 (例如按鈕) 提供焦點,這樣使用者就能看到什麼項目將接受輸入。 + +不過,如果裝置具有輕觸功能,而且使用者透過輕觸方式開始與介面互動,就不需要再將項目反白顯示,或是對特定檢視提供焦點。 + +因此,這就是名稱為「輕觸模式」的互動模式。 + +

      +

      +如果是具備輕觸功能的裝置,使用者輕觸螢幕之後,裝置就會進入輕觸模式。 +從這點以此類推,只有 +{@link android.view.View#isFocusableInTouchMode} 為 true 的檢視才可設定焦點,例如文字編輯小工具。 +其他可輕觸的檢視,例如按鈕,在輕觸時不會成為焦點;按下時,只會觸發 on-click 接聽器。 + +

      +

      +使用者在任何時候點擊方向鍵或使用軌跡球捲動時,裝置會結束輕觸模式,然後找尋要成為焦點的檢視。 +現在,使用者可繼續與使用者介面互動,而不必輕觸螢幕。 + +

      +

      +整個系統 (所有視窗和 Activity) 都會保留輕觸模式的狀態。如要查詢目前的狀態,您可以呼叫 +{@link android.view.View#isInTouchMode} 以查看裝置目前是否處於輕觸模式。 + +

      + + +

      處理焦點

      + +

      架構會處理慣例焦點移動以回應使用者輸入。 +這包括在移除或隱藏檢視時變更焦點,或是新檢視可用時。 +檢視可透過 {@link android.view.View#isFocusable()} 方法指出它們成為焦點的意願。 +如要變更檢視是否可成為焦點,可呼叫 {@link android.view.View#setFocusable(boolean) setFocusable()}。 +處於輕觸模式時,您可使用 {@link android.view.View#isFocusableInTouchMode()} 查詢檢視是否允許成為焦點。 + +您可以使用 {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode()} 進行變更。 +

      + +

      焦點移動是以演算法為依據,在指定的方向尋找最接近的項目。 +在少數情況下,預設的演算法與開發人員預期的行為可能不符。 +在這些情況下,您可以在版面配置檔案使用下列 XML 屬性進行明確的覆寫: + +nextFocusDownnextFocusLeftnextFocusRight和 +nextFocusUp。在移出焦點的檢視中,新增下列其中一個屬性。 +將屬性的值定義為應該成為焦點之檢視的 ID。 +例如:

      +
      +<LinearLayout
      +    android:orientation="vertical"
      +    ... >
      +  <Button android:id="@+id/top"
      +          android:nextFocusUp="@+id/bottom"
      +          ... />
      +  <Button android:id="@+id/bottom"
      +          android:nextFocusDown="@+id/top"
      +          ... />
      +</LinearLayout>
      +
      + +

      一般來說,在這個直式版面配置中,從第一個按鈕往上瀏覽或從第二個按鈕往下瀏覽都不會到任何地方。 +現在,上方按鈕已將下方按鈕定義為 + nextFocusUp (反之亦然),導覽焦點會從上到下以及從下到上循環。 +

      + +

      如果您想在 UI 中將檢視宣告為可設定焦點 (傳統上不行),可在您的版面配置宣告中將 android:focusable XML 屬性新增至檢視。 + +將值設定為 true。您也可以使用 android:focusableInTouchMode 在輕觸模式中將檢視宣告為可設定焦點。 +

      +

      如要要求特定檢視成為焦點,可呼叫 {@link android.view.View#requestFocus()}

      +

      如要接聽焦點事件 (在檢視獲得或失去焦點時收到通知),可使用 +{@link android.view.View.OnFocusChangeListener#onFocusChange(View,boolean) onFocusChange()} (如上述事件接聽器中所討論)。 +

      + + + + diff --git a/docs/html-intl/intl/zh-tw/index.jd b/docs/html-intl/intl/zh-tw/index.jd index cf1376facee794c8284c15aea3d7f0220aea89db..a5772ef0fab01d102ab21de4dda4cf36809948ff 100644 --- a/docs/html-intl/intl/zh-tw/index.jd +++ b/docs/html-intl/intl/zh-tw/index.jd @@ -5,42 +5,81 @@ page.customHeadTag= -
      --> - +
    -
    + + + -
    +

    Build Beautiful Apps

    Resources to get you started with designing and developing for Android. diff --git a/docs/html-intl/intl/zh-tw/preview/api-overview.jd b/docs/html-intl/intl/zh-tw/preview/api-overview.jd deleted file mode 100644 index a68f0cdfa159531f6c43381dff2f9daea70a4ef0..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/api-overview.jd +++ /dev/null @@ -1,521 +0,0 @@ -page.title=API 總覽 -page.keywords=預覽,sdk,相容性 -page.tags=previewresources, androidm -sdk。platform.apiLevel=22-mnc -page.image=images/cards/card-api-overview_16-9_2x.png -@jd:body - - - - -

    M 開發人員預覽版讓您能夠搶先查看即將發行的 Android 平台版本,這個版本將提供可供使用者和應用程式開發人員使用的新功能。 - -本文件提供最值得受到矚目的 API 簡介。

    - -

    M 開發人員預覽版的適用對象是早期採用的開發人員測試者。 -如果您對於如何影響 Android 架構方向深感興趣,請嘗試使用 M 開發人員預覽版,然後將您的意見反應傳送給我們! - - -

    - -

    注意:請勿將使用 M 開發人員預覽版的應用程式發行到 Google Play 商店。 -

    - -

    注意:本文件通常會參考 developer.android.com 上尚未提供可用參考資料的類別和方法。 -這些 API 元素在本文件中的格式是 {@code code style} (不含超連結)。 -如需這些元素的 API 初稿文件,請下載預覽版參考資料。 -

    - -

    重要行為變更

    - -

    如果您先前曾發行過適用於 Android 的應用程式,請注意,您的應用程式會受到平台中的變更所影響。 -

    - -

    如需完整資訊,請參閱行為變更

    - -

    應用程式連結

    -

    這個預覽版提供功能更強大的應用程式連結來增強 Android 的意圖系統。此功能讓您能夠將應用程式關聯到您自己的 Web 網域。 -根據這個關聯,平台可以判斷要用來處理特定 Web 連結的預設應用程式,並略過提示使用者選取應用程式的程序。如要深入瞭解如何實作此功能,請參閱應用程式連結。 - - - - -

    針對應用程式進行自動備份

    -

    系統現在會針對應用程式執行自動完整資料備份與還原。預設會針對目標為 M 預覽版的應用程式啟用這個行為;您不需要新增任何其他的程式碼。 -如果使用者刪除他們的 Google 帳戶,也會同時刪除他們的備份資料。 -如要深入瞭解此功能的運作方式以及如何在檔案系統上設定要備份的內容,請參閱針對應用程式進行自動備份。 - -

    - -

    驗證

    -

    這個預覽版提供新的 API,讓您能夠在支援的裝置上利用使用者的指紋掃描來驗證他們,並使用裝置解鎖機制 (例如鎖定螢幕密碼) 來檢查距離最後一次驗證該使用者的時間有多接近。 - -將這些 API 與 Android 金鑰存放區系統搭配使用。 -

    - -

    指紋驗證

    - -

    如要透過指紋掃描驗證使用者,請取得新的 -{@code android.hardware.fingerprint.FingerprintManager} 類別的執行個體,然後呼叫 -{@code FingerprintManager.authenticate()} 方法。您的應用程式必須在配備指紋感應器的相容裝置上執行。 -您必須在應用程式上實作適用於指紋驗證流程的使用者介面,並在您的 UI 中使用標準的 Android 指紋圖示。Android 指紋圖示 ({@code c_fp_40px.png}) 隨附於範例應用程式中。如果您正在開發多個使用指紋驗證的應用程式,請注意,每個應用程式都必須個別驗證使用者的指紋。 - - - - -

    - -

    如要在您的應用程式中使用此功能,請先在您的宣示說明中新增 {@code USE_FINGERPRINT} 權限。 -

    - -
    -<uses-permission
    -        android:name="android.permission.USE_FINGERPRINT" />
    -
    - - - -

    如要查看指紋驗證的應用程式實作,請參閱指紋對話方塊範例。 - -

    - -

    如果您正在測試此功能,請依照下列步驟執行:

    -
      -
    1. 安裝 Android SDK 工具修訂版 24.3 (如果您尚未安裝)。
    2. -
    3. 前往 [設定] > [安全性] > [指紋],然後依照註冊指示,在模擬器中註冊新的指紋。 -
    4. -
    5. 使用模擬器,利用下列命令來模擬指紋輕觸事件。 -使用同一個命令,在鎖定螢幕上或您的應用程式中模擬指紋輕觸事件。 - -
      -adb -e emu finger touch <finger_id>
      -
      -

      在 Windows 上,您可能必須執行 {@code telnet 127.0.0.1 },後面接著 -{@code finger touch }。 -

      -
    6. -
    - -

    確認認證

    -

    您的應用程式可以根據使用者最近一次將裝置解鎖的時間有多接近來驗證他們。此功能讓使用者不需記住其他應用程式特定的密碼,並且讓您不需要實作自己的驗證使用者介面。 - -您的應用程式應該將此功能與公用或秘密金鑰實作搭配使用,來進行使用者驗證。 -

    - -

    如要設定在成功驗證使用者之後,同一個金鑰可重複使用的逾時時間長度,可在您設定 {@link javax.crypto.KeyGenerator} 或 -{@link java.security.KeyPairGenerator} 時,呼叫新的 -{@code android.security.keystore.KeyGenParameterSpec.setUserAuthenticationValidityDurationSeconds()} -方法。 -此功能目前適用於對稱式密碼編譯操作。 -

    - -

    避免過度顯示重新驗證對話方塊 -- 您的應用程式應該先嘗試使用密碼編譯物件,如果逾時過期,才使用 -{@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence) createConfirmDeviceCredentialIntent()} -方法在您的應用程式內重新驗證使用者。 - -

    - -

    如要查看此功能的應用程式實作,請參閱確認認證範例。 - -

    - -

    直接分享

    - - - -

    這個預覽版提供 API,讓使用者能夠以直覺且快速的方式進行分享。您現在可以定義「直接分享目標」,在您的應用程式中啟動特定的行為。這些直接分享目標是透過 [分享] 選單來向使用者公開。 - -此功能讓使用者能夠將內容分享到其他應用程式內的目標,例如聯絡人。 -例如,直接分享目標可以在其他社交網路應用程式中啟動某個活動,讓使用者能夠在該應用程式中,直接與特定的朋友或社群分享內容。 - -

    - -

    如要啟用直接分享目標,您必須定義一個類別來擴充 -{@code android.service.}
    -{@code chooser.ChooserTargetService} 類別。在宣示說明中宣告您的 -{@code ChooserTargetService}。在該宣告中,指定 -{@code BIND_CHOOSER_TARGET_SERVICE} 權限以及含有 -{@code SERVICE_INTERFACE} 動作的意圖篩選條件。

    -

    下列範例示範如何在您的宣示說明中宣告 {@code ChooserTargetService}。 -

    -
    -<service android:name=".ChooserTargetService"
    -        android:label="@string/service_name"
    -        android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
    -    <intent-filter>
    -        <action android:name="android.service.chooser.ChooserTargetService" />
    -    </intent-filter>
    -</service>
    -
    - -

    針對您要向 {@code ChooserTargetService} 公開的每個活動,在您的應用程式宣示說明中,新增名稱為 -{@code "android.service.chooser.chooser_target_service"} 的 -{@code } 元素。 -

    - -
    -<activity android:name=".MyShareActivity”
    -        android:label="@string/share_activity_label">
    -    <intent-filter>
    -        <action android:name="android.intent.action.SEND" />
    -    </intent-filter>
    -<meta-data
    -        android:name="android.service.chooser.chooser_target_service"
    -        android:value=".ChooserTargetService" />
    -</activity>
    -
    - -

    語音互動

    -

    -這個預覽版提供新的語音互動 API,可與語音操作搭配使用,讓您能夠在應用程式內建置交談式語音體驗。 - -呼叫 -{@code android.app.Activity.isVoiceInteraction()} 方法,來判斷是否已啟動您的活動來回應語音操作。 -如果是,則您的應用程式可以使用 -{@code android.app.VoiceInteractor} 類別,來要求使用者進行語音確認、從選項清單中選取,以及其他更多動作。 -如要深入瞭解如何實作語音操作,請參閱語音操作開發人員網站。 - -

    - -

    協助 API

    -

    -這個預覽版提供一種新方式,可透過小幫手吸引使用者來使用您的應用程式。如要使用此功能,使用者必須啟用小幫手來使用目前的內容。 -啟用之後,使用者就能夠在所有應用程式內,按住 Home 按鈕不放來啟用小幫手。 -

    -

    您的應用程式可以設定 -{@link android.view.WindowManager.LayoutParams#FLAG_SECURE} 旗標,選擇不要與小幫手分享目前的內容。除了平台傳遞給小幫手的一組標準資訊之外,您的應用程式也可以使用新的 {@code android.app.Activity.AssistContent} 類別來分享其他資訊。 - -

    - -

    如要將您應用程式的其他內容提供給小幫手,請依照下列步驟執行:

    - -
      -
    1. 實作 {@link android.app.Application.OnProvideAssistDataListener} 介面。
    2. -
    3. 使用 -{@link android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()} 來註冊這個監聽器。
    4. -
    5. 如要提供特定活動的內容資訊,請覆寫 - {@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} -回呼,然後選擇性地覆寫新的 {@code Activity.onProvideAssistContent()} 回呼。 -
    - -

    通知

    -

    這個預覽版新增了下列適用於通知的 API 變更:

    -
      -
    • 新的 {@code NotificationListenerService.INTERRUPTION_FILTER_ALARMS} 篩選條件級別,會對應至新的「僅允許鬧鐘」的請勿打擾模式。 -
    • -
    • 新的 {@code Notification.CATEGORY_REMINDER} 類別值,可用來分辨來自其他事件 ({@link android.app.Notification#CATEGORY_EVENT}) 與鬧鐘 ({@link android.app.Notification#CATEGORY_ALARM}) 的使用者排程提醒。 - - -
    • -
    • 新的 {@code android.graphics.drawable.Icon} 類別,可以透過 {@code Notification.Builder.setSmallIcon(Icon)} 和 -{@code Notification.Builder.setLargeIcon(Icon)} 方法來附加您的通知。 -
    • -
    • 新的 {@code NotificationManager.getActiveNotifications()} 方法,讓您的應用程式能夠找出它們目前有哪些通知仍處於有效狀態。 -如要查看使用此功能的應用程式實作,請參閱使用中通知範例。 -
    • -
    - -

    藍牙手寫筆支援

    -

    這個預覽版提供對於使用者使用藍牙手寫筆進行輸入的改良支援。使用者可以將相容的藍芽手寫筆與他們的手機或平板電腦配對並連線。 -連線之後,來自觸控螢幕的位置資訊會與來自手寫筆的壓力和按鈕資訊結合,比起單獨使用觸控螢幕,這樣能夠提供更大範圍的表達方式。 - -您的應用程式可以藉由在您的活動中註冊新的 -{@code View.onStylusButtonPressListener} 和 {@code GestureDetector.OnStylusButtonPressListener} -回呼,來監聽手寫筆按鈕的按下動作,並執行次要動作。 -

    - -

    使用 {@link android.view.MotionEvent} 方法和常數來偵測手寫筆按鈕互動: -

    -
      -
    • 如果使用者使用具有一個按鈕的手寫筆輕觸應用程式的螢幕,則 -{@link android.view.MotionEvent#getToolType(int) getTooltype()} 方法會傳回 -{@link android.view.MotionEvent#TOOL_TYPE_STYLUS}。
    • -
    • 針對目標為 M 預覽版的應用程式, -{@link android.view.MotionEvent#getButtonState() getButtonState()} -方法會在使用者按下主要手寫筆按鈕時傳回 {@code MotionEvent.STYLUS_BUTTON_PRIMARY}。 -如果手寫筆有第二個按鈕,則當使用者按下該按鈕時,同一個方法會傳回 -{@code MotionEvent.STYLUS_BUTTON_SECONDARY}。如果使用者同時按下這兩個按鈕,此方法即會使用 OR 連結,一併傳回這兩個值 ({@code STYLUS_BUTTON_PRIMARY|STYLUS_BUTTON_SECONDARY})。 - -
    • -
    • -針對目標為較低平台版本的應用程式, -{@link android.view.MotionEvent#getButtonState() getButtonState()} 方法會傳回 -{@link android.view.MotionEvent#BUTTON_SECONDARY} (針對主要手寫筆按鈕的按下動作)、 -{@link android.view.MotionEvent#BUTTON_TERTIARY} (針對次要手寫筆按鈕的按下動作),或兩者。 -
    • -
    - -

    已改進藍牙低電量掃描

    -

    -如果您的應用程式會執行藍芽低電量掃描,就可以使用新的 -{@code android.bluetooth.le.ScanSettings.Builder.setCallbackType()} 方法,來指定如果先找到符合 -{@link android.bluetooth.le.ScanFilter} 組合的廣告封包,以及在某個時段中找不到它時,只需通知回呼。 - -比起先前平台版本中所提供的功能,這個掃描方法功能更強大且更有效率。 - -

    - -

    無線基地台 2.0 版本 1 支援

    -

    -這個預覽版在 Nexus 6 和 Nexus 9 裝置上新增對於無線基地台 2.0 版本 1 規格的支援。如要在您的應用程式中佈建無線基地台 2.0 認證,請使用 -{@link android.net.wifi.WifiEnterpriseConfig} 類別的新方法,例如 {@code setPlmn()} 和 -{@code setRealm()}。 -在 {@link android.net.wifi.WifiConfiguration} 物件中,您可以設定 -{@link android.net.wifi.WifiConfiguration#FQDN} 和 {@code providerFriendlyName} 欄位。新的 {@code ScanResult.PasspointNetwork} 屬性指出偵測到的網路是否代表無線基地台 2.0 存取點。 - - -

    - -

    4K 顯示模式

    -

    此平台現在允許應用程式能夠要求將在相容硬體中呈現的顯示解析度升級到 4K。 -如要查詢目前的實際解析度,請使用新的 -{@code android.view.Display.Mode} API。如果 UI 是使用較低的邏輯解析度來繪製,並向上升級為較高的實際解析度,請注意, -{@code Display.Mode.getPhysicalWidth()} 方法傳回的實際解析度可能會與 {@link android.view.Display#getSize(android.graphics.Point) getSize()} 報告的邏輯解析度不同。 - -

    - -

    您可以在應用程式執行時,藉由設定應用程式視窗的 {@code WindowManager.LayoutParams.preferredDisplayModeId} 屬性,來要求系統變更該應用程式中的實際解析度。 -如果您想要切換為 4K 顯示解析度,這個功能非常實用。 -儘管在 4K 顯示模式中,UI 會持續使用原始解析度 (例如 1080p) 來呈現並向上升級為 4K,但是 -{@link android.view.SurfaceView} 物件可能會以原生解析度來顯示內容。 -

    - -

    具備設計風格的 ColorStateList

    -

    針對執行 M 預覽版的裝置, -{@link android.content.res.ColorStateList} 中目前支援設計風格屬性。 -{@link android.content.res.Resources#getColorStateList(int) getColorStateList()} 和 -{@link android.content.res.Resources#getColor(int) getColor()} 方法已過時。如果您正在呼叫這些 API,請改為呼叫新的 {@code Context.getColorStateList()} 或 -{@code Context.getColor()} 方法。 -您也可以透過 {@link android.support.v4.content.ContextCompat},在 v4 appcompat 程式庫中取得這些方法。 -

    - -

    音訊功能

    - -

    這個預覽版在 Android 上新增了音訊處理的增強功能,包括:

    -
      -
    • 利用新的 {@code android.media.midi} API,來支援 MIDI 通訊協定。 -使用這些 API 來傳送與接收 MIDI 事件。 -
    • -
    • 新的 {@code android.media.AudioRecord.Builder} 和 {@code android.media.AudioTrack.Builder} -類別,可分別建立數位音訊擷取和播放物件,並設定音訊來源和接收屬性來覆寫系統預設值。 -
    • -
    • API 勾點,適合用來關聯音訊與輸入裝置。如果您的應用程式允許使用者從連接到 Android TV 的遊戲控制器或遙控器啟動音訊搜尋,則這特別有用。系統會在使用者啟動搜尋時,叫用新的 {@code android.app.Activity.onSearchRequested()} 回呼。 - - -如要判斷使用者的輸入裝置是否有內建的麥克風,請從該回呼中擷取 {@link android.view.InputDevice} 物件,然後呼叫新的 -{@code InputDevice.hasMic()} 方法。 -
    • -
    • 新的 {@code android.media.AudioDevicesManager} 類別,讓您能夠擷取所有已連接的來源與接收音訊裝置的清單。 -如果您想要讓應用程式在連接或中斷連接音訊裝置時收到通知,也可以指定 -{@code android.media.OnAudioDeviceConnectionListener} 物件。 -
    • -
    - -

    影片功能

    -

    這個預覽版在影片處理 API 中增加了新功能,包括:

    -
      -
    • 新的 {@code android.media.MediaSync} 類別,可協助應用程式同步轉譯音訊和影片串流。 -音訊緩衝區是利用非封鎖的方式來提交,並透過回呼來傳回。 -它也支援動態播放速率。 -
    • -
    • 新的 {@code MediaDrm.EVENT_SESSION_RECLAIMED} 事件,指出應用程式開啟的工作階段已由資源管理員所回收。 -如果您的應用程式使用 DRM 工作階段,就應該處理這個事件,並確定不會使用回收的工作階段。 - -
    • -
    • 新的 {@code MediaCodec.CodecException.ERROR_RECLAIMED} 錯誤碼,表示資源管理員已回收轉碼器所使用的媒體資源。 -如果發生這個例外狀況,就必須釋放轉碼器,就如同它已進入終止狀態。 - -
    • -
    • 新的 {@code MediaCodecInfo.CodecCapabilities.getMaxSupportedInstances()} 介面,可取得支援並行轉碼器執行個體數目上限的提示。 - -
    • -
    • 新的 {@code MediaPlayer.setPlaybackParams()} 方法,可將媒體播放速率設定為快速或慢速播放。 -這也可以和影片一起自動延伸或加速音訊播放。 -
    • -
    - -

    相機功能

    -

    這個預覽版包含下列可用來存取相機閃光燈和相機重新處理影像的 API: -

    - -

    閃光燈 API

    -

    如果相機裝置具有閃光裝置,則您可以呼叫 {@code CameraManager.setTorchMode()} -方法,在不開啟相機裝置的情況下,開啟或關閉閃光裝置的閃光模式。應用程式不具備閃光裝置或相機裝置的獨佔擁有權。 -每當相機裝置變成無法使用時,或者,當其他相機資源讓閃光變成無法使用時,閃光模式也會關閉且變成無法使用。 - -其他應用程式也會呼叫 {@code setTorchMode()} -來關閉閃光模式。關閉最後一個開啟閃光模式的應用程式時,閃光模式即會關閉。 -

    - -

    您可以呼叫 -{@code CameraManager.registerTorchCallback()} 方法,來註冊回呼要收到有關閃光模式狀態的通知。第一次註冊回呼時,會立即使用所有目前已知具有閃光裝置之相機裝置的閃光模式來呼叫它。 - -如果成功開啟或關閉閃光模式,即會叫用 -{@code CameraManager.TorchCallback.onTorchModeChanged()} 方法。

    - -

    重新處理 API

    -

    {@link android.hardware.camera2 Camera2} API 已擴充,支援重新處理 YUV 和私人不透明格式的影像。 -您的應用程式會判斷是否可透過 {@code CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES} 使用重新處理功能。 -如果裝置支援重新處理,您就可以呼叫 -{@code CameraDevice.createReprocessableCaptureSession()} 來建立可重新處理的相機拍攝工作階段,然後建立適用於重新處理輸入緩衝區的要求。 - -

    - -

    使用 {@code ImageWriter} 類別,將輸入緩衝區流程連接到相機重新處理輸入。 -如要取得空的緩衝區,請依照這個程式設計模型執行:

    - -
      -
    1. 呼叫 {@code ImageWriter.dequeueInputImage()} 方法。
    2. -
    3. 在輸入緩衝區中填入資料。
    4. -
    5. 呼叫 {@code ImageWriter.queueInputImage()} 方法,將緩衝區傳送到相機。
    6. -
    - -

    如果您將 {@code ImageWriter} 物件和 -{@code android.graphics.ImageFormat.PRIVATE} 影像一起使用,您的應用程式就無法直接存取影像資料。 -請改為呼叫 {@code ImageWriter.queueInputImage()} 方法但不含任何緩衝區複本,直接將 {@code ImageFormat.PRIVATE} 影像傳遞到 -{@code ImageWriter}。 -

    - -

    {@code ImageReader} 類別現在支援 {@code android.graphics.ImageFormat.PRIVATE} 格式的影像串流。 -這個支援讓您的應用程式能夠保留 -{@code ImageReader} 輸出影像的循環影像佇列、選取一或多個影像,然後將它們傳送到 -{@code ImageWriter} 以進行相機重新處理。

    - -

    Android for Work 功能

    -

    這個預覽版包含下列適用於 Android for Work 功能的新 API:

    -
      -
    • 已增強適用於公司擁有、單一用途裝置的控制項:裝置擁有者現在可以控制下列設定,來改進公司擁有、單一用途 (COSU) 裝置的管理: - - -
        -
      • 使用 -{@code DevicePolicyManager.setKeyguardEnabledState()} 方法來停用或重新啟用滑動解鎖。
      • -
      • 使用 -{@code DevicePolicyManager.setStatusBarEnabledState()} 方法,來停用或重新啟用狀態列 (包括快速設定、通知,以及啟動 Google 即時資訊的導覽向上滑動手勢)。 -
      • -
      • 使用 {@link android.os.UserManager} 常數 -{@code DISALLOW_SAFE_BOOT} 來停用或重新啟用安全開機。
      • -
      • 使用 - {@link android.provider.Settings.Global} 常數 {@code STAY_ON_WHILE_PLUGGED_IN},防止螢幕在使用者為裝置連接電源時關閉。
      • -
      -
    • -
    • 透過裝置擁有者自動安裝與解除安裝應用程式:裝置擁有者現在可以使用 {@link android.content.pm.PackageInstaller} -API (與 Google Play for Work 無關) 自動安裝與解除安裝應用程式。 -您現在可以透過裝置擁有者佈建裝置,該裝置擁有者可在不與使用者互動的情況下擷取並安裝應用程式。 -如要在不啟用 Google 帳戶的情況下輕觸一次就能佈建 Kiosk 或其他這類裝置,這個功能非常有用。 -
    • -
    • 自動存取企業憑證:當應用程式呼叫 -{@link android.security.KeyChain#choosePrivateKeyAlias(android.app.Activity,android.security.KeyChainAliasCallback,java.lang.String[],java.security.Principal[],java.lang.String,int,java.lang.String) choosePrivateKeyAlias()} 時,在系統提示使用者選取憑證之前,設定檔或裝置擁有者現在會呼叫 {@code DeviceAdminReceiver.onChoosePrivateKeyAlias()} 方法,為提出要求的應用程式自動提供別名。 - - -這個功能讓您能夠在不與使用者互動的情況下,為受管理的應用程式授與存取憑證的權限。 -
    • -
    • 自動接受系統更新。藉由使用 -{@code DevicePolicyManager.setSystemUpdatePolicy()} 來設定系統更新原則,裝置擁有者現在可以自動接受系統更新 (例如,在 Kiosk 裝置的案例中),或者延後更新,並防止使用者進行更新,最多 30 天。 - -因此,系統管理員可以設定必須取得更新的每日時間範圍 ,例如,在 Kiosk 裝置處於未使用狀態時。 -在系統更新可供使用時,系統就會檢查工作原則控制器應用程式是否已設定系統更新原則,並據以運作。 - - -
    • -
    • -委派的憑證安裝:設定檔或裝置擁有者現在可以授與第三方廠商的應用程式呼叫這些 {@link android.app.admin.DevicePolicyManager} 憑證管理 API 的能力: - - -
        -
      • {@link android.app.admin.DevicePolicyManager#getInstalledCaCerts(android.content.ComponentName) -getInstalledCaCerts()}
      • -
      • {@link android.app.admin.DevicePolicyManager#hasCaCertInstalled(android.content.ComponentName,byte[]) -hasCaCertInstalled()}
      • -
      • {@link android.app.admin.DevicePolicyManager#installCaCert(android.content.ComponentName,byte[]) -installCaCert()}
      • -
      • {@link android.app.admin.DevicePolicyManager#uninstallCaCert(android.content.ComponentName,byte[]) -uninstallCaCert()}
      • -
      • {@link android.app.admin.DevicePolicyManager#uninstallAllUserCaCerts(android.content.ComponentName) -uninstallAllUserCaCerts()}
      • -
      • {@link android.app.admin.DevicePolicyManager#installKeyPair(android.content.ComponentName,java.security.PrivateKey,java.security.cert.Certificate,java.lang.String) -installKeyPair()}
      • -
      -
    • -
    • 企業原廠重設保護:佈建裝置擁有者時,您現在可以藉由設定 -{@code DeviceManagerPolicy.EXTRA_PROVISIONING_RESET_PROTECTION_PARAMETERS} 套件組合,來設定參數以解除鎖定原廠重設保護 (FRP)。 -NFC 程式設計人員應用程式可以在已重設裝置來解除鎖定 FRP 並佈建裝置之後提供這些參數,而不需使用先前設定的 Google 帳戶。 - -如果您並未修改這些參數,FRP 會就地保留,並防止裝置在沒有先前啟用的 Google 認證的情況下啟用。 - - -

      此外,裝置擁有者可以在 Google Play 服務上設定應用程式限制,來指定可用來解除鎖定 FRP 的替代 Google 帳戶,以取代已在裝置上啟用的帳戶。 -

      -
    • - -
    • 資料使用量追蹤。設定檔或裝置擁有者現在可以使用新的 -{@code android.app.usage.NetworkStatsManager} 方法,針對可在 [設定] > [資料] 使用量中看見的資料使用量統計資料進行查詢。 -系統會自動授與設定檔擁有者權限來查詢他們所管理之設定檔上的資料,在此同時,裝置擁有者會取得受管理的主要使用者之使用量資料的存取權限。 - -
    • -
    • 執行階段權限管理: -

      設定檔或裝置擁有者可以使用 -{@code DevicePolicyManager.setPermissionPolicy()},針對所有應用程式的所有執行階段要求設定權限原則,以提示使用者授與一般權限,或者以無訊息方式自動授與或拒絕該權限。 - -如果設定了後項原則,使用者就無法在應用程式權限畫面的 [設定] 中,修改設定檔或裝置擁有者所做的選項。 - -

    • -
    • 設定中的 VPN:VPN 應用程式現在可以在 [設定] > [更多] > [VPN] 中看見。此外,伴隨 VPN 使用量出現的通知是該 VPN 設定方式的特定通知。 - - -針對設定檔擁有者,通知是專門用來通知是否已針對受管理的設定檔、個人設定檔或兩者設定了 VPN。 -針對裝置擁有者,通知是專門用來通知是否已針對整個裝置設定了 VPN。 -
    • -
    • 工作狀態通知:每當來自受管理設定檔的應用程式在前景中有活動時,狀態列上就會出現公事包圖示。 -因此,如果直接將裝置解除鎖定至受管理設定檔中應用程式的活動,即會顯示一個快顯通知,通知使用者他們正處於工作設定檔內。 - - -
    • -
    - -

    - 如需 M 開發人員預覽版中所有 API 變更的詳細檢視,請參閱 API 差異報告。 -

    diff --git a/docs/html-intl/intl/zh-tw/preview/backup/index.jd b/docs/html-intl/intl/zh-tw/preview/backup/index.jd deleted file mode 100644 index edb8b98fb63cfe5d887efb17e1a915fece490ff0..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/backup/index.jd +++ /dev/null @@ -1,327 +0,0 @@ -page.title=針對應用程式進行自動備份 -page.tags=backup, previewresources, androidm -page.keywords=backup, autobackup, preview -page.image=images/cards/card-auto-backup_2x.png -@jd:body - -
    -
    -

    本文件內容

    -
      -
    1. 總覽
    2. -
    3. 設定資料備份
    4. -
    5. 測試備份設定
    6. -
    7. 已知問題
    8. -
    -
    -
    - -

    - 使用者通常會在應用程式中花費很多時間精力建立資料和設定偏好設定。 -如果使用者換掉壞掉的裝置或升級到新的裝置時,能夠保存裝置上的資料,就能夠讓使用者獲得良好的體驗。 -執行 Android M 預覽版系統的裝置可以協助在上述情況自動將應用程式資料備份到 Google 雲端硬碟,讓使用者獲得良好的體驗。 - -如果使用者變更或升級裝置,應用程式資料會自動儲存。 - -

    - -

    - 在執行 Android M 預覽版的裝置上安裝的所有應用程式,預設都會啟用自動備份。不需要任何額外的應用程式程式碼。 -系統則提供使用者不進行自動備份資料的選項。 -您也可以選擇限制要備份應用程式的哪些資料。 -

    - -

    - 本文件說明新的系統行為,以及如何指定備份應用程式的哪些資料。 - -

    - -

    總覽

    - -

    - 自動備份功能保存應用程式資料的方法,是將使用者裝置上建立的資料上傳到使用者的 Google 雲端硬碟帳戶,然後將資料加密。 -儲存資料不會向您或使用者收費,儲存的資料也不會計入使用者個人雲端硬碟的容量配額。 -在 M 預覽版期間,使用者最多可以為每個 Android 應用程式儲存 25 MB 的資料。 - -

    - -

    - 當裝置閒置、正在充電且連線到 Wi-Fi 網路時,每 24 小時會執行一次自動備份。 -當符合上述情況時,備份管理員服務就會將所有可用的備份資料上傳到雲端。 -當使用者轉換到新的裝置,或者解除安裝又重新安裝已備份的應用程式時,系統會進行一項還原作業,將備份的資料複製到新安裝之應用程式的資料目錄。 - - -

    - -

    - 注意:如果您的應用程式使用舊版 - Android 備份服務,這個新的行為就不適用,現有的備份行為還是如往常運作。 - -

    - - -

    自動排除的資料檔案

    - -

    - 並非所有應用程式資料都需要備份,例如暫存檔案和快取,因此自動備份服務預設會排除某些資料檔案: - -

    - -
      -
    • {@link android.content.Context#getCacheDir -getCacheDir()} 和 {@link android.content.ContextWrapper#getCodeCacheDir getCodeCacheDir()} -方法所參考目錄中的檔案。 -
    • - -
    • 位於外部儲存空間的檔案,除非這些檔案位於 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} -方法所參考的目錄中。 -
    • - -
    • {@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()} 方法所參考目錄中的檔案。 - -
    • -
    - -

    設定資料備份

    - -

    - 除了上一節所列自動排除的檔案之外,其餘由 M 預覽版裝置上安裝的任何應用程式所建立的資料一律都會備份。 -您可以使用應用程式宣示說明中的設定,進一步限制和設定要備份應用程式的哪些資料。 - -

    - -

    納入或排除資料

    - -

    - 根據您的應用程式需要的資料和您儲存資料的方式,您可能需要設定特定的規則來納入或排除某些檔案或目錄。 -自動備份服務支援使用 XML 設定檔和應用程式宣示說明來設定這些備份規則。 - -在應用程式宣示說明中,您可以依照下列範例指定備份配置設定檔: - -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    -        xmlns:tools="http://schemas.android.com/tools"
    -        package="com.my.appexample">
    -    <uses-sdk android:minSdkVersion="MNC"/>
    -    <uses-sdk android:targetSdkVersion="MNC"/>
    -    <app ...
    -        android:fullBackupContent="@xml/mybackupscheme">
    -    </app>
    -    ...
    -</manifest>
    -
    - -

    - 在此範例程式碼中,android:fullBackupContent 屬性指定應用程式開發專案的 res/xml/ 目錄中,名為 -mybackupscheme.xml 的 XML 檔案。 -此設定檔包含要備份哪些檔案的規則。 -下列範例程式碼顯示排除特定檔案不要進行備份的設定檔: - -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<full-backup-content>
    -    <exclude domain="database" path="device_info.db"/>
    -</full-backup-content>
    -
    - -

    - 此範例備份設定只排除特定資料庫檔案不要進行備份。 - 其他所有檔案都會備份。 -

    - -

    備份設定語法

    - -

    - 您可以使用備份服務設定指定要將哪些檔案納入或排除備份。 -資料備份設定 XML 檔案的語法如下所示: -

    - -
    -<full-backup-content>
    -    <include domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
    -    <exclude domain=["file" | "database" | "sharedpref" | "external" | "root"] path="string" />
    -</full-backup-content>
    -
    - -

    - 您可以使用下列元素和屬性指定要將哪些檔案納入備份或從備份排除: - -

    - -
      -
    • - <include>。如果您想要指定備份一組資源,請使用此元素,而不要讓系統預設備份應用程式的所有資料。 -當您指定 -<include> 標籤時,系統只會備份此元素「指定的資源」。 - -
    • - -
    • - <exclude>。如果您想要指定一組資源不要進行備份,請使用此元素。 -系統會備份應用程式中除了此元素指定之資源以外的其他所有資料。 - -
    • - -
    • - domain. 您想要納入或排除備份的資源類型。此屬性可以指定的有效值包括: - -
    • - -
    • -
        -
      • - root。指定資源位於應用程式的根目錄。 -
      • - -
      • - file。位於 -{@link android.content.Context#getFilesDir getFilesDir()} 方法傳回的目錄中的資源。 -
      • - -
      • - database。{@link android.content.Context#getDatabasePath getDatabasePath()} 方法或使用 -{@link android.database.sqlite.SQLiteOpenHelper} 類別傳回的資料庫。 - -
      • - -
      • - sharedpref。{@link android.content.Context#getSharedPreferences getSharedPreferences()} -方法傳回的 {@link android.content.SharedPreferences} 物件。 - -
      • - -
      • - external。指定資源位於外部儲存空間,並且位於 -{@link android.content.Context#getExternalFilesDir getExternalFilesDir()} 方法傳回的目錄中的檔案。 - -
      • - -
      • - path。您想要納入或排除備份的資源檔案路徑。 - -
      • -
      -
    • -
    - - -

    禁止資料備份

    - -

    - 您可以在應用程式的宣示說明元素中將 -android:allowBackup 屬性設定為 false,選擇不要對任何應用程式資料進行自動備份。 -此設定如下列範例程式碼所示: -

    - -
    -<?xml version="1.0" encoding="utf-8"?>
    -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    -        xmlns:tools="http://schemas.android.com/tools"
    -        package="com.my.appexample">
    -    <uses-sdk android:minSdkVersion="MNC"/>
    -    <uses-sdk android:targetSdkVersion="MNC"/>
    -    <app ...
    -        android:allowBackup="false">
    -    </app>
    -    ...
    -</manifest>
    -
    - - -

    測試備份設定

    - -

    - 如果您建立了備份設定,就要進行測試,以確保您的應用程式能正確儲存和還原資料。 - -

    - - -

    啟用備份記錄

    - -

    - 如要協助判斷備份功能如何剖析您的 XML 檔案,請在執行測試備份之前先啟用記錄功能: - -

    - -
    -$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE
    -
    - -

    測試備份

    - -

    如要手動執行備份,首先您必須呼叫下列命令以初始化備份管理員: - -

    - -
    -$ adb shell bmgr run
    -
    - -

    - 接著,您要使用下列命令,將 <PACKAGE> 參數指定為您應用程式的套件名稱,手動備份您的應用程式: - -

    - -
    -$ adb shell bmgr fullbackup <PACKAGE>
    - - -

    測試還原

    - -

    - 如要在應用程式資料備份之後手動啟動還原,請呼叫下列命令,將 <PACKAGE> 參數指定為您應用程式的套件名稱: - -

    - -
    -$ adb shell bmgr restore <PACKAGE>
    -
    - -

    - 警告:這個動作在執行還原作業之前會先停止您的應用程式,然後清除其中的資料。 - -

    - -

    - 如果解除安裝又重新安裝應用程式,就會針對應用程式啟動還原程序。應用程式安裝完成之後,就會自動從雲端還原應用程式資料。 - -

    - - -

    疑難排解備份

    - -

    - 如果您遇到問題,可以在 [設定] > [備份] 中關閉備份再開啟、將裝置重設為出廠值,或是呼叫下列命令,清除備份資料及相關的中繼資料: - - -

    - -
    $ adb shell bmgr wipe <TRANSPORT> <PACKAGE>
    - -

    - <TRANSPORT> 值前面必須加上 com.google.android.gms。 - 如要取得傳輸清單,請呼叫下列命令: -

    - -
    $ adb shell bmgr list transports
    - -

    已知問題

    - -

    以下是自動備份服務的已知問題:

    - -
      -
    • Google 雲端通訊 - 使用「Google 雲端通訊」推送通知的應用程式有一個已知問題,亦即備份由「Google 雲端通訊」註冊程序傳回的註冊 ID,會讓還原後的應用程式無法傳送推送通知。在新裝置安裝 API 之後,需要向 API 查詢新的註冊 ID,因此如果之前備份了舊的註冊 ID,就會發生問題。 - - - - -如要避免發生這個情況,請不要備份包含註冊 ID 的檔案。 - -
    • -
    diff --git a/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd b/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd deleted file mode 100644 index 405aea1e53ac69ecc659e7a77c55ddc416d34fec..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/behavior-changes.jd +++ /dev/null @@ -1,402 +0,0 @@ -page.title=行為變更 -page.keywords=預覽版,sdk,相容性 -sdk.platform.apiLevel=MNC -@jd:body - - - -

    除了新特性和功能以外,M 開發人員預覽版還包含各種不同的系統變更和 API 行為變更。 -本文件將強調說明一些您應該知道且在您的應用程式中加以考量的重要變更。 -

    - -

    如果您先前曾發行過適用於 Android 的應用程式,請注意,您的應用程式可能會受到平台中的這類變更所影響。 -

    - -

    執行階段權限

    -

    這個預覽版引進了新的權限模型,使用者現在可以在執行階段直接管理應用程式權限。 -這個模型為使用者提供了改良的能見度並使其可完全控制權限,同時為應用程式開發人員提供更流暢的安裝和自動更新程序。使用者可以針對安裝的應用程式個別授與或撤銷權限。 - -

    - -

    在目標為 M 預覽版的應用程式中,請務必在執行階段檢查並要求權限。 -如要判斷您的應用程式是否已獲授與權限,請呼叫新的 {@code Context.checkSelfPermission()} 方法。 -如要要求權限,請呼叫新的 -{@code Activity.requestPermission()} 方法。即使您的應用程式目標不是 M,還是應該在新的權限模型下測試您的應用程式。 -

    - -

    如需在您的應用程式中支援新權限模型的詳細資訊,請參閱權限開發人員預覽版頁面。 - -如需評估對您應用程式的影響的祕訣,請參閱測試指南。 -

    - -

    省電最佳化

    -

    這個預覽版針對閒置的裝置和應用程式引進了新的省電最佳化功能。

    - -

    休眠

    -

    如果拔除裝置電源並關閉螢幕使其保持靜止狀態一段時間,該裝置即會進入「休眠」模式,它會嘗試讓系統保持睡眠狀態。 -在此模式中,裝置會在短期間內定期繼續執行正常操作,因此,會進行應用程式同步處理,而系統可以執行任何待處理的操作。 - -

    - -

    處於休眠狀態時,下列限制會套用到您的應用程式:

    -
      -
    • 除非您的應用程式接收到高優先順序的 Google 雲端通訊活動訊號 (Tickle),否則會停用網路存取。 -
    • -
    • 喚醒鎖定會被忽略。
    • -
    • 使用 {@link android.app.AlarmManager} 類別排定的鬧鐘會被停用,但使用 {@link android.app.AlarmManager#setAlarmClock setAlarmClock()}方法和 {@code AlarmManager.setAndAllowWhileIdle()} 設定的鬧鐘則不會被停用。 - -
    • -
    • WiFi 掃描不會執行。
    • -
    • 不容許執行您同步配接器的同步處理和工作以及 {@link android.app.job.JobScheduler}。 -
    • -
    -

    -

    當裝置離開休眠狀態時,就會執行所有待處理的工作和同步處理。

    -

    您可以測試此功能,方法是將執行 M 預覽版的裝置連接到您的開發電腦並呼叫下列命令: - -

    -
    -$ adb shell dumpsys battery unplug
    -$ adb shell dumpsys deviceidle step
    -$ adb shell dumpsys deviceidle -h
    -
    -

    注意:即將發行的 Google 雲端通訊版本讓您能夠指定高優先順序的訊息。 - - -如果您的應用程式收到高優先順序的 GCM 訊息,即使裝置處於休眠狀態,系統還是會授與它短暫的網路存取權限。 - -

    - -

    如需如何在您的應用程式中測試休眠的祕訣,請參閱測試指南。 - -

    - -

    應用程式待命

    -

    使用這個預覽版時,系統可在應用程式處於未使用狀態時,判斷它們是否處於閒置狀態。 -除非系統偵測到以下任一個訊號,否則會在一段時間之後將應用程式視為閒置: -

    - -
      -
    • 使用者明確啟動應用程式。
    • -
    • 應用程式目前在前景中有一個處理程序 (可能是做為活動或前景服務,也可能正由其他活動或前景服務所使用)。 -
    • -
    • 應用程式產生使用者可以在鎖定螢幕或通知匣中看見的通知。 -
    • -
    • 使用者透過 [設定] 明確要求應用程式不需進行最佳化。 -
    • -
    - -

    如果拔除了裝置電源,即會停用被視為閒置之應用程式的網路存取,並擱置它們的同步處理和工作。 -為裝置插上電源時,就允許這些應用程式進行網路存取,且可執行所有已擱置的工作和同步處理。 -如果裝置長時間處於閒置狀態,則允許閒置的應用程式進行網路存取,大約是一天一次。 -

    - -

    您可以測試此功能,方法是將執行 M 預覽版的裝置連接到您的開發電腦並呼叫下列命令: - -

    -
    -$ adb shell dumpsys battery unplug
    -$ adb shell am set-idle <packageName> true
    -$ adb shell am set-idle <packageName> false
    -$ adb shell am get-idle <packageName>
    -
    - -

    注意:即將發行的 Google 雲端通訊 (GCM) 版本讓您能夠指定高優先順序的訊息。 - - -如果您的應用程式收到高優先順序的 GCM 訊息,即使應用程式處於閒置狀態,系統還是會授與它短暫的網路存取權限。 - -

    - -

    如需如何在您的應用程式中測試應用程式待命的祕訣,請參閱測試指南。 - -

    - -

    可採用的儲存裝置

    -

    -使用這個預覽版時,使用者可以採用像是 SD 卡的外部儲存裝置。採用外部儲存裝置會加密並格式化裝置,使其可如內部儲存空間般運作。 -此功能讓使用者能夠在儲存裝置之間移動應用程式和這些應用程式的私人資料。 -移動應用程式時,系統會採用宣示說明中的 {@code android:installLocation} 偏好設定。 - - -

    - -

    如果您的應用程式會存取下列 API 或欄位,請注意,在內部和外部儲存裝置之間移動應用程式時,它們傳回的檔案路徑將會動態變更。建置檔案路徑時,強烈建議您一律動態呼叫這些 API。請勿使用硬式編碼的檔案路徑或保留先前建置的完整檔案路徑。 - - -

    - -
      -
    • {@link android.content.Context} 方法: -
        -
      • {@link android.content.Context#getFilesDir() getFilesDir()}
      • -
      • {@link android.content.Context#getCacheDir() getCacheDir()}
      • -
      • {@link android.content.Context#getCodeCacheDir() getCodeCacheDir()}
      • -
      • {@link android.content.Context#getDatabasePath(java.lang.String) getDatabasePath()}
      • -
      • {@link android.content.Context#getDir(java.lang.String,int) getDir()}
      • -
      • {@link android.content.Context#getNoBackupFilesDir() getNoBackupFilesDir()}
      • -
      • {@link android.content.Context#getFileStreamPath(java.lang.String) getFileStreamPath()}
      • -
      • {@link android.content.Context#getPackageCodePath() getPackageCodePath()}
      • -
      • {@link android.content.Context#getPackageResourcePath() getPackageResourcePath()}
      • -
      -
    • -
    • {@link android.content.pm.ApplicationInfo} 欄位: -
        -
      • {@link android.content.pm.ApplicationInfo#dataDir dataDir}
      • -
      • {@link android.content.pm.ApplicationInfo#sourceDir sourceDir}
      • -
      • {@link android.content.pm.ApplicationInfo#nativeLibraryDir nativeLibraryDir}
      • -
      • {@link android.content.pm.ApplicationInfo#publicSourceDir publicSourceDir}
      • -
      • {@link android.content.pm.ApplicationInfo#splitSourceDirs splitSourceDirs}
      • -
      • {@link android.content.pm.ApplicationInfo#splitPublicSourceDirs splitPublicSourceDirs}
      • -
      -
    • -
    - -

    如要在開發人員預覽版中對此功能進行偵錯,您可以執行下列命令來採用 USB 磁碟機 (這個磁碟機是透過 USB On-The-Go (OTG) 纜線連接到 Android 裝置): -

    - -
    -$ adb shell sm set-force-adoptable true
    -
    - -

    移除 Apache HTTP 用戶端

    -

    這個預覽版已移除對於 Apache HTTP 用戶端的支援。如果您的應用程式正在使用這個用戶端且目標為 Android 2.3 (API 級別 9) 或更高版本,請改為使用 {@link java.net.HttpURLConnection} 類別。 - -這個 API 的效率更高,因為它能透過透明的壓縮和回應快取來降低網路使用量,並將電源耗用量降至最低。 -如要繼續使用 Apache HTTP API,您必須先在 {@code build.gradle} 檔案中宣告下列編譯時期的相依性: - -

    -
    -android {
    -    useLibrary 'org.apache.http.legacy'
    -}
    -
    -

    Android 正從 OpenSSL 移至 BoringSSL 程式庫。 - -如果您正在應用程式中使用 Android NDK,請勿連結不屬於 NDK API 一部分的密碼編譯程式庫,例如 {@code libcrypto.so} 和 {@code libssl.so}。 -這些程式庫不是公用 API,而且可能在沒有通知的情況下,在新的版本和裝置上變更或終止支援。此外,您可能會讓自己暴露於安全性弱點中。 - -因此,請改為修改您的原生程式碼,透過 JNI 來呼叫 Java 密碼編譯 API,或以靜態方式連結您選擇的密碼編譯程式庫。 - -

    - -

    AudioManager 變更

    -

    不再支援透過 {@link android.media.AudioManager} 類別直接設定音量或將特定串流設定為靜音。 -{@link android.media.AudioManager#setStreamSolo(int,boolean) -setStreamSolo()} 方法已過時,您應該改為呼叫 -{@code AudioManager.requestAudioFocus()} 方法。同樣地, -{@link android.media.AudioManager#setStreamMute(int,boolean) setStreamMute()} 方法已過時;請改為呼叫 {@code AudioManager.adjustStreamVolume()} 方法並傳入方向值 {@code ADJUST_MUTE} 或 {@code ADJUST_UNMUTE}。 - -

    - -

    文字選取

    - - - -

    當使用者在您的應用程式中選取文字時,您現在可以在浮動工具列中顯示文字選取動作,例如,剪下、複製及貼上。 - -使用者互動實作類似於針對內容關聯動作列所做的實作,如為個別的檢視啟用內容關聯動作模式中所述。 - - -

    - -

    如要實作適用於文字選取的浮動工具列,請在您現有的應用程式中進行下列變更: -

    -
      -
    1. 在您的 {@link android.view.View} 或 {@link android.app.Activity} 物件中,將 -{@link android.view.ActionMode} 呼叫從 -{@code startActionMode(Callback)} 變更為 {@code startActionMode(Callback, ActionMode.TYPE_FLOATING)}。
    2. -
    3. 進行 {@code ActionMode.Callback} 的現有實作,並改為讓它擴充 -{@code ActionMode.Callback2}。
    4. -
    5. 覆寫 {@code Callback2.onGetContentRect()} 方法,以在檢視中提供內容 {@link android.graphics.Rect} 物件 (例如,文字選取矩形區塊) 的座標。 -
    6. -
    7. 如果矩形區塊位置不再有效,而且這是唯一變成無效的元素,請呼叫 {@code ActionMode.invalidateContentRect()} 方法。 -
    8. -
    - -

    如果您正在使用 Android 支援程式庫版本 22.2,請注意,浮動工具列無法向下相容,而且 appcompat 預設會取得 {@link android.view.ActionMode} 物件的完整控制權。 - - -這可防止浮動工具列顯示。如要在 -{@link android.support.v7.app.AppCompatActivity} 中啟用 -{@link android.view.ActionMode} 支援,請呼叫 -{@code android.support.v7.app.AppCompatActivity.getDelegate()},然後在傳回的 -{@link android.support.v7.app.AppCompatDelegate} 物件中呼叫 -{@code android.support.v7.app.AppCompatDelegate.setHandleNativeActionModesEnabled()},並將輸入參數設定為 {@code false}。 -這個呼叫會將 {@link android.view.ActionMode} 物件的控制權傳回架構中。 -儘管在 M 預覽版之前的裝置中,只支援 {@link android.support.v7.app.ActionBar} 模式,但在執行 M 預覽版的裝置中,允許架構支援 -{@link android.support.v7.app.ActionBar} 或浮動工具列模式。 -

    - -

    Android 金鑰存放區變更

    -

    使用這個預覽版時,Android 金鑰存放區供應程式不再支援 DSA。 - -但仍支援 ECDSA。

    - -

    在停用或重設安全鎖定螢幕時 (例如,由使用者或裝置管理員執行),將不再刪除其餘不需加密的金鑰。 -在這些事件期間,將會刪除其餘需要加密的金鑰。 -

    - -

    Wi-Fi 和網路變更

    - -

    這個預覽版引進了下列對於 Wi-Fi 和網路 API 的行為變更。

    -
      -
    • 唯有當您建立了 {@link android.net.wifi.WifiConfiguration} 物件時,您的應用程式現在才能變更這些物件的狀態。 -系統不容許您修改或刪除由使用者或其他應用程式所建立的 -{@link android.net.wifi.WifiConfiguration} 物件。 -
    • -
    • -在以前,如果應用程式使用 -{@link android.net.wifi.WifiManager#enableNetwork(int,boolean) enableNetwork()} 搭配 -{@code disableAllOthers=true} 設定來強制裝置連接到特定的 Wi-Fi 網路,裝置即會中斷與其他網路的連線,例如行動數據。 -在這個預覽版中,裝置不再中斷與這類其他網路的連線。如果您應用程式的 {@code targetSdkVersion} 是 {@code “20”} 或更低版本,即會將它固定到選取的 Wi-Fi 網路。 - -如果您應用程式的 {@code targetSdkVersion} 是 {@code “21”} 或更高版本,請使用多網路 API (例如, -{@link android.net.Network#openConnection(java.net.URL) openConnection()}、 -{@link android.net.Network#bindSocket(java.net.Socket) bindSocket()} 及新的 -{@code ConnectivityManager.bindProcessToNetwork()} 方法),以確保會在選取的網路上傳送它的網路流量。 - -
    • -
    - -

    相機服務變更

    -

    在這個預覽版中,在相機服務中存取分享資源的模型已經從先前的「先進先服務」存取模型變更為依照優先順序針對處理程序進行處理的存取模型。 - -對於服務行為的變更如下:

    -
      -
    • 存取相機子系統資源 (包括開啟和設定相機裝置) 的權限是根據用戶端應用程式處理程序的「優先順序」來授與。 -通常會為具有使用者可看見或前景活動的應用程式處理程序提供較高的優先順序,讓相機資源的取得和使用更可靠。 - -
    • -
    • 優先順序較低之應用程式的使用中相機用戶端可能會在優先順序較高的應用程式嘗試使用相機時被系統「撤出」。 -在已過時的 {@link android.hardware.Camera} API 中,這會導致針對被撤出的用戶端呼叫 -{@link android.hardware.Camera.ErrorCallback#onError(int,android.hardware.Camera) onError()}。 - -在 {@link android.hardware.camera2 Camera2} API 中,會導致針對被撤出的用戶端呼叫 -{@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected(android.hardware.camera2.CameraDevice) onDisconnected()} -。
    • -
    • 在配備適當相機硬體的裝置上,個別的應用程式處理程序能夠單獨開啟,同時使用不同的相機裝置。 -但是,相機服務現在可以偵測到且不允許多處理程序使用案例,同時存取會對任何已開啟的相機裝置造成顯著的效能或功能降級。 - -此變更可能會導致優先順序較低的用戶端被「撤出」,即使沒有任何其他應用程式正直接嘗試存取同一個相機裝置也一樣。 - - -
    • -
    • -變更目前的使用者會導致先前的使用者帳戶所擁有之應用程式的使用中相機用戶端被撤出。 -相機的存取權受限於目前裝置使用者所擁有的使用者設定檔。例如,這實際上表示「訪客」帳戶在使用者切換到不同帳戶之後,將無法保留使用相機子系統的執行中處理程序。 - - -
    • -
    - -

    ART 執行階段

    -

    ART 執行階段現在可以正確實作 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法的存取規則。這個變更會修正 Dalvik 在先前版本中以不正確方式檢查存取規則的問題。如果您的應用程式使用 -{@link java.lang.reflect.Constructor#newInstance(java.lang.Object...) newInstance()} 方法且您想要覆寫存取檢查,請搭配已設定為 {@code true} 的輸入參數呼叫 -{@link java.lang.reflect.Constructor#setAccessible(boolean) setAccessible()} 方法。 - - - -如果您的應用程式使用 v7 appcompat 程式庫v7 recyclerview 程式庫,您就必須更新應用程式來使用這些程式庫的最新版本。 - - -否則,請確定從 XML 參考的所有自訂類別都會更新,如此一來就能存取其類別建構函式。 -

    - -

    這個預覽版會更新動態連結器的行為。動態連結器現在瞭解程式庫的 {@code soname} 與其路徑 (公開的 Bug 6670) 之間的差異,而且現在會實作依 {@code soname} 進行搜尋。 - - - -先前可運作但含有錯誤 {@code DT_NEEDED} 項目 (通常是組建電腦之檔案系統上的絕對路徑) 的應用程式可能會在載入時失敗。 -

    - -

    {@code dlopen(3) RTLD_LOCAL} 旗標現在會以正確的方式實作。請注意, -{@code RTLD_LOCAL} 是預設值,因此,對 {@code dlopen(3)} 的呼叫 (不會明確使用 -{@code RTLD_LOCAL}) 將會受到影響 (除非您的應用程式明確使用 {@code RTLD_GLOBAL})。使用 -{@code RTLD_LOCAL},由後續呼叫 -{@code dlopen(3)} (相對於 {@code DT_NEEDED} 項目所參考) 所載入的程式庫將無法使用符號。

    -

    - -

    APK 驗證

    -

    此平台現在會執行較嚴格的 APK 驗證。如果檔案宣告於宣示說明中但未出現在 APK 本身中,則 APK 會被視為毀損。 -如果移除了任何內容,就必須重新簽署 APK。 -

    - -

    Android for Work 變更

    -

    這個預覽版包含下列對於 Android for Work 的行為變更:

    -
      -
    • 個人內容中的工作聯絡人。Google 撥號程式通話記錄現在會在使用者檢視過去的通話記錄時顯示工作聯絡人。將 {@code DevicePolicyManager.setCrossProfileCallerIdDisabled()} 設定為 {@code true},可以在 Google 撥號程式通話記錄中隱藏工作設定檔聯絡人。 - - -只有在您將 {@code DevicePolicyManager.setBluetoothContactSharingDisabled()} 設定為 {@code false} 時,工作聯絡人才會透過藍牙,與個人聯絡人一起顯示於裝置上。 - -預設會設定為 {@code true}。 - -
    • -
    • 移除 WiFi 設定:如果將工作設定檔刪除,則現在會移除由設定檔擁有者所新增的 WiFi 設定 (例如,透過呼叫 -{@link android.net.wifi.WifiManager#addNetwork(android.net.wifi.WifiConfiguration) -addNetwork()} 方法)。 -
    • -
    • 鎖定 WiFi 設定:使用者無法再修改或刪除任何由使用中裝置擁有者所建立的 WiFi 設定。 -只要尚未針對使用者設定 {@link android.os.UserManager} 常數 -{@link android.os.UserManager#DISALLOW_CONFIG_WIFI},該使用者就仍能建立和修改他們自己的 WiFi 設定。 -
    • -
    • 透過 Google 帳戶新增來下載工作原則控制器:在將要求透過工作原則控制器 (WPC) 應用程式進行管理的 Google 帳戶新增到受管理內容以外的裝置時,新增帳戶流程現在會提示使用者安裝適當的 WPC。這個行為也適用於在初始裝置設定精靈中透過 [設定] > [帳戶] 來新增的帳戶。 - - - -
    • -
    • 對於特定 DevicePolicyManager API 行為的變更:呼叫 {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName,boolean) setCameraDisabled()} -方法,只會對正在呼叫之使用者的相機產生影響;從受管理的設定檔呼叫它則不會對在主要使用者上執行的相機應用程式產生影響。 - -此外,除了裝置擁有者, -{@link android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures(android.content.ComponentName,int) setKeyguardDisabledFeatures()} -方法現在還可供設定檔擁有者使用。設定檔擁有者可以設定下列滑動解鎖限制: - -
        -
      • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} 和 -{@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_FINGERPRINT},它們會對設定檔上層使用者的滑動解鎖設定產生影響。 -
      • -
      • {@link android.app.admin.DevicePolicyManager#KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS},這只會影響受管理設定檔中由應用程式所產生的通知。 -
      • -
      -
    • -
    diff --git a/docs/html-intl/intl/zh-tw/preview/download.jd b/docs/html-intl/intl/zh-tw/preview/download.jd deleted file mode 100644 index 163af2c97aa44131003870aa968db98d0733aa78..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/download.jd +++ /dev/null @@ -1,360 +0,0 @@ -page.title=下載 -page.image=images/cards/card-download_16-9_2x.png - -@jd:body - -
    - - - - -
    - -
    -
    -

    本文件內容

    -
      -
    1. Android 6.0 SDK
    2. -
    3. 開發人員文件
    4. -
    5. 硬體系統映像
    6. -
    -

    Legacy downloads

    -
      -
    1. Developer Preview 1
    2. -
    3. Developer Preview 2
    4. -
    -
    -
    - - -

    - Android M 預覽版 SDK 有開發工具、Android 系統檔案以及程式庫檔案,可以幫助測試您的應用程式和下一個平台版本隨附的新 API。 -本文件會說明如何取得可下載的預覽版元件來測試您的應用程式。 - -

    - - -

    Android 6.0 SDK

    - -

    - 預覽版 SDK 可透過 Android SDK 管理器下載取得。如需有關下載和設定預覽版 SDK 的詳細資訊,請參閱設定預覽版 SDK。 - -

    - - -

    開發人員文件

    - -

    - 開發人員文件下載套件提供詳細的 API 參考資料和預覽版的 API 差異報告。 -

    - - - - - - - - - - -
    DescriptionDownload / Checksums
    Android M Preview 3
    Developer Docs
    m-preview-3-developer-docs.zip
    - MD5: d99b14b0c06d31c8dfecb25072654ca3
    - SHA-1: 9cefeeda07676130da606a1796e1c00fffc667c1 -
    - - -

    硬體系統映像

    - -

    - 這些系統映像可以讓您在實體裝置上安裝預覽版的平台來進行測試。 -使用其中一個映像設定裝置,您就可以安裝並測試您的應用程式,瞭解應用程式在下一個版本的平台上表現如何。 -在裝置上安裝系統映像的過程中,會「移除裝置當中所有的資料」,因此您應該在安裝系統映像之前備份您的資料。 - - -

    - -

    - 警告:下列 Android 系統映像是預覽版,可能隨時會有變更。使用這些系統映像受到「Android SDK 預覽版授權協議」的約束。 -Android 預覽版系統映像還不是穩定的版本,可能會包含許多錯誤和瑕疵而對您的電腦系統、裝置和資料造成損害。 - -預覽版的 Android 系統映像與出廠作業系統的測試不同,可能會導致您的手機和安裝的服務與應用程式停止運作。 - - -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    DeviceDownload / Checksums
    Nexus 5 (GSM/LTE)
    "hammerhead"
    hammerhead-MPA44I-preview-2ebbc049.tgz
    - MD5: 91a924fb0c9f8e716e3b4c9954fd0dbb
    - SHA-1: 2ebbc049b68c4da8baeee3e42bb94d7a965ba4a3 -
    Nexus 6
    "shamu"
    shamu-MPA44I-preview-62b9c486.tgz
    - MD5: ac6e58da86125073d9c395257fd42664
    - SHA-1: 62b9c486fd7a5020e228d53ca5acd5c1857e48ff -
    Nexus 9
    "volantis"
    volantis-MPA44I-preview-5c30a6e2.tgz
    - MD5: 7f83768757913d3fea945a661020d185
    - SHA-1: 5c30a6e2acd11a81f4105b12d23ff654f534f699 -
    Nexus Player
    "fugu"
    fugu-MPA44I-preview-2860040a.tgz
    - MD5: 438da8d37da9e341a69cfb16a4001ac5
    - SHA-1: 2860040a326582f1ff5f702bf9a1ef002717fc98 -
    - -

    在裝置上安裝映像

    - -

    - 如果要使用裝置映像進行測試,您必須先在相容的裝置上安裝映像。請依照下面的指示安裝系統映像: - -

    - -
      -
    1. 下載此處列出的其中一個系統映像,然後解壓縮。
    2. -
    3. 備份您要保留的所有裝置資料。
    4. -
    5. 依照 -developers.google.com/android -的指示,將映像更新到您的裝置。
    6. -
    - -

    - 注意:在您使用預覽版系統映像更新開發裝置之後,裝置就會透過無線 (OTA) 更新方式自動升級為下一個預覽版版本。 - -

    - -

    將裝置還原成出廠規格

    - -

    - 如果您要解除安裝預覽版並將裝置還原成出廠規格,請至 -developers.google.com/android 並下載您要為裝置更新的映像。 -依照該頁面的指示,將映像更新到您的裝置。 - -

    - -
    - -
    - - - - diff --git a/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd b/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd deleted file mode 100644 index 5be8a0f12c0161ab9c2c054c20ce3c604eac0cf6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/features/app-linking.jd +++ /dev/null @@ -1,123 +0,0 @@ -page.title=應用程式連結 -page.image=images/cards/card-app-linking_2x.png -page.keywords=應用程式連結, 深層連結, 意圖 -@jd:body - - - -

    - Android 意圖系統是一個彈性機制,讓應用程式能夠用來處理內容和要求。 - 有許多應用程式可能會在它們的意圖篩選條件中宣告相符的 URI 模式。當使用者按一下不含預設啟動處理常式的 Web 連結時,平台可能會顯示一個對話方塊,讓使用者能夠從具備已宣告相符意圖篩選條件的應用程式清單中選取。 - - -

    - -

    - Android M 開發人員預覽版引進了應用程式連結的支援,這樣就能藉由允許應用程式開發人員將應用程式關聯至他們所擁有的 Web 網域,來改善現有的連結處理。 -當開發人員建立這個關聯時,平台可以自動判斷要用來處理特定 Web 連結的預設應用程式,並略過詢問使用者的程序。 - - -

    - - -

    宣告網站關聯

    - -

    - 網站擁有者必須宣告與應用程式的關聯,才能建立應用程式連結。站台擁有者可藉由在網域上的已知位置裝載名為 {@code statements.json} 的 JSON 檔案,來宣告與應用程式的關係: - - -

    - -
    http://<domain>:<optional port>/.well-known/statements.json
    - -

    - 注意: - 在 M 開發人員預覽版期間,會透過 http 通訊協定來驗證 JSON 檔案。如果是平台的正式版本,就會透過加密的 https 通訊協定來驗證該檔案。 - -

    - -

    - 這個 JSON 檔案會指出應該在這個網域中用來做為 URL 預設處理常式的 Android 應用程式。 -它會根據下列欄位來識別應用程式: -

    - -
      -
    • {@code package_name}:宣告於應用程式宣示說明中的套件名稱。
    • - -
    • {@code sha256_cert_fingerprints}:您應用程式簽署憑證的 SHA256 指紋。 - 您可以使用 Java 金鑰工具,利用下列命令來產生指紋: -
      keytool -list -v -keystore my-release-key.keystore
      -
    • -
    - -

    - 下列檔案清單會顯示 -{@code statements.json} 檔案的內容與格式範例: -

    - -
    -[{
    -  "relation": ["delegate_permission/common.handle_all_urls"],
    -  "target": {
    -    "namespace": "android_app",
    -    "package_name": "<package name>",
    -    "sha256_cert_fingerprints": ["6C:EC:C5:0E:34:AE....EB:0C:9B"]
    -  }
    -}]
    -
    - - - - -

    - 應用程式可以要求平台根據裝載於個別 Web 網域上的 {@code statements.json} 檔案,自動驗證在其意圖篩選條件的資料元素中由主機名稱所定義的任何應用程式連結。 - -如要要求應用程式連結驗證,請將 {@code android:autoVerify} - 屬性新增到宣示說明中每個所需的意圖篩選條件中,如下列宣示說明程式碼片段所示: - -

    - -
    -<activity ...>
    -    <intent-filter android:autoVerify="true">
    -        <action android:name="android.intent.action.VIEW" />
    -        <category android:name="android.intent.category.DEFAULT" />
    -        <category android:name="android.intent.category.BROWSABLE" />
    -        <data android:scheme="http" android:host="www.android.com" />
    -        <data android:scheme="https" android:host="www.android.com" />
    -    </intent-filter>
    -</activity>
    -
    - -

    - 當 {@code android:autoVerify} 屬性出現在應用程式宣示說明時,平台即會在安裝該應用程式時嘗試驗證應用程式連結。 -如果平台無法成功驗證應用程式連結,就無法將該應用程式設定為偏好使用的應用程式來處理 Web 連結。 -當使用者下次開啟其中一個連結時,平台就切換回為該使用者展示一個對話方塊。 - - -

    - -

    - 注意:在測試期間,如果驗證失敗,可能會產生誤判,但是使用者已經使用系統「設定」應用程式,明確地啟用應用程式來自動開啟支援的連結。在此案例中,不會顯示任何對話方塊,而且連結會直接連至您的應用程式,但這只是基於使用者的設定,而不是因為驗證成功所致。 - - - -

    - - -

    管理應用程式連結設定

    - -

    - 使用者可以變更應用程式連結設定,讓他們能夠以偏好使用的方式來處理 URL。您可以在系統「設定」應用程式中,於 [設定] > [應用程式] > [應用程式資訊] > [預設開啟] 下方,檢閱和管理應用程式連結。 - - -

    diff --git a/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd b/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd deleted file mode 100644 index cf756aa5b358103c100f4909d779a2a97d9d1384..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/features/runtime-permissions.jd +++ /dev/null @@ -1,794 +0,0 @@ -page.title=權限 -page.tags=previewresources, androidm -page.keywords=權限, 執行階段, 預覽 -page.image={@docRoot}preview/features/images/permissions_check.png -@jd:body - - -
    -
    -

    快速檢視

    -
      -
    • 如果您的應用程式是以 M 預覽版 SDK 為目標,它會提示使用者在執行階段授與權限,而不是安裝期間。 -
    • -
    • 使用者能隨時從應用程式 [設定] 畫面撤銷權限。 -
    • -
    • 您的應用程式每次執行時都需要檢查它是否有所需的權限。 -
    • -
    - -

    本文件內容

    -
      -
    1. 總覽
    2. -
    3. 編寫執行階段權限的程式碼
    4. -
    5. 測試執行階段權限
    6. -
    7. 建議做法
    8. -
    - - - - -
    -
    - - -

    - M 開發人員預覽版導入新的應用程式權限模型,簡化使用者安裝和升級應用程式的程序。 -如果 M 預覽版上執行的應用程式支援新的權限模型,使用者安裝或升級應用程式時,不需要授與任何權限。應用程式會在需要時才要求權限,而且系統會對使用者顯示要求權限的對話方塊。 - - - - -

    - -

    - 如果應用程式支援新的權限模型,它仍能在在執行舊版 Android 的裝置上安裝並執行 (在那些裝置上使用舊的權限模型)。 - - -

    - -

    - 總覽 -

    - -

    - 使用 M 開發人員預覽版,平台導入新的應用程式權限模型。 -以下是這個新模型的主要元件摘要: -

    - -
      -
    • - 宣告權限:應用程式會在宣示說明中宣告所需的所有權限,如舊版 Android 平台。 - -
    • - -
    • - 權限群組:權限會根據其功能分為「權限群組」 -。例如, -CONTACTS 權限群組包含讀取和寫入使用者聯絡人與設定檔資訊的權限。 - -
    • - -
    • -

      安裝期間授與的有限權限:當使用者安裝或更新應用程式時,系統會將應用程式所要求且歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL} 的所有權限授與應用程式。 - - - 例如,會在安裝期間自動授與歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL} 的鬧鐘與網際網路權限。 - -

      - -

      系統也會授與應用程式簽名和系統權限,如系統應用程式和簽名所述。 - -安裝期間「不」會提示使用者授與任何權限。 -

      -
    • - -
    • - 使用者在執行階段授與權限:應用程式要求權限時,系統會對使用者顯示對話方塊,接著呼叫應用程式的回呼函數,通知它是否已授與權限。 - -如果使用者授與權限,應用程式會獲得在應用程式宣示說明中所宣告之權限功能區域中的所有權限。 - - -
    • - -
    - -

    - 此權限模型改變應用程式要求權限的功能行為。 -以下是您應遵循以調整此模型的開發做法摘要: - -

    - -
      - -
    • - 一律檢查是否具備權限:當應用程式需要執行任何需要權限的動作時,都應要先檢查是否具備有該權限。 - -若不具備,即要求獲得授與該權限。 - -
    • - -
    • - 適當處理缺少權限的情況:如果應用程式未獲授與適當的權限,它應要能完全處理失敗。 - - 例如,若只有新增功能需要該權限,應用程式可以將該功能停用。 -如果應用程式務必要具備該權限才能運作,應用程式可以停用其所有功能,並通知使用者務必要授與該權限。 - - -
    • - -
      - -

      - 圖 1.應用程式 [設定] 的權限畫面。 -

      -
      - -
    • - 權限可以撤銷:使用者可以隨時撤銷應用程式的權限。 -如果使用者關閉應用程式的權限,並「不」會通知應用程式。 -再次強調,您的應用程式在執行任何受限制的動作之前,應該驗證它是否具備所需的權限。 - -
    • -
    - -

    - 注意:如果應用程式是以 M 開發人員預覽版為目標,「務必要」 -使用新的權限模型。 -

    - -

    - 自 M 開發人員預覽版推出起,並非所有 Google 應用程式都完全實作新的權限模型。 -Google 正透過 M 開發人員預覽版逐漸更新這些應用程式,以適當保留權限切換設定。 - - -

    - -

    - 注意:如果您的應用程式有自己的 API 介面,務必要先確定呼叫端具備存取該資料的必要權限後,再 Proxy 權限。 - - -

    - -

    - 系統應用程式和簽名權限 -

    - -

    - 一般來說,當使用者安裝應用程式時,系統只會將 - {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL - PROTECTION_NORMAL} 授與應用程式。不過,在某些情況下,系統會授與應用程式更多權限: - -

    - -
      -
    • 如果應用程式屬於系統映像的一部分,會自動獲授與其宣示說明中列出的所有權限。 - -
    • - -
    • 如果應用程式要求宣示說明中歸入 {@link -android.content.pm.PermissionInfo#PROTECTION_SIGNATURE PROTECTION_SIGNATURE} 的權限,並和宣告那些權限的應用程式一樣,以相同的憑證簽署應用程式,系統會在安裝時將那些權限授與要求的應用程式。 - - - -
    • -
    - -

    - 在這兩種情況下,使用者仍能隨時撤銷權限,只要前往系統的 [設定] 畫面,然後選擇 [應用程式] > - - app_name > [權限]。應用程式應持續在執行階段檢查是否具備權限,並在必要時予以要求。 - - -

    - -

    - 往後和回溯相容性 -

    - -

    - 如果應用程式不是以 M 開發人員預覽版為目標,即使在 M 預覽版裝置上,應用程式也會持續使用舊的權限模型。 -當使用者安裝應用程式時,系統會要求使用者授與應用程式的宣示說明中列出的所有權現。 - - -

    - -

    - 注意:在執行 M 開發人員預覽版的裝置上,使用者能從應用程式的設定畫面關閉任何應用程式 (包括舊版應用程式) 的權限。 - -如果使用者關閉舊版應用程式的權限,系統會自動停用適當功能。 -當應用程式嘗試執行需要那項權限的操作時,該操作不一定會造成例外狀況。 - -而可能傳回空的資料集,通知發生錯誤,或展示未預期的行為。 -例如,如果您不具備查詢行事曆的權限,方法會傳回空的資料集。 - -

    - -

    - 如果您在並非執行 M 預覽版的裝置上使用新的權限模型來安裝應用程式,系統會將它和任何其他應用程式一視同仁:系統會在安裝期間要求使用者授與所有宣告的權限。 - - - -

    - -

    - 注意:對於預覽版,您必須將 SDK 最低版本設定為 M 預覽版 SDK,才能以預覽版 SDK 編譯。 -這表示在開發人員預覽版期間,您將無法在舊版平台上測試這類應用程式。 - - -

    - -

    權限與意圖比較

    - -

    - 在許多情況下,您可以為應用程式在兩種方法當中擇一來執行工作。 -您可以讓應用程式要求權限以自行執行操作。 -或者,您可以讓應用程式使用意圖,讓其他應用程式來執行工作。 - -

    - -

    - 例如,假設您的應用程式需要能夠使用裝置相機拍攝相片。 -您的應用程式能要求 -android.permission.CAMERA 權限,讓應用程式直接存取相機。 -接著,應用程式會使用相機 API 來控制相機並拍攝相片。 -這種方法可讓您的應用程式對攝影處理程序有完整控制權,並讓您將相機 UI 納入應用程式。 - - -

    - -

    - 不過,如果您不需要這類控制權,您可以只使用 {@link -android.provider.MediaStore#ACTION_IMAGE_CAPTURE ACTION_IMAGE_CAPTURE} 意圖來要求影像。 -當您啟動意圖時,會提示使用者選擇相機應用程式 (如果還沒有預設的相機應用程式),然後該應用程式會拍攝相片。 - -相機應用程式會將相片傳回應用程式的 {@link - android.app.Activity#onActivityResult onActivityResult()} 方法。 -

    - -

    - 同樣地,如果您需要撥打電話、存取使用者的聯絡人等等,您都可以建立適當的意圖來執行,或是要求權限,然後直接存取適當的物件。 - -每種方法各有利弊。 - -

    - -

    - 如果您使用權限: -

    - -
      -
    • 當您執行操作時,應用程式對使用者體驗有完整控制權。 -不過,如此多樣化控制使您必須設計適當的 UI,而增加工作複雜度。 - -
    • - -
    • 當您初次執行操作時,會提示使用者授與權限 (僅此一次)。 -之後應用程式可以逕行執行該操作,不需要再與使用者有其他互動。 -不過,如果使用者未授與權限 (或稍後予以撤銷),您的應用程式會完全無法執行該操作。 - - -
    • -
    - -

    - 如果您使用意圖: -

    - -
      -
    • 您不必為該操作設計 UI。處理意圖的應用程式會提供 UI。不過,這表示您對使用者體驗沒有控制權。 - -使用者可能會和您不曾見過的應用程式互動。 - -
    • - -
    • 如果使用者沒有執行該操作的預設應用程式,系統會提示使用者選擇應用程式。如果使用者未指定預設處理常式,每次執行操作時可能都要完成額外的對話方塊。 - - - -
    • -
    - -

    編寫執行階段權限的程式碼

    - -

    - 如果您的應用程式是以新的 M 開發人員預覽版為目標,請務必使用新的權限模型。 -這表示除了在宣示說明中宣告所需的權限之外,您也必須在執行階段檢查是否具備權限,並在您不具備時要求權限。 - - - -

    - -

    - 啟用新的權限模型 -

    - -

    - 如要啟用新的 M 開發人員預覽版權限模型,可將應用程式的 -targetSdkVersion 屬性設定為 "MNC",並將 -compileSdkVersion 設定為 "android-MNC"。這樣做可啟用所有新權限功能。 - -

    - -

    - 對於預覽版,您必須將 minSdkVersion 設定為 -"MNC",才能以預覽版 SDK 編譯。 -

    - -

    - 指定只限 M 預覽版使用的權限 -

    - -

    - 您可以在宣示說明中使用新的 <uses-permission-sdk-m> 元素,指出只有在 M 預覽版上才需要某權限。 -如果您以這種方式宣告權限,每當在舊版裝置上安裝應用程式時,系統都不會提示使用者或將權限授與應用程式。藉由使用 <uses-permission-sdk-m> 元素,當使用者安裝更新時,您不需要強制他們授與權限,就可以將新的權限新增至更新的應用程式版本。 - - - - - - -

    - -

    - 如果應用程式在使用 M 開發人員預覽版的裝置上執行, -<uses-permission-sdk-m> 的運作方式會和 -<uses-permission> 相同。 - 當他們安裝應用程式時,系統不會提示使用者授與任何權限,而應用程式會在需要時才要求權限。 - -

    - -

    - 提示授與權限 -

    - -

    - 如果您的應用程式使用新的 M 開發人員預覽版權限模型,在執行 M 預覽版的裝置上初次啟動應用程式時,不會要求使用者授與所有權限。 - -您的應用程式會在需要時才要求權限。 -應用程式要求權限時,系統會對使用者顯示對話方塊。 - -

    - -

    - 如果您的應用程式在 SDK 22 以下版本的裝置上執行,應用程式會使用舊的權限模型。 -當使用者安裝應用程式時,會提示他們授與應用程式在其宣示說明中要求的所有權限,但標示為 <uses-permission-sdk-m> 的那些權限除外。 - - -

    - -

    檢查應用程式執行所在的平台

    - -

    - 只有執行 M 開發人員預覽版的裝置上才支援此權限模型。 -呼叫這些方法之前,應用程式應該檢查 {@link android.os.Build.VERSION#CODENAME - Build.VERSION.CODENAME} 的值,驗證它執行所在的平台。 - -如果裝置是執行 M 開發人員預覽版, -{@link android.os.Build.VERSION#CODENAME CODENAME} 是 "MNC"。 -

    - -

    檢查應用程式是否具備所需權限

    - -

    當使用者嘗試執行需要權限的操作時,應用程式會檢查它目前是否具備可執行此操作的權限。 -如要這麼做,應用程式可呼叫 - -Context.checkSelfPermission(permission_name)。由於使用者可以隨時撤銷應用程式的權限,即使應用程式知道使用者已授與該權限,還是應該執行此檢查。 - - -例如,如果使用者想要使用應用程式拍攝相片,應用程式會呼叫 Context.checkSelfPermission(Manifest.permission.CAMERA)。 - -

    - -

    - 表 1.權限和權限群組。

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    權限群組權限
    android.permission-group.CALENDAR -
      -
    • - android.permission.READ_CALENDAR -
    • -
    -
      -
    • - android.permission.WRITE_CALENDAR -
    • -
    -
    android.permission-group.CAMERA -
      -
    • - android.permission.CAMERA -
    • -
    -
    android.permission-group.CONTACTS -
      -
    • - android.permission.READ_CONTACTS -
    • -
    • - android.permission.WRITE_CONTACTS -
    • -
    • - android.permission.READ_PROFILE -
    • -
    • - android.permission.WRITE_PROFILE -
    • -
    -
    android.permission-group.LOCATION -
      -
    • - android.permission.ACCESS_FINE_LOCATION -
    • -
    • - android.permission.ACCESS_COARSE_LOCATION -
    • -
    -
    android.permission-group.MICROPHONE -
      -
    • - android.permission.RECORD_AUDIO -
    • -
    -
    android.permission-group.PHONE -
      -
    • - android.permission.READ_PHONE_STATE -
    • -
    • - android.permission.CALL_PHONE -
    • -
    • - android.permission.READ_CALL_LOG -
    • -
    • - android.permission.WRITE_CALL_LOG -
    • -
    • - com.android.voicemail.permission.ADD_VOICEMAIL -
    • -
    • - android.permission.USE_SIP -
    • -
    • - android.permission.PROCESS_OUTGOING_CALLS -
    • -
    -
    android.permission-group.SENSORS -
      -
    • - android.permission.BODY_SENSORS -
    • -
    -
      -
    • - android.permission.USE_FINGERPRINT -
    • -
    -
    android.permission-group.SMS -
      -
    • - android.permission.SEND_SMS -
    • -
    • - android.permission.RECEIVE_SMS -
    • -
    • - android.permission.READ_SMS -
    • -
    • - android.permission.RECEIVE_WAP_PUSH -
    • -
    • - android.permission.RECEIVE_MMS -
    • -
    • - android.permission.READ_CELL_BROADCASTS -
    • -
    -
    - -

    必要時要求權限

    - -

    如果應用程式還沒有所需的權限,應用程式會呼叫 -Activity.requestPermissions(String[], int) 方法以要求適當的權限。 -應用程式會傳遞它所需的權限,還有整數「要求代碼」。 - - 這種方法以非同步方式運作:它會立即傳回,並在使用者回應對話方塊後,系統會以該結果呼叫應用程式的回呼方法,傳遞應用程式傳遞給 requestPermissions() 的相同「要求代碼」。 - - -

    - -

    下列程式碼會檢查應用程式是否具備讀取使用者聯絡人的權限,並在必要時要求權限。 -

    - -
    -if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
    -        != PackageManager.PERMISSION_GRANTED) {
    -    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
    -            MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    -
    -    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
    -    // app-defined int constant
    -
    -    return;
    -}
    -
    - -

    處理權限要求回應

    - -

    - 當應用程式要求權限時,系統會對使用者呈現對話方塊。 -當使用者回應時,系統會呼叫 Activity.onRequestPermissionsResult(int, String[], int[]) 並將使用者回應傳遞給它。 - -您的應用程式需要覆寫該方法。將您傳遞給 requestPermissions() 的相同要求代碼傳遞給回呼。 - -例如,如果應用程式要求 READ_CONTACTS 存取權,可能會有下列回呼方法: - - -

    - -
    -@Override
    -public void onRequestPermissionsResult(int requestCode,
    -        String permissions[], int[] grantResults) {
    -    switch (requestCode) {
    -        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
    -            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    -
    -                // permission was granted, yay! do the
    -                // calendar task you need to do.
    -
    -            } else {
    -
    -                // permission denied, boo! Disable the
    -                // functionality that depends on this permission.
    -            }
    -            return;
    -        }
    -
    -        // other 'switch' lines to check for other
    -        // permissions this app might request
    -    }
    -}
    -
    - -

    如果使用者授與權限,系統就會將應用程式宣示說明所列出該功能區域的所有權限給予應用程式。 -如果使用者拒絕要求,您應該執行適當動作。 -例如,您可能會停用依存於此權限的任何選單動作。 - - -

    - -

    - 系統要求使用者授與權限時,使用者可選擇告知系統不要再次要求該權限。 -在上述情況下,當應用程式使用 requestPermissions() 要求該權限時,系統會立即拒絕要求。 - -在這種情況下,如果使用者再次明確拒絕您的要求,系統會以相同的方式呼叫您的 onRequestPermissionsResult()。 - -基於這個理由,您的應用程式不能假設已與使用者發生任何直接互動。 - -

    - -

    測試執行階段權限

    - - -

    - 如果您的應用程式是以新的 M 開發人員預覽版為目標,您必須測試它是否能適當處理權限。 -您不能假設應用程式在執行時具備任何特定權限。 -應用程式初次啟動時,很可能不具備任何權限,且使用者可隨時撤銷或還原權限。 - - -

    - -

    - 您應該測試應用程式,確保它在所有權限情況下都能正常運作。 -使用 M 預覽版 SDK,我們現在提供 Android 偵錯橋 (adb) 命令,不管需要嘗試哪種權限設定都能讓您測試。 - - - -

    - -

    - 新的 adb 命令和選項 -

    - -

    - M 預覽版 SDK 平台工具提供的數個命令可讓您測試應用程式如何處理權限。 - -

    - -

    - 連同權限一併安裝 -

    - -

    - 您可以使用 adb - install 命令的新 -g 選項,安裝應用程式並授與其宣示說明中列出的所有權限: - -

    - -
    -$ adb install -g <path_to_apk>
    -
    - -

    - 授與和撤銷權限 -

    - -

    - 您可以使用新的 ADB 套件管理員 (pm) 命令,對安裝的應用程式授與和撤銷權限。此功能在自動化測試時非常實用。 - - -

    - -

    - 如要授與權限,可使用套件管理員的 grant 命令: -

    - -
    -$ adb pm grant <package_name> <permission_name>
    -
    - -

    - 例如,如要將可錄製音訊的權限授與 com.example.myapp 套件,請使用此命令: - -

    - -
    -$ adb pm grant com.example.myapp android.permission.RECORD_AUDIO
    -
    - -

    - 如要撤銷權限,可使用套件管理員的 revoke 命令: -

    - -
    -$ adb pm revoke <package_name> <permission_name>
    -
    - -

    最佳做法

    - -

    - 新的權限模型讓使用者有更順暢的體驗,並能輕鬆安裝應用程式且對應用程式執行的工作感到自在。 - -建議使用下列最佳做法以充分利用新的模型。 - -

    - - -

    只要求您所需的權限

    - -

    - 每次要求權限時,您都是在強迫使用者做出決定。 - 如果使用者拒絕要求,就會減少您應用程式的功能。 - 您應該儘可能減少提出這些要求的次數。 -

    - -

    - 例如,您的應用程式可經常使用意圖來取得所需功能,而不是要求權限。 - -如果您的應用程式需要使用手機的相機拍攝相片,應用程式可以使用 {@link - android.provider.MediaStore#ACTION_IMAGE_CAPTURE - MediaStore.ACTION_IMAGE_CAPTURE} 意圖。 -當您的應用程式執行意圖時,系統會提示使用者選擇已安裝的相機應用程式來拍攝相片。 - - -

    - -

    - 別讓使用者無法承受 -

    - -

    - 如果您讓使用者一次面對太多權限要求,可能會讓使用者無法承受而結束您的應用程式。您應該改為在需要時才要求權限。 - - -

    - -

    - 在某些情況下,您的應用程式可能必須具備一或多個權限。在那種情況下,在應用程式啟動時立即要求所有權限是合理的舉措。 - -例如,如果您建立攝影應用程式,該應用程式會需要存取裝置相機。 -使用者初次啟動應用程式時,看到要求使用相機的權限不會被嚇到。 - -但如果相同的應用程式具有與使用者聯絡人分享相片的功能,您可能「不」應該在初次啟動時要求該權限。 - -可以等到使用者嘗試使用「分享」功能時,再要求該權限。 - -

    - -

    - 如果您的應用程式提供教學課程,在教學課程結束時要求應用程式的基本權限是合理的舉措。 - -

    - -

    - 說明需要權限的原因 -

    - -

    - 當您呼叫 -requestPermissions() 時,系統顯示的權限對話方塊會說明您的應用程式想要的權限,但不會說明原因。 -在某些情況下,使用者可能會感到不解。 - 在呼叫 requestPermissions() 前對使用者說明應用程式想要權限的原因是不錯的想法。 - -

    - -

    - 例如,攝影應用程式可能想要使用定位服務,以便將相片加上地理標籤。 -一般使用者可能不明白相片可以包含定位資訊,而不明白為何攝影應用程式會想要知道位置。 - -在這種情況下,在呼叫 requestPermissions()「之前」,將此功能的相關資訊告訴使用者會是不錯的想法。 - - -

    - -

    - 您可以將這些要求與應用程式教學課程結合來完成此作業。教學課程可依序顯示應用程式的各項功能,並可以同時說明需要哪些權限。 - -例如,攝影應用程式的教學課程可以示範「與聯絡人分享相片」功能,接著告訴使用者需要提供權限,應用程式才能看到使用者的聯絡人。 - - -接著,應用程式可以呼叫 requestPermissions(),要求使用者提供該存取權。 -當然,並非每位使用者都會依照教學課程執行動作,所以您仍需要在應用程式的正常操作期間檢查和要求權限。 - - -

    diff --git a/docs/html-intl/intl/zh-tw/preview/index.jd b/docs/html-intl/intl/zh-tw/preview/index.jd deleted file mode 100644 index 3aaf3820b3dfacf38e92192acd6ddbd7f7ed2a0a..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/index.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=Android M 開發人員預覽版 -page.tags="preview", -meta.tags="preview, M preview", androidm -fullpage=true -section.landing=true -header.hide=1 -footer.hide=1 -@jd:body - -
    -
    -
    -
    - -
    -
    -

    Android M 開發人員預覽版

    -

    - 準備使用新版 Android。在 Nexus 5、6、9 和 Player 上測試您的應用程式。 -瞭解新功能 — 執行階段權限休眠應用程式待命等省電功能、新的輔助技術,以及其他功能。 - - -

    - - - - 開始使用! -
    - - - Developer Preview 3 (final SDK) -
    -
    -
    -
    -
    -
    -
    - -
    -

    資源

    -
    - 以下重要資訊可幫助您的應用程式準備好使用 Android M。 -
    - -
    - - -
    -
    - diff --git a/docs/html-intl/intl/zh-tw/preview/license.jd b/docs/html-intl/intl/zh-tw/preview/license.jd deleted file mode 100644 index c4acb91a450c68c0807c8442a1e314a93947911e..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/license.jd +++ /dev/null @@ -1,143 +0,0 @@ -page.title=授權協議 - -@jd:body - -

    -如果想要開始使用 Android SDK 預覽版,您必須同意遵守下列條款和條件。如下方所述,請注意這是 Android SDK 預覽版,可能隨時會有變更,請您自負使用風險。 -Android SDK 預覽版還不是穩定的版本,可能會包含許多錯誤和瑕疵而對您的電腦系統、裝置和資料造成嚴重的損害。 -

    - -

    -這是「Android SDK 預覽版授權協議」(以下稱「授權協議」)。 -

    -
    -1.簡介 - -1.1「Android SDK 預覽版」(在「授權協議」中稱為「預覽版」,包括 (如果有可用的) Android 系統檔案、經過封裝的 API 和預覽版程式庫檔案) 是在「授權協議」之條款的約束下授權給您使用。「授權協議」就您對「預覽版」的使用,構成您與 Google 間具法律約束力之合約。 - -1.2「Android」係指「Android 軟體開放原始碼專案」(http://source.android.com/) 所提供的 Android 裝置軟體堆疊 (不定期更新)。 - -1.3「Google」係指 Google Inc.,是一家在美國德拉瓦州註冊的公司,地址為 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States。 - -2.接受「授權協議」 - -2.1 必須先同意遵守「授權協議」,才能使用此「預覽版」。如果不接受「授權協議」,您就無法使用此「預覽版」。 - -2.2 按一下 [接受] 且/或使用「預覽版」,即表示您同意「授權協議」的條款。 - -2.3 如果您是美國或其他國家/地區 (包括您所居住或使用此「預覽版」的國家/地區) 的法律所禁止接收此「預覽版」的人員,就不得使用此「預覽版」及接受「授權協議」。 - -2.4 如果您將在公司或組織內部使用「預覽版」,您就要代表雇主或其他實體同意受「授權協議」約束,且您代表並保證具備完整法定權限來約束您的雇主或這類實體遵守「授權協議」。如果您不具備必要的權限,就不得代表您的雇主或其他實體接受「授權協議」或使用此「預覽版」。 - -3.Google 的預覽版授權 - -3.1 在「授權協議」之條款的約束下,Google 授權您使用此「預覽版」,此授權為買斷式、不可轉讓、非獨占性、不可轉授權、有限且可撤銷,僅在您公司或組織內部私下或內部使用。此「預覽版」僅供您用於開發在 Android 平台上執行的應用程式。 - -3.2 您同意 Google 或第三方對此「預覽版」擁有一切法定權利及權益,包括存在於此「預覽版」中的任何「智慧財產權」。「智慧財產權」係指專利法、著作權法、商業秘密法、商標法及任何和所有其他專利權下的任何及一切權利。Google 保留一切未明確授予您的權利。 - -3.3 您不得將此「預覽版」用於「授權協議」未明文許可的任何用途。除非適用的第三方授權所需,否則您不得:(a) 對此「預覽版」或其任何部分進行複製 (備份用途除外)、修改、改編、轉散佈、反向組譯、還原工程、解編或製作衍生成品;或是 (b) 將此「預覽版」的任何部分載入至行動電話或個人電腦以外的任何其他硬體裝置、將此「預覽版」的任何部分與其他軟體結合,或散佈包含此「預覽版」之任一部分的任何軟體或裝置。 - -3.4 您同意不會從事任何可能導致或造成 Android 分裂的活動,包括但不限於以任何形式散佈從此「預覽版」衍生的軟體開發套件、參其製作或宣傳。 - -3.5 對開放原始碼軟體授權下所授權之「預覽版」的使用、複製及散佈,完全受該開放原始碼軟體授權的條款管制,而不受「授權協議」管制。您同意遵守從這類開放原始碼軟體授權獲得的所有權利,並且避免採取任何可能終止、中止或侵害這類權利的行為。 - -3.6 您同意 Google 可在不事先通知您的情況下變更其所提供之「預覽版」的形式和本質,且此「預覽版」的未來版本可與在此「預覽版」的先前版本上開發的應用程式不相容。您同意 Google 通常可全權斟酌永久或暫時停止提供此「預覽版」(或此「預覽版」的任何功能) 給您或使用者,毋須事先通知。 - -3.7「授權協議」中的所有條款皆未授予您任何使用 Google 之商業名稱、商標、服務標章、標誌、網域名稱或其他明確品牌特徵的權利。 - -3.8 您同意不會移除、遮蔽或更改可能附加至或內含在此「預覽版」中的任何專利權通知 (包括著作權和商標通知)。 - -4.您對「預覽版」的使用 - -4.1 Google 同意在「授權協議」下,任何條款皆未從您 (或您的授權人) 賦予 Google 對您使用此「預覽版」開發之任何軟體應用程式的任何權利及權益,包括存在於這些應用程式中的任何智慧財產權。 - -4.2 您同意只就 (a)「授權協議」和 (b) 相關管轄權中任何適用法律、規定或是普遍獲得接受之慣例或指導方針 (包括任何有關將資料或軟體輸出或輸入美國或其他相關國家/地區的法律) 所允許的用途使用此「預覽版」及撰寫應用程式。 - -4.3 您同意如果使用此「預覽版」開發應用程式,您將保護使用者的隱私權和法定權利。如果使用者提供您使用者名稱、密碼或是其他登入資訊或個人資訊,您必須告知使用者這類資訊將提供給您的應用程式使用,並且必須為這些使用者提供法定充分的隱私權通知和保護。如果您的應用程式會儲存使用者所提供的個人或敏感資訊,它必須確保這些資訊安全無虞。如果使用者提供 Google 帳戶資訊給您,則只有在每個使用者已授權您存取其 Google 帳戶並僅限用於使用者所授權之用途的情況下,您的應用程式才能使用該資訊來存取使用者的 Google 帳戶。 - -4.4 您同意不會使用此「預覽版」從事任何不當活動,例如開發或散佈會以未經授權的方式干擾、妨礙、損害或存取 Google 或任何第三方之伺服器、網路或是其他財產或服務的應用程式。 - -4.5 您同意對您透過 Android 裝置和 (或) Android 應用程式建立、傳輸或顯示的任何資料、內容或資源,以及上述行為造成的後果 (包括 Google 可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.6 您同意對違反在此「授權協議」、任何適用之第三方合約或《服務條款》或是任何適用之法律或規定下所必須遵守的義務,以及違反相關義務造成的後果 (包括 Google 或任何第三方可能蒙受的任何損失或損害) 負起全責 (而 Google 對您或任何第三方就上述一切不需負任何責任)。 - -4.7「預覽版」目前正在開發中,因此您的測試與意見反應對開發程序非常重要。使用「預覽版」,您即認同某些功能仍處於開發階段,因此您不應期待「預覽版」擁有穩定版本的完整功能。在官方 Android SDK 發行之後,此「預覽版」不再受到支援時,您同意不使用此「預覽版」公開散佈或隨附任何應用程式。 - -5.您的開發人員認證 - -5.1 您同意負責保密 Google 可能核發給您或您自己選擇的任何開發人員認證,並且對在您開發人員認證名義下開發的所有應用程式負起全責。 - -6.隱私權和資訊 - -6.1 為了持續更新及改進此「預覽版」,Google 可能會從軟體收集某些使用狀況統計數據,包括但不限於軟體的唯一識別碼、相關 IP 位址、版本號碼,以及有關使用此「預覽版」中的哪些工具和 (或) 服務及其使用方式的相關資訊。在收集這類資訊之前,此「預覽版」會先通知您並徵求您的同意。如果您不同意,Google 將不會收集這類資訊。 - -6.2 Google 會彙總並檢查收集到的資料,據以改善此「預覽版」,並且會依據《Google 隱私權政策》(http://www.google.com/policies/privacy/) 加以妥善保存。 - -7.第三方應用程式 - -7.1 如果您使用此「預覽版」來執行第三方開發的應用程式,或是執行會存取第三方所提供之資料、內容或資源的應用程式,您同意 Google 不需對這類應用程式、資料、內容或資源負任何責任。您瞭解您透過第三方應用程式存取的所有資料、內容或資源是由其提供者負起全責,而 Google 對您因使用或存取任何這些第三方應用程式、資料、內容或資源所造成的損失或損害不需負任何責任。 - -7.2 您瞭解第三方應用程式提供給您的資料、內容或資源可能受到提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您不得根據這類資料、內容或資源 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品,除非相關擁有者明確授權您從事上述活動。 - -7.3 您瞭解使用第三方應用程式、資料、內容或資源可能受到您與相關第三方之間的個別條款約束。 - -8.使用 Google API - -8.1 Google API - -8.1.1 如果您使用任何 API 從 Google 擷取資料,您瞭解這些資料可能受到 Google 或資料提供者 (或代表他們的其他人員或公司) 所擁有的智慧財產權保護。您對任何這類 API 的使用可能受到其他《服務條款》約束。除非相關《服務條款》明文允許,否則您不得根據這類資料 (不論是整個或部分) 修改、出租、出借、販售、散佈或製作衍生成品。 - -8.1.2 使用任何 API 從 Google 擷取使用者的資料時,您瞭解並同意只有在該使用者明確同意且授權您擷取其資料,而且僅限用於使用者所授權之用途的情況下,您才能擷取資料。 - -9.終止「授權協議」 - -9.1 除非您或 Google 終止「授權協議」(請見下方說明),否則「授權協議」將持續具有效力。 - -9.2 如果想終止「授權協議」,只要停止使用此「預覽版」及任何相關的開發人員憑證即可。 - -9.3 Google 可隨時通知您終止「授權協議」,無論有無原因。 - -9.4「授權協議」在先發生下列任一情況時,將自動終止而不另行通知或採取其他行動: -(A) Google 決定不再提供此「預覽版」或此「預覽版」的特定部分給您所居住或使用此服務之國家/地區的使用者;或 -(B) Google 發行最終版本的 Android SDK。 - -9.5 當「授權條款」終止時,您在「授權協議」所獲得的授權也將會一併終止,您將立即停止「預覽版」的所有使用,而第 10、11、12 和 14 項的條款將無限期持續適用。 - -10.免責聲明 - -10.1 您明確瞭解並同意完全自負使用此「預覽版」的風險,並且此「預覽版」是依「現況」和「可提供性」提供,Google 不負任何擔保責任。 - -10.2 您對使用此「預覽版」及透過此「預覽版」以下載或其他方式取得的任何內容,需自行斟酌和自負風險,而且您對因這類使用而對您的電腦系統或其他裝置所造成的任何損害或資料遺失,需負起全責。不限於前述,您瞭解此「預覽版」不是穩定的版本,可能會包含許多錯誤、瑕疵和安全性弱點而對您的電腦系統或其他裝置造成嚴重的損害,包括完全、不可回復的損失。 - -10.3 Google 進一步明確表示不提供任何形式的瑕疵擔保和條件 (不論明示或默示),包括但不限於適售性、適合特定用途及未侵權的默示擔保和條件。 - -11.責任限制 - -11.1 您明確瞭解並同意在任何歸責理論下,就可能由您引起的任何直接、間接、附隨性、特殊性、衍生性或懲罰性損害賠償 (包括任何資料遺失),不論 Google 或其代表是否已獲告知或應已瞭解發生任何這類損失的可能性,Google、其子公司和關係企業及其授權人不必對您負起任何責任。 - -12.賠償 - -12.1 在法律允許的最大範圍內,您同意為 Google、其子公司及其個別董監事、主管、員工和代理人,就任何和一切索賠、法律行動、訴訟或訴訟程序,以及因下列原因而引起的任何和一切損失、責任、損害賠償、費用及開支 (包括合理的律師費),提供辯護、賠償損失並確保其免於承擔賠償責任:(a) 您使用此「預覽版」;(b) 您使用此「預覽版」開發的應用程式侵害任何人的任何智慧財產權,或是詆毀任何人或違反其公開權或隱私權;以及 (c) 您未遵守「授權協議」。 - -13.對「授權協議」做出的變更 - -13.1 Google 可在散佈此「預覽版」的新版本時修改「授權協議」。做出這類變更後,Google 會在提供此「預覽版」的網站上提供「授權協議」的新版本。 - -14.一般法律條款 - -14.1「授權協議」構成您與 Google 之間的法律協議,用於管制您對此「預覽版」(不包括 Google 依據個別書面協議提供給您的任何服務) 的使用,並完全取代先前您與 Google 之間就此「預覽版」簽署的相關協議。 - -14.2 您同意如果 Google 未行使或執行「授權協議」所含的任何法律權利或救濟 (或在任何適用法律下對 Google 有利的權益),並不代表 Google 正式放棄權利,Google 日後仍可行使這些權利或救濟。 - -14.3 如果經任何法院 (就此事宜依管轄權決定) 裁決「授權協議」中有任何條款無效,則該條款將自「授權協議」中移除,「授權協議」的其餘部分則不受影響。「授權協議」的其餘條款將持續具有效力且可執行。 - -14.4 您瞭解並同意 Google 旗下子公司體系的每位成員都是「授權協議」的第三方受益人,而且這類其他公司有權直接執行和依據「授權協議」中對其授予權益 (或對其有利之權利) 的任何條款。除此之外的任何其他人員或公司皆非「授權協議」的第三方受益人。 - -14.5 出口限制。此「預覽版」受美國出口法規約束。您必須遵守適用於此「預覽版」的所有國內和國際出口法規。這些法律包括對目的地、使用者及最終用途的限制。 - -14.6 未事先取得 Google 事先書面核准的情況下,您不得轉讓或轉移「授權協議」,未經這類核准的任何轉讓將會失效。您不得在未事先取得 Google 書面核准的情況下,委派其「授權協議」涵蓋的責任或義務。 - -14.7「授權協議」以及您與 Google 就「授權協議」構成的關係皆受加州法律管轄,毋須理會其法律牴觸條款。您和 Google 同意服從位於加州聖塔克拉拉 (Santa Clara, California) 郡內法院的專屬管轄權,以解決由「授權協議」產生的任何法律事務。儘管如此,您同意 Google 仍可在任何管轄權中申請禁制令救濟 (或同等類型的緊急法定救濟)。 - - -
    \ No newline at end of file diff --git a/docs/html-intl/intl/zh-tw/preview/overview.jd b/docs/html-intl/intl/zh-tw/preview/overview.jd deleted file mode 100644 index 9693eec670f1a8a011c98f4f244aaf5577585658..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/overview.jd +++ /dev/null @@ -1,389 +0,0 @@ -page.title=程式總覽 -page.metaDescription=歡迎使用 Android M 開發人員預覽版,本程式提供為新版 Android 測試和最佳化您應用程式所需的一切。 -page.image=images/cards/card-preview_16-9_2x.png -page.tags="preview", "developer", "android" - -@jd:body - -
    -

    - Developer Preview 2 is now available -

    - - -
    - -

    - 歡迎使用「Android M 開發人員預覽版」,本程式提供為新版 Android 測試和最佳化您應用程式所需的一切。 - -免費使用,您只要下載 M 開發人員預覽版工具,就能立即開始使用。 - -

    - -
    -
    -
    -
    -
    - 硬體與模擬器系統映像 -
    - -

    - 在 Nexus 5、6、9 和 Player (適用於電視) ,以及模擬器上執行和測試您的應用程式。 - -

    -
    - -
    -
    - 最新的平台程式碼 -
    - -

    - 我們將在預覽版期間提供多次更新,讓您能夠針對最新的平台變更進行測試。 - -

    -
    - -
    -
    - 透過 OTA 傳遞更新 -
    - -

    - 在您將裝置刷新為初始預覽版之後,就能以無線方式取得更新。 - -

    -
    -
    - -
    - - -
    -
    - 新行為和功能 -
    - -

    - 儘早開始開發以支援新的平台行為,例如新的執行階段權限模型和省電功能。 - -

    -
    - -
    -
    - 開發人員限時優先回報問題 -
    - -

    - 在前幾個星期內,我們將讓開發人員優先回報問題,因此請盡快測試並提供意見反應。 - -

    -
    - -
    -
    - 意見反應與支援 -
    - -

    - 使用問題追蹤器回報問題並提供意見反應。 - 與 M 開發人員社群中的其他開發人員聯絡。 - -

    -
    -
    -
    -
    - - - - -

    - 時間軸和更新 -

    -Preview program timeline -

    - M 開發人員預覽版可從 5 月 28 日開始執行,直到我們將在 2015 年第 3 季正式發行之前發行的最終版 Android M SDK。 - - -

    - -

    - 我們將在主要的開發里程碑為您的測試裝置提供更新。 - 里程碑暫定如下 -

    - -
      -
    • - 預覽版 1 (初始預覽版,五月下旬)、 -
    • - -
    • - 預覽版 2 (六月下旬/七月上旬) 及 -
    • - -
    • - 預覽版 3 (接近最終版,七月下旬) -
    • -
    - -

    - 這些更新最終會成為「最終版 SDK」 (稍後於第 3 季),為新版 Android 提供正式的 API,以及最終的系統行為和功能。 - - -

    - -

    - 當您在 Android M 上測試和開發時,強烈建議您在預覽版更新發行時立即更新,讓「您的開發環境保持在最新狀態」。 - - 為了讓程序更簡單,我們將對更新為預覽版建置的裝置以無線 (OTA) 方式提供更新,還會提供您能手動下載和更新的系統映像。 - - -

    -

    - 注意:最終版 SDK 與系統映像無法以 OTA 方式提供,將必須以手動方式更新至您的測試裝置。 - - -

    - -

    - 我們將透過 Android 開發人員部落格,還有本網站與 Android M 開發人員社群來通知您有可用的預覽版更新。 - - -

    - -

    - 預覽版新增功能 -

    - -

    - M 開發人員預覽版包含您針對各種不同螢幕大小、網路技術、 CPU/GPU 晶片組及硬體架構,測試現有應用程式所需的一切。 - - -

    - -

    - SDK 工具 -

    - -

    - 您可以透過 Android Studio 中的「SDK 管理器」下載以下元件: -

    - -
      -
    • M 開發人員預覽版 SDK 工具 -
    • - -
    • M 開發人員預覽版模擬器系統映像 (32 位元和 64 位元) - -
    • - -
    • M 開發人員預覽版模擬器系統映像 (適用於 Android TV) (32 位元) - -
    • -
    - -

    - 硬體系統映像 -

    - -

    - 您可以從下載頁面下載適用於 Nexus 裝置的以下硬體系統映像: - -

    - -
      -
    • - Nexus 5 (GSM/LTE)“hammerhead”裝置系統映像 -
    • - -
    • - Nexus 6“shamu”裝置系統映像 -
    • - -
    • - Nexus 9 (Wi-Fi)“volantis”裝置系統映像 -
    • - -
    • - Nexus Player (Android TV)“fugu”裝置系統映像 -
    • -
    - -

    - 文件和範例程式碼 -

    - -

    - 這些文件資源可協助您瞭解預覽版: -

    - - - -

    - 支援資源 -

    - -

    - 在 M 開發人員預覽版上測試和開發時,請使用以下支援資源: - -

    - -
      -
    • M 開發人員預覽版問題追蹤器是您的主要意見反應管道。 - -您可以透過問題追蹤器來回報錯誤、效能問題及一般意見反應。 -您也可以檢查已知問題 -和尋找因應方式步驟。 -
    • - -
    • Android M 開發人員社群是您能與其他 Android M 開發人員聯絡的 Google+ 社群。您可以分享有關 Android M 的觀察或想法,或尋找解答。 - - - -
    • -
    - - -

    - 目標設定、預覽版 API 及發行 -

    - -

    - Android M 開發人員預覽版是開發專用的版本,而且沒有標準的 API 層級。 -如果您想要選擇退出相容性行為以測試您的應用程式 (強烈建議),您可以將應用程式的 targetSdkVersion 設定為 “MNC”,就能以 M 開發人員預覽版為目標。 - - - -

    - -

    - Android M 開發人員預覽版提供預覽版 API — 在最終版 SDK (目前規劃在 2015 年第三季) 發行之前的都不是正式 API。 - -這表示您可以預期 API 會隨時間而有些許變更,特別是程式一開始的幾週。 - -我們會將 Android M 開發人員預覽版每次更新的變更摘要提供給您。 - -

    - -

    - 請注意,雖然預覽版 API 可能會改變,但例如執行階段權限和省電功能等基本系統行為,均已穩定且能夠立即測試。 - - -

    - -

    - 在發行方面,Google Play 會禁止您發行以 M 開發人員預覽版為目標的應用程式。 -當 Android M 最終版 SDK 推出時,您將能夠以正式 Android M API 層級為目標,並將您的應用程式發行至 Google Play。 - -同時,您可以透過電子郵件或直接從您的網站下載,來對測試者散佈以 Android M 為目標的應用程式。 - - -

    - -

    - 如何開始 -

    - -

    - 如要開始測試您的應用程式: -

    - -
      -
    1. 檢閱 API 總覽行為變更,以瞭解新增功能,還有它會如何影響您的應用程式。 - -特別是瞭解新的執行階段權限模型、省電功能以及自動化備份。 - - -
    2. - -
    3. 依照設定預覽版 SDK 的指示來設定您的環境,並設定測試裝置。 - - -
    4. - -
    5. 依照刷新指示,針對 Nexus 5、6、9 及 Player 刷新最新的 M 開發人員預覽版系統映像。 - -在您刷新開發裝置之後,預覽版更新將以無線 (OTA) 更新的方式提供。 - -
    6. - -
    7. 下載 M 預覽版 API 參考資料M 預覽版範例,以深入瞭解新的 API 功能以及如何在您的應用程式中運用。 - - - -
    8. - -
    9. 加入 Android M 開發人員社群以取得最新消息,並與其他使用新平台的開發人員聯絡。 - - -
    10. -
    - -

    - 感謝您參與 Android M 開發人員預覽版程式! -

    diff --git a/docs/html-intl/intl/zh-tw/preview/samples.jd b/docs/html-intl/intl/zh-tw/preview/samples.jd deleted file mode 100644 index 2ef9a60b5129c0aeed11e43779cda6985c78093f..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/samples.jd +++ /dev/null @@ -1,70 +0,0 @@ -page.title=範例 -page.image=images/cards/samples-new_2x.png -@jd:body - -

    - 下列提供 M 開發人員預覽版的程式碼範例。如要下載 Android Studio 的範例,請選取 [File] (檔案) > [Import Samples] (匯入範例) 選單選項。 - -

    - -

    - 注意:這些可下載的專案是專為與 Gradle 和 Android Studio 一起使用而設計。 - -

    - - -

    執行階段權限

    - -

    - Android M 改變系統權限的運作方式。改為在執行階段才要求使用者核准權限要求,而不是安裝期間。 -這個範例顯示如何要求這些權限。 - -

    - -

    在 GitHub 上取得

    - -

    確認認證

    - -

    - 這個範例示範如何在您的應用程式中使用裝置認證做為驗證方法。 -

    - -

    在 GitHub 上取得 -

    - -

    指紋對話方塊

    - -

    - 這個範例示範如何在您的應用程式中辨識註冊的指紋以驗證使用者。 - -

    - -

    在 GitHub 上取得

    - -

    針對應用程式進行自動備份

    - -

    - Android M 導入自動備份應用程式設定的功能。這個範例示範如何將篩選規則新增至應用程式以管理設定備份。 - -

    - -

    在 GitHub 上取得

    - -

    相機 2 Raw

    - -

    - 示範如何使用 Camera2 API,以擷取 RAW 相機緩衝區並另存為 DNG 檔案。 - -

    - -

    在 GitHub 上取得

    - -

    使用中通知

    - -

    - 這個範例示範 -NotificationManager - 如何將您應用程式目前顯示的通知數目告訴您。 -

    - -

    在 GitHub 上取得

    diff --git a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd b/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd deleted file mode 100644 index 1769f74dcfd778b7538b4e7450a6816b16352f8a..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/setup-sdk.jd +++ /dev/null @@ -1,207 +0,0 @@ -page.title=設定預覽版 SDK -page.image=images/cards/card-set-up_16-9_2x.png - -@jd:body - - - - -

    M 開發人員預覽版 SDK 可以從 Android SDK Manager 取得。本文件假設您熟悉 Android 應用程式開發工作,例如使用 Android SDK Manager 和建立專案。 - -如果您是 Android 的新手,請先參閱建置您的第一個應用程式訓練課程。 - -

    - -

    取得 Android Studio 1.3

    - -

    開發人員預覽版最適合與處於預覽版狀態的 Android Studio 1.3 一起使用。 -強烈建議您安裝 Android Studio 1.3 預覽版與開發人員預覽版一起使用。 -

    - -

    注意:Android Studio 1.3 的測試預覽版仍在持續開發中。 -如果您使用主要開發電腦來測試開發人員預覽版,可以建立第二個 Android Studio 安裝以用於測試。 - -

    - -

    如要安裝 Android Studio 1.3 預覽版:

    - -
      -
    1. 下載並啟動 Android Studio。 - -
    2. - -
    3. 開啟 [Settings] (設定) 視窗 (在 Windows 上,您可以選擇 [檔案] > [設定] 來執行此動作)。 -選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Updates] (更新) 面板。 - - - -

      在 OSX 上,您可以在 Android Studio 的「Preferences」(偏好設定) 視窗中,找到 [Appearance & Behavior] (外觀和行為) 面板。 - -

      -
    4. - -
    5. 在「Updates」(更新) 面板上,選擇以下選項:[Automatically check updates for: (自動檢查以下的可用更新:) -Canary Channel] (測試版管道)。 -
    6. - -
    7. 在「Updates」(更新) 面板上,選取 [Check Now] (立即檢查) 以檢查是否有可用的最新測試版建置。 -版本請在系統提示您時,下載並安裝該版本。 - -
    8. -
    - -

    取得預覽版 SDK

    - -

    如要將預覽版 SDK 元件新增至您的開發環境:

    - -
      -
    1. 啟動 Android Studio 1.3 預覽版。 -
    2. - -
    3. 開啟 [Settings] (設定) 視窗 (在 Windows 上,您可以選擇 [檔案] > [設定] 來執行此動作)。 -選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Updates] (更新) 面板。 - - - -

      在 OSX 上,您可以在 Android Studio 的「Preferences」(偏好設定) 視窗中,找到 [Appearance & Behavior] (外觀和行為) 面板。 - -

      -
    4. - -
    5. 在「Updates」(更新) 面板上,選擇以下選項:[Automatically check updates for: (自動檢查以下的可用更新:) -Canary Channel] (測試版管道) 以及 [Automatically check updates for Android SDK: (自動檢查 Android SDK 的可用更新:) -Preview Channel] (預覽版管道)。 -
    6. - -
    7. 啟動 Android Studio Manager。(使用 Android Studio 1.3,SDK Manager 會與 Android Studio 整合,而不是獨立的應用程式。) - - -
    8. - -
    9. 在「Platforms」(平台) 區段下,選取 [Android MNC Preview] (Android MNC 預覽版)。 - -
    10. - -
    11. 在「Tools」(工具) 區段中,選擇最新的 Android SDK 工具、平台工具以及建置工具。 - - -
    12. - -
    13. 按一下 [Install packages] (安裝套件) 並接受所有套件的授權合約。 - -
    14. - -
    15. 開啟 [Settings] (設定) 視窗,然後選擇 [Appearance & Behavior] (外觀和行為) > [System Settings] (系統設定) > [Android SDK] 面板,來驗證已安裝 M 開發人員預覽版。 - -
    16. - -
    17. 在「Android SDK」面板上,選擇 [SDK Platforms] (SDK 平台) 索引標籤。 -「Android MNC Preview」(Android MNC 預覽版) 應該會列在「Installed」(已安裝)。 -此外,開啟 [SDK Tools] (SDK 工具) 索引標籤以確認已經安裝最新的工具。 - - -
    18. -
    -

    完成這些步驟之後,就能在您的開發環境中使用預覽版元件。 -

    - - -

    建立或更新專案

    - -

    - 為了使用預覽版 API,您必須建立或更新開發專案,才能使用預覽版元件。 - -

    - - -

    建立新專案

    - -

    - 建議您使用 Android Studio 搭配預覽版來建立專案。依照建立專案中所述的步驟執行,直到您到達專案精靈中的「大小」畫面。 - -接著,執行下列步驟以建立為預覽版設定的專案。 - -

    - -
      -
    • 核取 [Phone and Tablet] (手機與平板電腦)。
    • -
    • 選取 [MNC:Android M (Preview)] (Android M (預覽版)),它位於 [Minimum SDK] (SDK 最低版本)。 -
    • -
    - - -

    更新現有的專案

    - -

    - 對於現有專案,您必須修改專案設定以啟用預覽版 API。在您的開發環境中,開啟模組的 build.gradle 檔案,然後將這些值設定如下: - - -

    - -
      -
    • compileSdkVersion 設定為 'android-MNC'
    • -
    • minSdkVersion 設定為 'MNC'
    • -
    • targetSdkVersion 設定為 'MNC'
    • -
    - - -

    設定以進行測試

    - -

    - 利用預覽版測試應用程式,需要您將裝置或虛擬裝置設定為使用平台的預覽版本。 -如果您有相容的裝置,您可以安裝預覽版平台以用於測試。 -否則,您可以設定虛擬裝置以用於測試。 -

    - -

    設定實體裝置

    - -

    - 如果您有 Nexus 5、Nexus 6、Nexus 9 或 Android 電視,您可以在這些裝置上安裝預覽版系統映像,以測試您的應用程式。您可以使用 Android Virtual Device Manager (Android 虛擬裝置管理員) 工具,從 Android Studio 內設定使用平台預覽版本的虛擬裝置。 - - - -

    - -

    - 重要說明:在裝置上安裝預覽版映像,會「移除當中的所有資料」,因此您應該在安裝預覽版映像之前備份任何資料。 - -

    - -

    設定虛擬裝置

    - -

    - 您可以使用 Android Virtual Device Manager (Android 虛擬裝置管理員) 工具,從 Android Studio 內設定使用平台預覽版本的虛擬裝置。 - -

    - -

    如要使用 AVD Manager 來建立 AVD:

    - -
      -
    1. 請在您的開發環境中安裝預覽版 SDK,如設定預覽版 SDK 中所述。 - -
    2. -
    3. 依照使用 AVD Manager 來管理 AVD中的步驟執行。 - -使用下列設定: -
        -
      • 裝置:Nexus 5、Nexus 6、Nexus 9 或 Android 電視
      • -
      • 目標: - Android M (預覽版) - API 級別 M
      • -
      • ABI: x86
      • -
      -
    4. -
    - -

    - 如需建立虛擬裝置以用於測試的詳細資訊,請參閱管理虛擬裝置。 -

    diff --git a/docs/html-intl/intl/zh-tw/preview/testing/guide.jd b/docs/html-intl/intl/zh-tw/preview/testing/guide.jd deleted file mode 100644 index 879ec02cd5f5459fb81000c49794efb2e647be59..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/testing/guide.jd +++ /dev/null @@ -1,187 +0,0 @@ -page.title=測試指南 -page.image=images/cards/card-build_16x9_2x.png -page.keywords=previewresources,androidm,testing,permissions - -@jd:body - - - -

    - Android M 開發人員預覽版能夠讓您有機會確保您的應用程式能搭配下一個版本的平台運作。 -此預覽版包含許多 API 和足以影響應用程式的行為變更,如 API 總覽行為變更中所述。 - -使用預覽版測試應用程式時,您必須著重在許多特定的系統變更,以確保使用者都能擁有良好的體驗。 - - -

    - -

    - 本指南說明對應用程式測試預覽功能的內容與方法。您應優先測試這些特定的預覽功能,因為這些功能可能會對您的應用程式行為有非常大的影響: - - -

    - - - -

    - 如需有關如何設定使用預覽版系統映像的裝置或虛擬裝置以進行測試的詳細資訊,請參閱設定預覽版 SDK。 - -

    - - -

    測試權限

    - -

    - 新的權限模型改變使用者對您的應用程式分配權限的方式。 -以前在安裝過程中會授與所有權限,現在您的應用程式則必須在執行階段向使用者要求個別的權限。 - -對於使用者而言,這個行為能夠對每個應用程式的Activity提供更細膩的控制,而且也能更瞭解應用程式為何要要求特定權限。 -使用者可以隨時個別對應用程式授與或撤銷權限。 -預覽版的這個功能最有可能影響到您應用程式的行為,而且可能會導致應用程式某些功能無法正常運作,或者這些功能會以降級的狀態運作。 - - -

    - -

    - 這個變更會影響在新平台上執行的所有應用程式,甚至影響並非針對新平台版本開發的應用程式。 -平台會對舊版應用程式提供有限的相容性行為,但是您最好還是立即開始規劃將應用程式移轉到新的權限模型,以便能在官方平台上市時發佈更新的應用程式版本。 - - -

    - - -

    測試祕訣

    - -

    - 使用下列測試祕訣,可以幫助您使用新的權限行為,規劃和執行應用程式測試。 - -

    - -
      -
    • 辨別應用程式目前的權限與相關的程式碼路徑。
    • -
    • 針對所有權限保護的服務與資料測試使用者流程。
    • -
    • 測試各種組合的授與/撤銷權限。
    • -
    • 從命令列使用 {@code adb} 工具管理權限: -
        -
      • 依群組列出權限與狀態: -
        adb shell pm list permissions -d -g
        -
      • -
      • 使用下列語法授與或撤銷一或多個權限:
        -
        adb shell pm [grant|revoke] <permission.name> ...
        -
      • -
      -
    • -
    • 分析使用權限的應用程式服務。
    • -
    - -

    測試策略

    - -

    - 權限變更會影響應用程式的結構和設計,同時也會影響使用者的體驗以及您提供給使用者的流程。 -您應該評估應用程式目前的權限作法,然後開始規劃您要提供的新流程。 -官方版本的平台會提供相容性行為,但是您最好規劃更新您的應用程式,而不要依賴這些行為。 - - -

    - -

    - 辨別您的應用程式實際需要且使用到的權限,然後找出使用受權限保護之服務的各種程式碼路徑。 -您可以在新平台上測試,並且進行程式碼分析,來達到上述目的。 -測試時,您應該變更應用程式的 {@code targetSdkVersion} 為預覽版本,著重在加入執行階段權限。 -如需詳細資訊,請參閱設定預覽版 SDK。 - -

    - -

    - 測試各種撤銷/加入權限的組合,著重在依據這些權限的使用者流程。 -如果相依性不明顯或邏輯不清楚時,您可以考慮重新分解或劃分流程以消除相依性,或是更清楚為何需要某個權限。 - - -

    - -

    - 如需有關執行階段權限行為、測試和建議做法的詳細資訊,請參閱 權限開發人員預覽版頁面。 - - -

    - - -

    測試休眠與應用程式待命

    - -

    - 當裝置處於閒置狀態,或者當您的應用程式不在使用中時,「休眠」與「應用程式待命」的省電功能會限制應用程式可以執行的背景處理量。 -系統可能加諸在應用程式的限制,包括網路受到限制或沒有網路、背景工作暫停、通知暫停、喚醒要求被略過,以及警示被略過。 - -如要確保您的應用程式能正確運作,而且這些省電功能也達到最好的效益,您應該透過模擬這些低電量狀態,測試您的應用程式。 - - -

    - -

    搭配休眠測試應用程式

    - -

    對您的應用程式測試休眠:

    - -
      -
    1. 設定 M 預覽版系統映像的硬體裝置或虛擬裝置。
    2. -
    3. 將裝置連線到您的開發電腦並安裝您的應用程式。
    4. -
    5. 執行您的應用程式並讓應用程式保持使用中。
    6. -
    7. 執行下列命令,模擬裝置進入「休眠」模式: - -
      -$ adb shell dumpsys battery unplug
      -$ adb shell dumpsys deviceidle step
      -$ adb shell dumpsys deviceidle -h
      -
      - -
    8. -
    9. 觀察當裝置重新啟動時的應用程式行為。當裝置結束「休眠」時,確定應用程式順利回復。 -
    10. -
    - - -

    搭配應用程式待命測試應用程式

    - -

    對您的應用程式測試應用程式待命模式:

    - -
      -
    1. 設定 M 預覽版系統映像的硬體裝置或虛擬裝置。
    2. -
    3. 將裝置連線到您的開發電腦並安裝您的應用程式。
    4. -
    5. 執行您的應用程式並讓應用程式保持使用中。
    6. -
    7. 執行下列命令,模擬應用程式進入待命模式: - -
      -$ adb shell am broadcast -a android.os.action.DISCHARGING
      -$ adb shell am set-idle <packageName> true
      -
      - -
    8. -
    9. 使用下列命令,模擬喚醒應用程式: -
      $ adb shell am set-idle <packageName> false
      -
    10. -
    11. 觀察當應用程式被喚醒時的行為。確定應用程式順利從待命模式回復。 -您尤其要檢查應用程式通知與背景工作是否有如預期般繼續運作 -。
    12. -
    - -

    針對應用程式進行自動備份與裝置特定識別碼

    - -

    如果您的應用程式在內部儲存空間存有任何裝置特定識別碼,例如 Google -雲端通訊註冊 ID,請確認按照建議做法排除這些儲存位置不要進行自動備份,如針對應用程式進行自動備份中所述。 - - -

    diff --git a/docs/html-intl/intl/zh-tw/preview/testing/performance.jd b/docs/html-intl/intl/zh-tw/preview/testing/performance.jd deleted file mode 100644 index 5437e9da6bfa49c910a6d0de67367e1ff1119cf6..0000000000000000000000000000000000000000 --- a/docs/html-intl/intl/zh-tw/preview/testing/performance.jd +++ /dev/null @@ -1,656 +0,0 @@ -page.title=測試顯示效能 -page.image=images/cards/card-test-performance_2x.png -page.keywords=效能, fps, 工具 - -@jd:body - - - - - -

    - 使用者介面效能測試可確保您的應用程式不只符合功能需求,與使用者與應用程式的互動也無比順暢,執行時每秒一致有 60 個畫面 (為什麼 60fps?),任何畫面都不會遺漏或延遲,或稱為「閃避」現象。 - - -本文件說明可用以測量 UI 效能的工具,以及呈現可將 UI 效能測量與測試做法整合的方法。 - - -

    - - -

    測量 UI 效能

    - -

    - 為改善效能,首先您需要測量系統效能的能力,接著在管道的各部分發生問題時加以診斷和辨識。 - - -

    - -

    - dumpsys 是一種 Android 工具,可在裝置上執行和傾印有關系統服務狀態的有趣資訊。 - -將 gfxinfo 命令傳送至 dumpsys,會將錄製階段所發生與動畫的畫面相關的效能資訊以 logcat 提供輸出。 - - -

    - -
    -> adb shell dumpsys gfxinfo <PACKAGE_NAME>
    -
    - -

    - 此命令會產生多種不同的畫面計時資料。 -

    - -

    彙總畫面統計資料

    - -

    - 使用 M 預覽版,命令會在程序的生命週期全程收集畫面資料,並將彙總的分析列印到 logcat。 -例如: -

    - -
    -Stats since: 752958278148ns
    -Total frames rendered: 82189
    -Janky frames: 35335 (42.99%)
    -90th percentile: 34ms
    -95th percentile: 42ms
    -99th percentile: 69ms
    -Number Missed Vsync: 4706
    -Number High input latency: 142
    -Number Slow UI thread: 17270
    -Number Slow bitmap uploads: 1542
    -Number Slow draw: 23342
    -
    - -

    - 這些高階統計資料是以高階方式轉換應用程式的轉譯效能,還有其在許多畫面的穩定性。 - -

    - - -

    精確畫面計時資訊

    - -

    - M 預覽版隨附新的命令 gfxinfo,而 framestats 可從最近的畫面提供相當詳細的畫面計時資訊,讓您可以追蹤並更準確進行除錯。 - - -

    - -
    ->adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats
    -
    - -

    - 此命令會從應用程式所產生至少 120 個畫面當中,加上奈秒時間戳記印出畫面計時資訊。以下範例是 adb dumpsys gfxinfo <PACKAGE_NAME> framestats 的原始輸出: - - -

    - -
    -0,49762224585003,49762241251670,9223372036854775807,0,49762257627204,49762257646058,49762257969704,49762258002100,49762265541631,49762273951162,49762300914808,49762303675954,
    -0,49762445152142,49762445152142,9223372036854775807,0,49762446678818,49762446705589,49762447268818,49762447388037,49762453551527,49762457134131,49762474889027,49762476150120,
    -0,49762462118845,49762462118845,9223372036854775807,0,49762462595381,49762462619287,49762462919964,49762462968454,49762476194547,49762476483454,49762480214964,49762480911527,
    -0,49762479085548,49762479085548,9223372036854775807,0,49762480066370,49762480099339,49762481013089,49762481085850,49762482232152,49762482478350,49762485657620,49762486116683,
    -
    - -

    - 這裡的每一行輸出都代表應用程式產生的一個畫面。每行都有固定的資料欄編號,描述畫面產生管道的各階段所花費的時間。 -下一節會詳細說明此格式,包括各資料欄代表的意義。 - -

    - - -

    Framestats 資料格式

    - -

    - 由於資料區塊是以 CSV 格式輸出,所以可以直接將它貼到選擇的試算表工具,或使用指令碼來收集和剖析。 -下表說明輸出資料欄的格式。 -所有時間戳記都以奈秒為單位。 -

    - -
      -
    • FLAGS -
        -
      • FLAGS 資料欄的資料列均為 0,從 FRAME_COMPLETED 資料欄減去 INTENDED_VSYNC 資料欄可計算得出總畫面時間。 - -
      • - -
      • 如果這個值非零,應該略過該資料列,因為從正常效能來判斷,畫面已是極端值,版面配置與繪製預期都要花費 16ms 以上的時間。 - -以下是發生這種情況的幾個原因: -
          -
        • 視窗版面配置改變 (例如應用程式的第一個畫面或經過旋轉) - -
        • - -
        • 也可能是略過畫面,在這種情況下,某些值會含有記憶體回收的時間戳記。 -例如,如果畫面速度超過 60fps,或如果螢幕上空無一物,最後卻有所改變,就可能會略過畫面,這不一定是應用程式發生問題的徵兆。 - - -
        • -
        -
      • -
      -
    • - -
    • INTENDED_VSYNC -
        -
      • 預期的畫面起始點。如果這個值和VSYNC 不同,表示 UI 執行緒中發生的工作使它無法即時回應 vsync 訊號。 - - -
      • -
      -
    • - -
    • VSYNC -
        -
      • 用於所有 vsync 接聽器和繪製畫面的時間值 (Choreographer 畫面回呼、動畫、View.getDrawingTime() 等等) - -
      • - -
      • 如要深入瞭解有關 VSYNC 和它如何影響您的應用程式,請參閱瞭解 VSYNC 影片。 - - -
      • -
      -
    • - -
    • OLDEST_INPUT_EVENT -
        -
      • 輸入佇列中最舊輸入事件的時間戳記,或為 Long.MAX_VALUE,若沒有該畫面的輸入事件的話。 - -
      • - -
      • 這個值主要用於平台工作,對應用程式開發人員用處不大。 - -
      • -
      -
    • - -
    • NEWEST_INPUT_EVENT -
        -
      • 輸入佇列中最新輸入事件的時間戳記,或為 0,若沒有該畫面的輸入事件的話。 - -
      • - -
      • 這個值主要用於平台工作,對應用程式開發人員用處不大。 - -
      • - -
      • 不過,透過查看 (FRAME_COMPLETED - NEWEST_INPUT_EVENT),可以大略知道應用程式還會延遲多少時間。 - -
      • -
      -
    • - -
    • HANDLE_INPUT_START -
        -
      • 將輸入事件分配給應用程式時的時間戳記。 -
      • - -
      • 查看這個值與 ANIMATION_START 之間的時間,即可測量出應用程式花費在處理輸入事件的時間。 - -
      • - -
      • 如果這個數字很高 (>2ms),這表示應用程式花費在處理輸入事件(例如 View.onTouchEvent()) 的時間過長,指出需要將這項工作最佳化,或卸載交由其他執行緒處理。 - -請注意,還有一些情況本就預期且可接受這個數字較大,例如會啟動新活動或類似工作的點擊事件。 - - -
      • -
      -
    • - -
    • ANIMATION_START -
        -
      • 向 Choreographer 註冊的動畫執行時的時間戳記。 -
      • - -
      • 查看這個值與 PERFORM_TRANVERSALS_START 之間的時間,即可判斷它花費多久的時間評估所有執行中的動畫器 (常見的有 ObjectAnimator、ViewPropertyAnimator 及 Transitions)。 - - -
      • - -
      • 如果這個數字很高 (>2ms),可查看您的應用程式是否撰寫任何自訂動畫器,或 ObjectAnimators 正進行動畫處理的資料欄,並確定它們都適用於動畫。 - - -
      • - -
      • 如要深入瞭解 Choreographer,請參閱順滑流暢與否影片。 - -
      • -
      -
    • - -
    • PERFORM_TRAVERSALS_START -
        -
      • 如果您從這個值中減去 DRAW_START,就可以得知版面配置和測量階段花費多久的時間完成。(請注意,在捲動或動畫期間,您會希望這個值趨近於零。) - - -
      • - -
      • 如要深入瞭解轉譯管道的測量與版面配置階段,請參閱無效判定、版面配置及效能影片。 - - -
      • -
      -
    • - -
    • DRAW_START -
        -
      • performTraversals 的繪製階段開始的時間。這是判定無效的任何檢視顯示清單的記錄起始點。 - -
      • - -
      • 這個值與 SYNC_START 之間的時間,就是對樹狀結構中所有無效判定檢視呼叫 View.draw() 所花費的時間。 - -
      • - -
      • 如需繪製模型的詳細資訊,請參閱硬體加速無效判定、版面配置及效能影片。 - - -
      • -
      -
    • - -
    • SYNC_START -
        -
      • 繪製的同步階段開始的時間。 -
      • - -
      • 如果這個值與 ISSUE_DRAW_COMMANDS_START 之間的時間相當長 (約為 >0.4ms ),通常代表已繪製許多必須上傳至 GPU 的新點陣圖。 - - -
      • - -
      • 如要深入瞭解同步階段,請參閱設定檔 GPU 轉譯影片。 - -
      • -
      -
    • - -
    • ISSUE_DRAW_COMMANDS_START -
        -
      • 硬體轉譯器開始對 GPU 發出繪製命令的時間。 -
      • - -
      • 這個值與 FRAME_COMPLETED 之間的時間大約就是應用程式產生的 GPU 工作量。 -太過度繪製或無效轉譯效果之類的問題都會顯示於此。 - -
      • -
      -
    • - -
    • SWAP_BUFFERS -
        -
      • 相對於無趣的平台工作以外,呼叫 eglSwapBuffers 的時間。 - -
      • -
      -
    • - -
    • FRAME_COMPLETED -
        -
      • 全部完成!花費在處理這個畫面的總時間,計算方法是 FRAME_COMPLETED - INTENDED_VSYNC。 - -
      • -
      -
    • - -
    - -

    - 您能以不同的方式使用這項資料。顯示不同延遲貯體中畫面時間分布的長條圖就是一種簡單但實用的方式,請見下圖。 - -本圖可一目瞭然地告訴我們,大部分畫面都低於 16ms 的上限 (紅色除外),但只有幾個畫面明顯超過上限。 - -我們可以查看此長條圖一段時間的變化,觀察出現的大規模位移或產生新的極端值。 -您也能根據資料中的許多時間戳記將輸入延遲、花費在版面配置的時間或其他類似的有趣度量指標繪成圖表。 - - -

    - - - - -

    簡易畫面計時傾印

    - -

    - 如果 [開發人員選項] 中的 [設定檔 GPU 轉譯] 設定為 [In adb shell dumpsys gfxinfo] -,adb shell dumpsys gfxinfo 命令會印出最近 120 個畫面的計時資訊,以定位鍵分隔值分成數個不同類別。 - -這項資料非常適合用來指出可能是繪製管道的哪個部分太慢。 - -

    - -

    - 類似於上述的 framestats,可以直接將它貼到選擇的試算表工具,或使用指令碼來收集和剖析。 - -下圖顯示許多由應用程式產生的畫面花費時間的分類細項。 - -

    - - - -

    - 執行 gfxinfo、複製輸出、將輸出貼入試算表應用程式,然後將資料繪製成堆疊長條圖的結果。 - -

    - -

    - 每個直條都代表動畫的一個畫面,其高度代表計算該畫面所花費的毫秒數。 -長條的每個色塊都代表轉譯管道的不同階段,好讓您看出應用程式的哪部分可能產生瓶頸。 - -如需瞭解繪製管道以及如何最佳化的詳細資訊,請參閱硬體加速或無效判定、版面配置及效能影片。 - - -

    - - -

    控制統計資料收集時間

    - -

    - framestats 與簡單的畫面計時都會收集極短時間內的資料 – 轉譯約需兩秒。 -為了精確控制這段時間,例如只限特定動畫的資料,您可以重設所有計數器,然後彙總收集的統計資料。 - - -

    - -
    ->adb shell dumpsys gfxinfo <PACKAGE_NAME> reset
    -
    - -

    - 這也能和傾印命令本身結合使用,定期收集和重設,持續擷取兩秒時間內的畫面。 - - -

    - - -

    診斷效能回復

    - -

    - 識別回復是追蹤問題和維護應用程式健康情況的第一步。 -不過,dumpsys 只能識別有問題存在與相關的嚴重性。 -您仍需要診斷造成效能問題的特定原因,以及找出適當的修正方式。 -因此,強烈建議您使用 systrace 工具。 - -

    - - -

    其他資源

    - -

    - 如需 Android 的轉譯管道如何運作、常見問題以及如何修正的詳細資訊,下列的一些資訊可能會很實用: - - -

    - -
      -
    • 轉譯效能 101 -
    • -
    • 為什麼 60fps? -
    • -
    • Android UI 和 GPU -
    • -
    • 無效判定、版面配置及效能 -
    • -
    • 利用 Systrace 分析 UI 效能 -
    • -
    - - -

    自動化 UI 效能測試

    - -

    - UI 效能測試的方法之一就是讓測試人員對目標應用程式執行一組使用者操作,並以肉眼查看,或花費很長一段時間使用工具導向的方法,尋找閃避現象。 - -但這種靠人工的方式充滿危險,人類對畫面率變化的感知能力因人而異,而且這種方法也很費時、繁瑣且容易出錯。 - - -

    - -

    - 較有效率的方法是從自動化的 UI 測試中記錄和分析重要效能度量指標。 -Android M 開發人員預覽版包含新的記錄功能,能夠輕鬆判斷應用程式動畫中閃避現象的數量與嚴重程度,還能用來建置嚴謹的程序,判斷目前的效能並追蹤未來的效能目標。 - - - -

    - -

    - 本文會逐步說明建議用來使用資料以自動化效能測試的方法。 - -

    - -

    - 這種方法大多分成兩個主要動作。首先,識別您要測試的項目,以及測試的方法。其次是設定和維護自動化測試環境。 - - -

    - - -

    設定 UI 測試

    - -

    - 在您開始進行自動化測試之前,務必要決定幾個高階決策,才能適當瞭解您的測試空間與可能會有的需求。 - -

    - -

    - 識別要測試的主要動畫 / 流程 -

    - -

    - 請記住,流暢的動畫有所中斷時,就是使用者最容易看見效能低落的時候。 -因此,識別要測試哪種類型的 UI 動作時,最好著重在使用者最常看見或對他們的體驗最重要的主要動畫。 - -例如,以下是一些可能有利於識別的常見情況: -

    - -
      -
    • 捲動主要的 ListView 或 RecyclerView -
    • - -
    • 非同步等待週期內的動畫 -
    • - -
    • 當中會載入 / 操縱點陣圖的任何動畫 -
    • - -
    • 包含 Alpha 透明混色的動畫 -
    • - -
    • 使用畫布繪製的自訂檢視 -
    • -
    - -

    - 和您團隊的工程人員、設計師及產品經理合作,優先考慮將這些主要產品動畫放入測試涵蓋範圍內。 - -

    - -

    - 定義未來目標並予以追蹤 -

    - -

    - 從高階觀點來看,重要的是識別特定的效能目標,並著重在撰寫測試及收集相關資料。 -例如: -

    - -
      -
    • 您是否只想初次開始追蹤 UI 效能以深入瞭解? -
    • - -
    • 您是否想要避免可能在未來導入的回復? -
    • - -
    • 今日有 90% 的順暢畫面並想要在本季達到 98%? -
    • - -
    • 今日有 98% 的順暢畫面且不想要回復? -
    • - -
    • 您的目標是改善低階裝置上的效能嗎? -
    • -
    - -

    - 在上述的這些情況中,您會想要有歷史追蹤功能,來顯示不同應用程式版本間的效能。 - -

    - -

    - 識別測試要用的裝置 -

    - -

    - 應用程式效能會因其執行所在裝置而異。有些裝置包含的記憶體較少、GPU 較不強大或 CPU 晶片速度較慢。 -這表示可在某組硬體上執行良好的動畫,在其他組合上不一定能執行良好,更糟的是可能會在管道的不同部分產生瓶頸。 - -使用者所見可能會不同,為將這點列入考量,請挑選涵蓋當前高階裝置、低階裝置、平板電腦等的一系列裝置執行測試。 - -尋找 CPU 效能、RAM、畫面密度、大小等方面的變化。 -高階裝置上通過的測試,在低階裝置上可能會失敗。 - -

    - -

    - UI 測試的基本架構 -

    - -

    - 工具套件 (例如 UI AutomatorEspresso) 是為協助將使用者在您的應用程式四處移動的動作自動化而建置。 - -這些都是模擬使用者與裝置互動的簡單架構。 -如要使用這些架構,您要有效地建立會逐一執行一組使用者動作的獨特指令碼,然後在裝置上自行播放。 - - -

    - -

    - 連同 dumpsys gfxinfo,再結合這些自動化測試,您可快速建立可重現系統,讓您執行測試並測量該特定情況下的效能資訊。 - - -

    - - -

    設定自動化 UI 測試

    - -

    - 在您能夠執行 UI 測試,還有可從單一測試收集資料的管道後,下一個重要步驟是利用可多次執行該項測試的架構,然後彙總產生的效能資料,以供您的開發團隊進一步分析。 - - - -

    - -

    - 測試自動化的架構 -

    - -

    - 直接在目標裝置/模擬器上執行的 UI 測試架構 (例如 UI Automator) 毫無價值。 -因為效能收集資訊是由主控機器透過 ADB 傳送命令驅動 dumpsys gfxinfo 來完成。 -MonkeyRunner 架構是為了協助橋接這些個別實體開發。在主控機器上執行的指令碼處理系統可對一組連接的裝置發出命令,也能接收來自這些裝置的資料。 - - - -

    - -

    - 建置一組指令碼以適當自動化 UI 效能測試,至少應能利用 monkeyRunner 來完成下列工作: - -

    - -
      -
    • 對目標裝置或模擬器載入和啟動所需的 APK。 -
    • - -
    • 啟動並允許執行 UI Automator UI 測試 -
    • - -
    • 透過 dumpsys gfxinfo 收集效能資訊。 -
    • - -
    • 彙總資訊並以對開發人員有用的方式顯示。 -
    • -
    - - -

    分類和修正觀察到的問題

    - -

    - 在辨識出問題模式與回復之後,下一個步驟是辨識和套用修正。 -如果您的自動化測試架構會為畫面保留精確的計時分類細項,可幫助您詳細審察目前可疑的程式碼/版面配置變化 (在回復的情況下),或在您切換為靠人工探究時縮小要分析的系統部分。 - - -如需靠人工探究時,systrace 是開始進行的好地方,顯示轉譯管道各階段、系統中每個執行緒與核心,還有您所定義任何自訂事件標記的精確計時資訊。 - - -

    - -

    - 適當分析暫時的計時 -

    - -

    - 請務必注意,從轉譯效能中取得和測量計時的困難度。 -這些數字不具決定性且通常受系統狀態、可用記憶體數目、溫度調節,還有上次太陽閃焰何時衝擊您所在地區影響。 - -重點是您執行相同的測試兩次,而每次得到的數字都有些微不同,數字很接近但不會完全相同。 - - -

    - -

    - 以這種方式適當收集和分析資料,表示執行相同的測試多次,並累積結果取平均值或中間值。(為了簡單起見,我們稱之為「批次」) 這可讓您粗略計算測試的效能,而不需要精確的計時。 - - - -

    - -

    - 在變更的程式碼之間使用批次,可看出那些變更對效能的影響。 -如果前次變更批次的平均畫面率大於後來變更批次,您通常會有那項特定變更的整體 win wrt 效能。 - - -

    - -

    - 這表示您執行的任何自動化 UI 測試都應將此概念列入考量,同時考量可能會在測試期間發生的任何異常情況。 -例如,您的應用程式效能若因為某些裝置問題而突然下降 (並非由您的應用程式引起),您可能會想要重新執行批次,以讓取得的計時較不混亂。 - - - -

    - -

    - 應該執行多少次測試才能獲得有意義的測量結果呢?最少應執行 10 次,若執行更多次 (像是 50 或 100 次) 可以產生更準確的結果 (當然您現在是以時間換取準確度)。 - - -

    diff --git a/docs/html-intl/intl/zh-tw/sdk/index.jd b/docs/html-intl/intl/zh-tw/sdk/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..ba11c46cd7919a4469b683ab151df930ecd55cb3 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/sdk/index.jd @@ -0,0 +1,429 @@ +page.title=下載 Android Studio 和 SDK 工具 +page.tags=sdk, android studio +page.template=sdk +page.image=images/cards/android-studio_2x.png +header.hide=1 +page.metaDescription=下載官方 Android IDE 和開發人員工具以建置適用於 Android 手機、平板電腦、穿戴式裝置、電視等裝置的應用程式。 + +@jd:body + + + + + + + +
    + + + + + + + + + +
    + +
     
    + + + +
    + +

    Android Studio

    + +

    官方 Android IDE

    + +
      +
    • Android Studio IDE
    • +
    • Android SDK 工具
    • +
    • Android 6.0 (Marshmallow) 平台
    • +
    • 含 Google API 的 Android 6.0 模擬器系統映像檔
    • +
    + +Download Android Studio
    + + +

    +想要取得 Android Studio 或獨立 SDK 工具,請造訪 developer.android.com/sdk/ +

    +
    + + + + + + +

    智慧型程式碼編輯器

    + +
    + +
    + +
    +

    Android Studio 的核心是一個智慧型程式碼編輯器,能夠進行進階的程式碼自動完成、重構及程式碼分析作業。 +

    +

    這項功能強大的程式碼編輯器可協助您成為更具生產力的 Android 應用程式開發人員。

    +
    + + + + + +

    程式碼範本與 GitHub 整合

    + +
    + +
    + +
    +

    全新的專案精靈可協助您及早發起新專案。

    + +

    您可以使用模式 (例如導覽匣和資料檢視巡覽區) 範本程式碼,甚至是從 GitHub 匯入 Google 程式碼來發起專案。 +

    +
    + + + + +

    多螢幕應用程式開發

    + +
    + +
    + +
    +

    建置適用於 Android 手機、平板電腦、Android Wear、Android TV、Android Auto 和 Google Glass 的應用程式。 +

    +

    透過 Android Studio 中新的「Android 專案檢視」和模組支援功能,管理應用程式專案和相關資源變得更加容易。 + +

    + + + + +

    適用於所有形狀和大小的虛擬裝置

    + +
    + +
    + +
    +

    Android Studio 隨附預先設定、經過最佳化的模擬器映像檔。

    +

    經過更新及簡化的 Virtual Device Manager (虛擬裝置管理員) 可為常見的 Android 裝置提供預先定義的裝置設定檔。 +

    +
    + + + + +

    +Android 版本已隨 Gradle 更新

    + +
    + +
    + +
    +

    可使用同一項專案為您的 Android 應用程式建立多個具有不同功能的 APK。

    +

    可使用 Maven 管理應用程式依附功能。

    +

    可使用 Android Studio 或命令列建置 APK。

    +
    + + + + +

    瞭解 Android Studio

    +
    + +Download + +
      +
    • 以 IntelliJ IDEA Community Edition (JetBrains 所推出的常用 Java IDE) 為基礎。
    • +
    • 具有彈性的 Gradle 型建置系統。
    • +
    • 可產生建置變體和多個 APK。
    • +
    • 可為 Google 各項服務和各種裝置類型提供額外的範本支援。
    • +
    • 具有支援主題編輯功能的版面配置編輯器。
    • +
    • 具有可取得效能、可用性、版本相容性資料及偵測其他問題的 Lint 工具。
    • +
    • 具有 ProGuard 和應用程式簽署功能。
    • +
    • 內建 Google Cloud Platform 支援,可讓您輕鬆整合 Google Cloud Messaging 與 App Engine。 +
    • +
    + +

    +如要進一步瞭解 Android Studio 提供的功能,請參閱《Android Studio 基本概念》指南。 +

    +
    + + +

    如果您搭配 ADT 使用 Eclipse,請注意,Android Studio 現已成為 Android 的官方 IDE,因此請遷移至 Android Studio 以接收 IDE 的所有更新資訊。 + +如果想瞭解如何遷移專案,請參閱遷移至 Android Studio。 + +

    + + + + + + + +

    系統需求

    + +

    Windows

    + +
      +
    • Microsoft® Windows® 8/7/Vista/2003 (32 或 64 位元)
    • +
    • 2 GB 以上的 RAM (建議準備 4 GB 的 RAM)
    • +
    • 400 MB 的硬碟空間
    • +
    • 至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)
    • +
    • 1280 x 800 以上的螢幕解析度
    • +
    • Java Development Kit (JDK) 7
    • +
    • 提升模擬器效能的選用配件:支援 Intel® VT-x、Intel® EM64T (Intel® 64) 及 Execute Disable (XD) Bit 功能的 Intel® 處理器 +
    • +
    + + +

    Mac OS X

    + +
      +
    • Mac® OS X® 10.8.5 以上版本;最高可支援 10.9 (Mavericks)
    • +
    • 2 GB 以上的 RAM (建議準備 4 GB 的 RAM)
    • +
    • 400 MB 的硬碟空間
    • +
    • 至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)
    • +
    • 1280 x 800 以上的螢幕解析度
    • +
    • Java Runtime Environment (JRE) 6
    • +
    • Java Development Kit (JDK) 7
    • +
    • 提升模擬器效能的選用配件:支援 Intel® VT-x、Intel® EM64T (Intel® 64) 及 Execute Disable (XD) Bit 功能的 Intel® 處理器 +
    • +
    + +

    如果您使用 Mac OS,可搭配 Java Runtime Environment (JRE) 6 執行 Android Studio 以獲得最佳字型顯示效果。 +此外,您還可以設定專案使用 Java Development Kit (JDK) 6 或 JDK 7。

    + + + +

    Linux

    + +
      +
    • GNOME 或 KDE 桌面
    • +
    • GNU C 程式庫 (glibc) 2.15 以上版本
    • +
    • 2 GB 以上的 RAM (建議準備 4 GB 的 RAM)
    • +
    • 400 MB 的硬碟空間
    • +
    • 至少 1 GB (供 Android SDK、模擬器系統映像檔及快取使用)
    • +
    • 1280 x 800 以上的螢幕解析度
    • +
    • Oracle® Java Development Kit (JDK) 7
    • +
    +

    已在 Ubuntu® 14.04 Trusty Tahr (能夠執行 32 位元應用程式的 64 位元發行版本) 上經過測試。 +

    + + + + +

    其他下載選項

    + + diff --git a/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd b/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd new file mode 100644 index 0000000000000000000000000000000000000000..563df51e22d335a7d8095f3ed1b01598d057dbcf --- /dev/null +++ b/docs/html-intl/intl/zh-tw/sdk/installing/adding-packages.jd @@ -0,0 +1,226 @@ +page.title=新增 SDK 封裝 + +page.tags=sdk manager + +@jd:body + + + + +

    +Android SDK 預設不會包括開發所需的所有內容。SDK 將工具、平台及其他元件劃分為不同的封裝。您可以使用 + Android SDK Manager 視需要下載。因此,您要先將封裝加入 Android SDK 後,才可以開始進行。 + + +

    + +

    如要新增封裝,請透過下列其中一種方式啟動 Android SDK Manager:

    +
      +
    • 在 Android Studio 中,按一下工具列的 [SDK Manager] +
    • +
    • 如果您不是使用 Android Studio: +
        +
      • Windows:按兩下位於 Android SDK 目錄之根目錄的 SDK Manager.exe 檔案。 +
      • +
      • Mac/Linux:開啟終端機並瀏覽到安裝 Android SDK 的 tools/ 目錄,然後執行 android sdk。 +
      • +
      +
    • +
    + +

    第一次開啟 SDK Manager 時,預設會選取多個封裝。 +保留預設選取的封裝,但務必按照以下步驟確認您已經取得開發所需的所有內容: +

    + + +
      +
    1. +

      取得最新的 SDK 工具

      + + + +

      設定 Android SDK 時,您一定要下載最新的工具和平台: +

      +
        +
      1. 開啟 Tools 目錄並選取: +
          +
        • Android SDK 工具
        • +
        • Android SDK 平台工具
        • +
        • Android SDK 建置工具 (最新版本)
        • +
        +
      2. +
      3. 開啟第一個 Android X.X 資料夾 (最新版本) 並選取: +
          +
        • SDK 平台
        • +
        • 模擬器的系統映像,例如
          + ARM EABI v7a 系統映像
        • +
        +
      4. +
      +
    2. + +
    3. +

      取得額外 API 的支援程式庫

      + + + +

      Android 支援程式庫提供的 API 延伸集合,可以與大部分的 Android 版本相容。 +

      + +

      開啟 Extras 目錄並選取:

      +
        +
      • Android 支援存放庫
      • +
      • Android 支援程式庫
      • +
      + +

       

      +

       

      + +
    4. + + +
    5. +

      從 Google Play 服務取得更多 API

      + + + +

      如要使用 Google API 進行開發,您需要 Google Play 服務封裝:

      +

      開啟 Extras 目錄並選取:

      +
        +
      • Google 存放庫
      • +
      • Google Play 服務
      • +
      + +

      注意:並非所有 Android 裝置都提供 Google Play 服務,但內含 Google Play 市集的所有裝置都可以使用 Google Play 服務。 +如要在 Android 模擬器中使用這些 API,您必須從 SDK Manager 最新的 Android X.X 目錄中安裝 Google API 系統映像。 + +

      +
    6. + + +
    7. +

      安裝封裝

      +

      您選好需要的所有封裝後,就可以繼續安裝:

      +
        +
      1. 按一下 [安裝 X 封裝]。
      2. +
      3. 在下一個視窗中,按兩下左側的每個封裝名稱,以接受每項的授權合約。 +
      4. +
      5. 按一下 [安裝]。
      6. +
      +

      SDK Manager 視窗的下方會顯示下載進度。 + 請勿結束 SDK Manager,下載會因而取消。

      +
    8. + +
    9. +

      開始建置!

      + +

      Android SDK 現在已內含上述封裝,您可以開始建置 Android 應用程式了。 +若有新的工具和其他 API,只要啟動 SDK Manager 即可將新的封裝下載到 SDK。 +

      + +

      以下是繼續進行的選項:

      + +
      +
      +

      開始使用

      +

      如果您是 Android 開發新手,可參考建置第一個應用程式指南瞭解 Android 應用程式的基本概念。 +

      + +
      +
      +

      建置穿戴式應用程式

      +

      如果您準備好開始建置 Android 穿戴式裝置的應用程式,請參閱建置 Android Wear 的應用程式指南。 +

      + +
      +
      +

      使用 Google API

      +

      如要開始使用 Google API (例如「地圖」或 Play Game 服務),請參閱設定 Google Play 服務指南。 + + +

      + +
      +
      + + +
    10. + +
    + + diff --git a/docs/html-intl/intl/zh-tw/training/material/animations.jd b/docs/html-intl/intl/zh-tw/training/material/animations.jd new file mode 100644 index 0000000000000000000000000000000000000000..ba285751bcdf27e085359e278e83aae906eb3675 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/animations.jd @@ -0,0 +1,550 @@ +page.title=定義自訂動畫 + +@jd:body + + + + +

    材料設計中的動畫讓使用者在進行操作動作之後獲得回饋,並且在使用者與您的應用程式互動時提供視覺上的連續性。 +材料設計風格針對按鈕和操作行為轉換,提供某些預設的動畫,而 Android 5.0 (API 級別 21) 以上版本則可讓您自訂這些動畫並建立新的動畫: + +

    + +
      +
    • 輕觸回饋
    • +
    • 循環顯示
    • +
    • 操作行為轉換
    • +
    • 曲線動作
    • +
    • 視圖狀態變更
    • +
    + + +

    自訂輕觸回饋

    + +

    當使用者與 UI 元素互動時,材料設計中的輕觸回饋在接觸點提供即時的視覺化確認。 +按鈕的預設輕觸回饋動畫使用新的 {@link android.graphics.drawable.RippleDrawable} 類別,透過漣漪效果在不同狀態之間進行轉換。 + +

    + +

    大多數情況下,您應該在視圖 XML 中將視圖背景指定為下列項目以套用此功能: +

    + +
      +
    • 對有邊界的漣漪指定為 ?android:attr/selectableItemBackground
    • +
    • 對延伸出視圖的漣漪指定為 ?android:attr/selectableItemBackgroundBorderless。 +這會繪製在具有非空背景的視圖之最接近父項上,並以其作為邊界。 +
    • +
    + +

    注意:selectableItemBackgroundBorderless 是 API 級別 21 推出的新屬性。 +

    + + +

    或者,您也可以使用 ripple 元素將 {@link android.graphics.drawable.RippleDrawable} 定義為 XML 資源。 +

    + +

    您可以對 {@link android.graphics.drawable.RippleDrawable} 物件指定色彩。如要變更預設的輕觸回饋色彩,請使用設計風格的 android:colorControlHighlight 屬性。 + +

    + +

    如需詳細資訊,請參閱 {@link +android.graphics.drawable.RippleDrawable} 類別的 API 參考資料。

    + + +

    使用顯示效果

    + +

    當您顯示或隱藏一組 UI 元素時,顯示動畫可提供使用者視覺上的連續性。 +{@link android.view.ViewAnimationUtils#createCircularReveal +ViewAnimationUtils.createCircularReveal()}方法可讓您以動畫顯示裁剪的圓形,以顯示或隱藏視圖。 +

    + +

    使用下列效果顯示之前看不見的視圖:

    + +
    +// previously invisible view
    +View myView = findViewById(R.id.my_view);
    +
    +// get the center for the clipping circle
    +int cx = (myView.getLeft() + myView.getRight()) / 2;
    +int cy = (myView.getTop() + myView.getBottom()) / 2;
    +
    +// get the final radius for the clipping circle
    +int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
    +
    +// create the animator for this view (the start radius is zero)
    +Animator anim =
    +    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
    +
    +// make the view visible and start the animation
    +myView.setVisibility(View.VISIBLE);
    +anim.start();
    +
    + +

    使用下列效果隱藏之前看見的視圖:

    + +
    +// previously visible view
    +final View myView = findViewById(R.id.my_view);
    +
    +// get the center for the clipping circle
    +int cx = (myView.getLeft() + myView.getRight()) / 2;
    +int cy = (myView.getTop() + myView.getBottom()) / 2;
    +
    +// get the initial radius for the clipping circle
    +int initialRadius = myView.getWidth();
    +
    +// create the animation (the final radius is zero)
    +Animator anim =
    +    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
    +
    +// make the view invisible when the animation is done
    +anim.addListener(new AnimatorListenerAdapter() {
    +    @Override
    +    public void onAnimationEnd(Animator animation) {
    +        super.onAnimationEnd(animation);
    +        myView.setVisibility(View.INVISIBLE);
    +    }
    +});
    +
    +// start the animation
    +anim.start();
    +
    + + +

    自訂操作行為轉換

    + + +
    +
    + +
    +
    +

    圖 1 - 使用分享元素的轉換。 +

    + 如要重播影片,請按一下裝置螢幕 +
    +
    + +

    材料設計應用程式中的操作行為轉換透過常見元素之間的動作和轉換,在不同的狀態之間提供視覺上的連接效果。 +您可針對進入和離開轉換及不同操作行為之間分享元素的轉換指定自訂動畫。 +

    + +
      +
    • 進入轉換決定操作行為中的視圖如何進入場景。例如,在爆炸進入轉換中,視圖會從外面進入場景,接著飛入螢幕的中央。 + +
    • + +
    • 離開轉換決定操作行為中的視圖如何離開場景。例如,在爆炸離開轉換中,視圖會從中央離開場景。 + +
    • + +
    • 分享元素轉換決定在兩個操作行為轉換之間分享的視圖如何在這些操作行為之間轉換。 +例如,若兩個操作行為在不同的位置和大小具有相同的影像,changeImageTransform 分享元素轉換會在這些操作行為之間流暢地解譯影像以及調整影像的大小。 + +
    • +
    + +

    Android 5.0 (API 級別 21) 支援下列進入和離開轉換:

    + +
      +
    • 爆炸 - 視圖從場景的中央移入和移出。
    • +
    • 滑動 - 視圖從場景的其中一邊移入和移出。
    • +
    • 淡化 - 改變視圖的透明度,以將視圖加入場景,或從場景中移除。
    • +
    + +

    所有延伸 {@link android.transition.Visibility} 類別的轉換都可作為進入或離開轉換。 +如需詳細資訊,請參閱 {@link android.transition.Transition} 類別的 API 參考資料。 +

    + +

    Android 5.0 (API 級別 21) 也支援下列分享元素轉換:

    + +
      +
    • changeBounds - 動畫顯示目標視圖版面配置邊界的變更。
    • +
    • changeClipBounds - 動畫顯示目標視圖裁剪邊界的變更。
    • +
    • changeTransform - 動畫顯示目標視圖比例和旋轉方向的變更。
    • +
    • changeImageTransform - 動畫顯示目標影像大小和比例的變更。
    • +
    + +

    當您在應用程式中啟用操作行為轉換時,進入和離開操作行為之間會啟動預設交錯淡化轉換。 +

    + + +

    圖 2 - 使用一個分享元素的場景轉換。 +

    + +

    指定自訂轉換

    + +

    首先,當您定義一個從材料設計風格繼承而來的樣式時,請使用 android:windowContentTransitions 屬性啟用視窗內容轉換。 +您也可在樣式定義中指定進入、離開和分享元素轉換: +

    + +
    +<style name="BaseAppTheme" parent="android:Theme.Material">
    +  <!-- enable window content transitions -->
    +  <item name="android:windowContentTransitions">true</item>
    +
    +  <!-- specify enter and exit transitions -->
    +  <item name="android:windowEnterTransition">@transition/explode</item>
    +  <item name="android:windowExitTransition">@transition/explode</item>
    +
    +  <!-- specify shared element transitions -->
    +  <item name="android:windowSharedElementEnterTransition">
    +    @transition/change_image_transform</item>
    +  <item name="android:windowSharedElementExitTransition">
    +    @transition/change_image_transform</item>
    +</style>
    +
    + +

    此範例中的 change_image_transform 轉換定義如下:

    + +
    +<!-- res/transition/change_image_transform.xml -->
    +<!-- (see also Shared Transitions below) -->
    +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    +  <changeImageTransform/>
    +</transitionSet>
    +
    + +

    changeImageTransform 元素對應至 {@link android.transition.ChangeImageTransform} 類別。 +如需詳細資訊,請參閱 {@link android.transition.Transition} 的 API 參考資料。 +

    + +

    如要在程式碼中改為啟用視窗內容轉換,請呼叫 {@link android.view.Window#requestFeature Window.requestFeature()}方法: +

    + +
    +// inside your activity (if you did not enable transitions in your theme)
    +getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    +
    +// set an exit transition
    +getWindow().setExitTransition(new Explode());
    +
    + +

    如要在程式碼中指定轉換,請以 {@link +android.transition.Transition} 物件呼叫下列方法:

    + +
      +
    • {@link android.view.Window#setEnterTransition Window.setEnterTransition()}
    • +
    • {@link android.view.Window#setExitTransition Window.setExitTransition()}
    • +
    • {@link android.view.Window#setSharedElementEnterTransition + Window.setSharedElementEnterTransition()}
    • +
    • {@link android.view.Window#setSharedElementExitTransition + Window.setSharedElementExitTransition()}
    • +
    + +

    {@link android.view.Window#setExitTransition setExitTransition()} 和 {@link +android.view.Window#setSharedElementExitTransition setSharedElementExitTransition()} 方法定義了呼叫操作行為的離開轉換。 +{@link android.view.Window#setEnterTransition +setEnterTransition()} 和 {@link android.view.Window#setSharedElementEnterTransition +setSharedElementEnterTransition()} 方法定義被呼叫操作行為的進入轉換。

    + +

    如要取得轉換的完整效果,您必須同時對呼叫與被呼叫操作行為啟用視窗內容轉換。 +否則,呼叫操作行為會啟動離開轉換,然後您只會看到視窗轉換 (類似調整大小或淡化)。 +

    + +

    如要立即啟動進入轉換,請對被呼叫的操作行為使用 {@link android.view.Window#setAllowEnterTransitionOverlap Window.setAllowEnterTransitionOverlap()} 方法。 + +這樣您就會有更戲劇性的進入轉換。

    + +

    使用轉換啟動操作行為

    + +

    若您啟用轉換並對操作行為設定離開轉換,當您啟動另一個操作行為時,就會啟動轉換,如下所示: +

    + +
    +startActivity(intent,
    +              ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
    +
    + +

    若您已針對第二個操作行為設定進入轉換,當操作行為啟動時,也會啟動轉換。 +若要在啟動另一個操作行為時停用轉換,請提供 null 選項組合。 +

    + +

    使用一個分享元素啟動操作行為

    + +

    在兩個擁有分享元素的操作行為之間製作螢幕轉換動畫:

    + +
      +
    1. 在設計風格中啟用視窗內容轉換。
    2. +
    3. 在樣式中指定分享元素轉換。
    4. +
    5. 將您的轉換定義為 XML 資源。
    6. +
    7. 使用 android:transitionName 屬性,對兩個版面配置中的分享元素指定通用名稱。 +
    8. +
    9. 使用 {@link android.app.ActivityOptions#makeSceneTransitionAnimation +ActivityOptions.makeSceneTransitionAnimation()} 方法。
    10. +
    + +
    +// get the element that receives the click event
    +final View imgContainerView = findViewById(R.id.img_container);
    +
    +// get the common element for the transition in this activity
    +final View androidRobotView = findViewById(R.id.image_small);
    +
    +// define a click listener
    +imgContainerView.setOnClickListener(new View.OnClickListener() {
    +    @Override
    +    public void onClick(View view) {
    +        Intent intent = new Intent(this, Activity2.class);
    +        // create the transition animation - the images in the layouts
    +        // of both activities are defined with android:transitionName="robot"
    +        ActivityOptions options = ActivityOptions
    +            .makeSceneTransitionAnimation(this, androidRobotView, "robot");
    +        // start the new activity
    +        startActivity(intent, options.toBundle());
    +    }
    +});
    +
    + +

    對於您在程式碼中所產生的分享動態視圖,請使用 {@link android.view.View#setTransitionName View.setTransitionName()} 方法,在兩個操作行為中指定通用元素名稱。 + +

    + +

    如要在完成第二個操作行為時倒轉場景轉換動畫,請呼叫 {@link android.app.Activity#finishAfterTransition Activity.finishAfterTransition()} 方法而不是 {@link android.app.Activity#finish Activity.finish()}。 + +

    + +

    使用多個分享元素啟動操作行為

    + +

    如要在兩個具有多個分享元素的操作行為之間製作場景轉換動畫,請使用 android:transitionName 屬性在兩個版面配置中定義分享元素 (或在兩個操作行為中使用 {@link android.view.View#setTransitionName View.setTransitionName()} 方法),然後建立 {@link android.app.ActivityOptions} 物件,如下所示: + + +

    + +
    +ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
    +        Pair.create(view1, "agreedName1"),
    +        Pair.create(view2, "agreedName2"));
    +
    + + +

    使用曲線動作

    + +

    材料設計中的動畫依據使用時間插值法和空間移動模式的曲線而定。 +在 Android 5.0 (API 級別 21) 及以上版本中,您可為動畫定義自訂時間曲線和曲線動作模式。 +

    + +

    {@link android.view.animation.PathInterpolator} 類別是根據貝茲曲線 (Bézier curve) 或 {@link android.graphics.Path} 物件的一種新插值器。 +這個插值器在一個 1x1 的方格中指定動作曲線,在 (0,0) 和 (1,1) 有兩個錨定點,以及使用建構函式引數指定的控制點。 + +您也可以將路徑插值器定義為 XML 資源:

    + +
    +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:controlX1="0.4"
    +    android:controlY1="0"
    +    android:controlX2="1"
    +    android:controlY2="1"/>
    +
    + +

    系統在材料設計規格中提供三種基本曲線的 XML 資源: +

    + +
      +
    • @interpolator/fast_out_linear_in.xml
    • +
    • @interpolator/fast_out_slow_in.xml
    • +
    • @interpolator/linear_out_slow_in.xml
    • +
    + +

    您可將 {@link android.view.animation.PathInterpolator} 物件傳送至 {@link +android.animation.Animator#setInterpolator Animator.setInterpolator()} 方法。

    + +

    {@link android.animation.ObjectAnimator} 類別有新的建構函式,可讓您一次使用兩個以上的屬性沿著路徑以動畫顯示座標。 +例如,下列動畫器使用 {@link android.graphics.Path} 物件,以動畫顯示視圖的 X 和 Y 屬性: +

    + +
    +ObjectAnimator mAnimator;
    +mAnimator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
    +...
    +mAnimator.start();
    +
    + + +

    動畫顯示視圖狀態變更

    + +

    {@link android.animation.StateListAnimator} 類別可讓您定義視圖狀態變更時所執行的動畫器。 +下列範例示範如何將 {@link +android.animation.StateListAnimator} 定義為 XML 資源:

    + +
    +<!-- animate the translationZ property of a view when pressed -->
    +<selector xmlns:android="http://schemas.android.com/apk/res/android">
    +  <item android:state_pressed="true">
    +    <set>
    +      <objectAnimator android:propertyName="translationZ"
    +        android:duration="@android:integer/config_shortAnimTime"
    +        android:valueTo="2dp"
    +        android:valueType="floatType"/>
    +        <!-- you could have other objectAnimator elements
    +             here for "x" and "y", or other properties -->
    +    </set>
    +  </item>
    +  <item android:state_enabled="true"
    +    android:state_pressed="false"
    +    android:state_focused="true">
    +    <set>
    +      <objectAnimator android:propertyName="translationZ"
    +        android:duration="100"
    +        android:valueTo="0"
    +        android:valueType="floatType"/>
    +    </set>
    +  </item>
    +</selector>
    +
    + +

    如要將自訂的視圖狀態動畫連接到視圖,請依照此範例在 XML 資源檔案中使用 selector 元素定義動畫器,並使用 android:stateListAnimator 屬性將其指派到您的視圖。 + +如要在程式碼中為視圖指派狀態清單動畫器,請使用 {@link android.animation.AnimatorInflater#loadStateListAnimator +AnimationInflater.loadStateListAnimator()} 方法,然後使用 {@link android.view.View#setStateListAnimator View.setStateListAnimator()} 方法將動畫器指派到您的視圖。 + +

    + +

    當您的設計風格延伸材料設計風格時,按鈕預設會有 Z 動畫。如果要避免在按鈕中出現這類行為,請將 android:stateListAnimator 屬性設定為 @null。 + +

    + +

    {@link android.graphics.drawable.AnimatedStateListDrawable} 類別可讓您在相關視圖的狀態變更之間,建立顯示動畫的可繪項目。 +Android 5.0 中的某些系統小工具預設會使用這些動畫。 +下列範例示範如何將 {@link android.graphics.drawable.AnimatedStateListDrawable} 定義為 XML 資源: +

    + +
    +<!-- res/drawable/myanimstatedrawable.xml -->
    +<animated-selector
    +    xmlns:android="http://schemas.android.com/apk/res/android">
    +
    +    <!-- provide a different drawable for each state-->
    +    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
    +        android:state_pressed="true"/>
    +    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
    +        android:state_focused="true"/>
    +    <item android:id="@id/default"
    +        android:drawable="@drawable/drawableD"/>
    +
    +    <!-- specify a transition -->
    +    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
    +        <animation-list>
    +            <item android:duration="15" android:drawable="@drawable/dt1"/>
    +            <item android:duration="15" android:drawable="@drawable/dt2"/>
    +            ...
    +        </animation-list>
    +    </transition>
    +    ...
    +</animated-selector>
    +
    + + +

    動畫顯示矢量可繪

    + +

    矢量可繪可以調整大小,但又不會喪失定義。 +{@link android.graphics.drawable.AnimatedVectorDrawable} 類別可讓您以動畫顯示矢量可繪的屬性。 +

    + +

    您通常會在下列三個 XML 檔案中定義能可動矢量可繪:

    + +
      +
    • res/drawable/ 中包含 <vector> 元素的矢量可繪 +
    • +
    • res/drawable/ 中包含 <animated-vector> 元素的可動矢量可繪 +
    • +
    • res/anim/ 中包含 <objectAnimator> 元素的一或多個物件動畫器 +
    • +
    + +

    可動的矢量可繪能以動畫顯示 <group><path> 元素的屬性。 +<group> 元素定義一組路徑或子群組,而 <path> 元素則定義要繪製的路徑。 +

    + +

    當您定義要可動的矢量可繪時,請使用 android:name 屬性為群組和路徑指派唯一的名稱,以便從您的動畫器定義參考這些群組和路徑。 + +例如:

    + +
    +<!-- res/drawable/vectordrawable.xml -->
    +<vector xmlns:android="http://schemas.android.com/apk/res/android"
    +    android:height="64dp"
    +    android:width="64dp"
    +    android:viewportHeight="600"
    +    android:viewportWidth="600">
    +    <group
    +        android:name="rotationGroup"
    +        android:pivotX="300.0"
    +        android:pivotY="300.0"
    +        android:rotation="45.0" >
    +        <path
    +            android:name="v"
    +            android:fillColor="#000000"
    +            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    +    </group>
    +</vector>
    +
    + +

    可動的矢量可繪定義是依照名稱來參考矢量可繪中的群組和路徑: +

    + +
    +<!-- res/drawable/animvectordrawable.xml -->
    +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    +  android:drawable="@drawable/vectordrawable" >
    +    <target
    +        android:name="rotationGroup"
    +        android:animation="@anim/rotation" />
    +    <target
    +        android:name="v"
    +        android:animation="@anim/path_morph" />
    +</animated-vector>
    +
    + +

    動畫定義代表 {@link android.animation.ObjectAnimator} 或 {@link +android.animation.AnimatorSet} 物件。此範例中的第一個動畫器會 360 度旋轉目標群組: +

    + +
    +<!-- res/anim/rotation.xml -->
    +<objectAnimator
    +    android:duration="6000"
    +    android:propertyName="rotation"
    +    android:valueFrom="0"
    +    android:valueTo="360" />
    +
    + +

    此範例中的第二個動畫器會將矢量可繪的路徑從一種形狀變成另外一種形狀。 +兩個路徑必須相容才能變形:意即兩個路徑必須有相同數量的命令,而每個命令必須有相同數量的參數。 +

    + +
    +<!-- res/anim/path_morph.xml -->
    +<set xmlns:android="http://schemas.android.com/apk/res/android">
    +    <objectAnimator
    +        android:duration="3000"
    +        android:propertyName="pathData"
    +        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
    +        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
    +        android:valueType="pathType" />
    +</set>
    +
    + +

    如需詳細資訊,請參閱 {@link +android.graphics.drawable.AnimatedVectorDrawable} 的 API 參考資料。

    diff --git a/docs/html-intl/intl/zh-tw/training/material/compatibility.jd b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd new file mode 100644 index 0000000000000000000000000000000000000000..767788bf75be5648b2884ec88e7b23cc3a2b46c2 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/compatibility.jd @@ -0,0 +1,168 @@ +page.title=維持相容性 + +@jd:body + + + + +

    某些材料設計功能如材料設計風格和自訂操作行為轉換等,只能在 Android 5.0 (API 級別 21) 及以上版本中使用。 +然而,您還是可以將應用程式設計為在支援材料設計的裝置上執行時才使用這些功能,並仍然與執行舊版 Android 的裝置相容。 + +

    + + +

    定義替代樣式

    + +

    您可以設定應用程式在支援材料設計風格的裝置上執行時才予以使用,在執行舊版 Android 的裝置上執行時則轉換成舊版的設計風格: +

    + +
      +
    1. res/values/styles.xml 中定義繼承自舊版設計風格 (例如 Holo) 的設計風格。 +
    2. +
    3. res/values-v21/styles.xml 中定義與材料設計風格相同名稱的設計風格。 +
    4. +
    5. 在宣示說明檔案中將此設計風格設定為應用程式的設計風格。
    6. +
    + +

    注意:若您的應用程式使用材料設計風格,但卻未以此方式提供替代設計風格,您的應用程式將無法在 Android 5.0 之前的版本上執行。 + + +

    + + +

    提供替代版面配置

    + +

    若您根據材料設計指南所設計的版面配置並未使用任何 Android 5.0 (API 級別 21) 中推出的全新 XML 屬性,則這些版面配置就可以在舊版 Android 上運作。 + +或者,您也可以提供替代的版面配置。您也可提供替代的版面配置以自訂應用程式在舊版 Android 上如何顯示。 +

    + +

    res/layout-v21/ 內建立 Android 5.0 (API 級別 21) 的版面配置檔案,並在 res/layout/ 內建立舊版 Android 的替代版面配置檔案。例如,res/layout/my_activity.xmlres/layout-v21/my_activity.xml 的替代版面配置。 + + +

    + +

    為了避免程式碼重複,請在 res/values/ 內定義您的樣式,在 res/values-v21/ 中針對新的 API 修改樣式,再使用樣式繼承,在 res/values/ 中定義基礎樣式,然後從 res/values-v21/ 中的樣式繼承。 + +

    + + +

    使用支援程式庫

    + +

    v7 支援程式庫 r21 以及更新版本包含下列材料設計功能: +

    + + + +

    系統小工具

    + +

    Theme.AppCompat 設計風格提供下列小工具的材料設計樣式:

    + +
      +
    • {@link android.widget.EditText}
    • +
    • {@link android.widget.Spinner}
    • +
    • {@link android.widget.CheckBox}
    • +
    • {@link android.widget.RadioButton}
    • +
    • {@link android.support.v7.widget.SwitchCompat}
    • +
    • {@link android.widget.CheckedTextView}
    • +
    + +

    色板

    + +

    如果要取得材料設計樣式並使用 Android v7 支援程式庫自訂色板,請套用其中一個 Theme.AppCompat 設計風格: +

    + +
    +<!-- extend one of the Theme.AppCompat themes -->
    +<style name="Theme.MyTheme" parent="Theme.AppCompat.Light">
    +    <!-- customize the color palette -->
    +    <item name="colorPrimary">@color/material_blue_500</item>
    +    <item name="colorPrimaryDark">@color/material_blue_700</item>
    +    <item name="colorAccent">@color/material_green_A200</item>
    +</style>
    +
    + +

    清單和卡片

    + +

    在舊版 Android 中,透過 Android v7 支援程式庫可以使用 {@link android.support.v7.widget.RecyclerView} 和 {@link +android.support.v7.widget.CardView} 小工具,但有下列限制: +

    +
      +
    • {@link android.support.v7.widget.CardView} 只能使用額外的填補方式,有計畫地實作陰影。 +
    • +
    • {@link android.support.v7.widget.CardView} 不會裁剪與圓形邊角重疊的下方視圖。 +
    • +
    + + +

    相依性

    + +

    如果要在 Android 5.0 (API 級別 21) 以前的版本中使用這些功能,請在您的專案中包含 Android v7 支援程式庫做為 Gradle 相依性: +

    + +
    +dependencies {
    +    compile 'com.android.support:appcompat-v7:21.0.+'
    +    compile 'com.android.support:cardview-v7:21.0.+'
    +    compile 'com.android.support:recyclerview-v7:21.0.+'
    +}
    +
    + + +

    檢查系統版本

    + +

    下列功能只能在 Android 5.0 (API 級別 21) 以及更新版本中使用:

    + +
      +
    • 操作行為轉換
    • +
    • 輕觸回饋
    • +
    • 顯示動畫
    • +
    • 路徑型動畫
    • +
    • 矢量可繪
    • +
    • 繪製著色
    • +
    + +

    如要維持與舊版 Android 的相容性,在呼叫這些功能的 API 之前,請先在執行期間檢查系統 {@link +android.os.Build.VERSION#SDK_INT version}: +

    + +
    +// Check if we're running on Android 5.0 or higher
    +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    +    // Call some material design APIs here
    +} else {
    +    // Implement this feature without material design
    +}
    +
    + +

    注意:如果要指定應用程式支援的 Android 版本,請在您的宣示說明檔案中使用 android:minSdkVersionandroid:targetSdkVersion 屬性。 + +如果要在 Android 5.0 中使用材料設計功能,請將 android:targetSdkVersion 屬性設定為 21。 +如需詳細資訊,請參閱 <uses-sdk> API 指南。 + +

    diff --git a/docs/html-intl/intl/zh-tw/training/material/drawables.jd b/docs/html-intl/intl/zh-tw/training/material/drawables.jd new file mode 100644 index 0000000000000000000000000000000000000000..ae807c0b18066ed60e6f8e0e85efebbaf14fc5a7 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/drawables.jd @@ -0,0 +1,126 @@ +page.title=使用可繪項目 + +@jd:body + + + +

    下列可繪項目功能可以幫助您在應用程式中運用材料設計:

    + +
      +
    • 繪製著色
    • +
    • 顯著顏色提取
    • +
    • 矢量可繪
    • +
    + +

    本課程示範如何在應用程式中使用這些功能。

    + + +

    為可繪項目資源著色

    + +

    若您使用 Android 5.0 (API 級別 21) 或以上版本,您可對點陣圖和定義為 Alpha 遮罩的九宮格影像著色。 +您可使用顏色資源或解析為顏色資源的設計風格屬性 (如 ?android:attr/colorPrimary 為圖片著色。 +這些資產您通常只會建立一次,然後自動著色以符合您的設計風格。 +

    + +

    您可使用 {@code setTint()} 方法對 {@link android.graphics.drawable.BitmapDrawable} 或 {@link +android.graphics.drawable.NinePatchDrawable} 物件套用著色。您也可以使用 android:tintandroid:tintMode 屬性,設定版面配置中的著色顏色和模式。 + +

    + + +

    從影像提取顯著顏色

    + +

    Android 支援程式庫 r21 和更新版本包含 {@link +android.support.v7.graphics.Palette} 類別,可讓您從影像提取出顯著的顏色。此類別會提取下列顯著顏色: +

    + +
      +
    • 鮮明
    • +
    • 鮮明 (深色)
    • +
    • 鮮明 (淺色)
    • +
    • 柔和
    • +
    • 柔和 (深色)
    • +
    • 柔和 (淺色)
    • +
    + +

    如果要提取這些顏色,請將 {@link android.graphics.Bitmap} 物件傳遞到載入影像之背景執行緒中的 {@link android.support.v7.graphics.Palette#generate Palette.generate()} 靜態方法。 + +如果您無法使用該執行緒,請改為呼叫 {@link android.support.v7.graphics.Palette#generateAsync Palette.generateAsync()} 方法並提供接聽程式。 + +

    + +

    您可使用 Palette 類別中的 getter 方法 (如 Palette.getVibrantColor 從影像擷取顯著顏色。 +

    + +

    如果要在專案中使用 {@link android.support.v7.graphics.Palette} 類別,請在應用程式的模組中加入下列 Gradle 相依性: + +

    + +
    +dependencies {
    +    ...
    +    compile 'com.android.support:palette-v7:21.0.0'
    +}
    +
    + +

    如需詳細資訊,請參閱 {@link android.support.v7.graphics.Palette} 類別的 API 參考資料。 +

    + + +

    建立矢量可繪

    + + + +
    +

    影片

    +

    Android Vector Graphics

    +
    +
    + +

    在 Android 5.0 (API 級別 21) 或以上版本中,您可定義矢量可繪,這種圖形可以調整大小但不會遺失定義。 +一個矢量影像只需要一個資產檔案,但點陣圖影像的每一種螢幕密度都需要一個資產檔案。 +如果要建立矢量影像,您必須在 <vector> XML 元素內定義圖形的詳細資料。 +

    + +

    下列範例定義一個具有心形圖案的矢量影像:

    + +
    +<!-- res/drawable/heart.xml -->
    +<vector xmlns:android="http://schemas.android.com/apk/res/android"
    +    <!-- intrinsic size of the drawable -->
    +    android:height="256dp"
    +    android:width="256dp"
    +    <!-- size of the virtual canvas -->
    +    android:viewportWidth="32"
    +    android:viewportHeight="32">
    +
    +  <!-- draw a path -->
    +  <path android:fillColor="#8fff"
    +      android:pathData="M20.5,9.5
    +                        c-1.955,0,-3.83,1.268,-4.5,3
    +                        c-0.67,-1.732,-2.547,-3,-4.5,-3
    +                        C8.957,9.5,7,11.432,7,14
    +                        c0,3.53,3.793,6.257,9,11.5
    +                        c5.207,-5.242,9,-7.97,9,-11.5
    +                        C25,11.432,23.043,9.5,20.5,9.5z" />
    +</vector>
    +
    + +

    在 Android 中,{@link android.graphics.drawable.VectorDrawable} 物件代表矢量影像。 +如需有關 pathData 語法的詳細資訊,請參閱 SVG 路徑參考資料。如需有關繪製矢量可繪屬性的詳細資訊,請參閱動畫顯示矢量可繪。 + +

    diff --git a/docs/html-intl/intl/zh-tw/training/material/get-started.jd b/docs/html-intl/intl/zh-tw/training/material/get-started.jd new file mode 100644 index 0000000000000000000000000000000000000000..dfa0074e17cefc6f873e25a4d6a645073f88b476 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/get-started.jd @@ -0,0 +1,171 @@ +page.title=開始使用 + +@jd:body + + + + +

    如果要使用材料設計建立應用程式:

    + +
      +
    1. + 請閱讀材料設計規格
    2. +
    3. + 對應用程式套用材料設計風格
    4. +
    5. + 依照材料設計指南建立版面配置
    6. +
    7. + 指定視圖的高度來投射出陰影。
    8. +
    9. + 使用系統小工具製作清單和卡片。
    10. +
    11. + 自訂應用程式中的動畫
    12. +
    + +

    維持向下相容性

    + +

    您可在應用程式中加入多種材料設計功能,同時維持與 Android 5.0 之前版本的相容性。 +如需詳細資訊,請參閱維持相容性。 +

    + +

    更新應用程式以納入材料設計功能

    + +

    如要更新現有應用程式以納入材料設計功能,請依照下列材料設計指南更新版面配置。 +同時確保加入深度、輕觸回饋和動畫。 +

    + +

    使用材料設計建立新應用程式

    + +

    如果您要使用材料設計功能建立新應用程式,材料設計指南可以提供一致性的設計架構。 +請依照下列指南,使用 Android 架構中的新功能來設計和開發應用程式。 +

    + + +

    套用材料設計風格

    + +

    如要在應用程式中套用材料設計風格,請指定一個繼承自 android:Theme.Material 的樣式: +

    + +
    +<!-- res/values/styles.xml -->
    +<resources>
    +  <!-- your theme inherits from the material theme -->
    +  <style name="AppTheme" parent="android:Theme.Material">
    +    <!-- theme customizations -->
    +  </style>
    +</resources>
    +
    + +

    材料設計風格提供已更新的系統小工具,可讓您設定系統小工具的色板和預設動畫,作為輕觸回饋與操作行為轉換。 +如需詳細資訊,請參閱使用材料設計風格。 +

    + + +

    設計版面配置

    + +

    除了套用和自訂材料設計風格之外,您的版面配置也必須遵守材料設計指南。 +當您設計版面配置時,請注意下列事項: +

    + +
      +
    • 基準線網格
    • +
    • 邊線
    • +
    • 間距
    • +
    • 輕觸目標大小
    • +
    • 版面配置結構
    • +
    + + +

    指定視圖中的高度

    + +

    檢視可以投射出陰影,因此檢視的高度值會決定陰影的大小及其繪製順序。 +如要設定視圖的高度,請在版面配置中使用 android:elevation 屬性: +

    + +
    +<TextView
    +    android:id="@+id/my_textview"
    +    android:layout_width="wrap_content"
    +    android:layout_height="wrap_content"
    +    android:text="@string/next"
    +    android:background="@color/white"
    +    android:elevation="5dp" />
    +
    + +

    您可以使用新的 translationZ 屬性建立動畫,來反映視圖高度的短暫變化。 +高度變化可以用來回應輕觸手勢。 + +

    + +

    如需詳細資訊,請參閱定義陰影和裁剪檢視。 +

    + + +

    建立清單和卡片

    + +

    {@link android.support.v7.widget.RecyclerView} 是一個更容易插入的 {@link +android.widget.ListView} 版本,可支援不同的版面配置類型並提供更好的效能。{@link android.support.v7.widget.CardView} 可讓您在不同的應用程式之間以一致的外觀顯示卡片內部的資訊。 + +下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.CardView}: +

    + +
    +<android.support.v7.widget.CardView
    +    android:id="@+id/card_view"
    +    android:layout_width="200dp"
    +    android:layout_height="200dp"
    +    card_view:cardCornerRadius="3dp">
    +    ...
    +</android.support.v7.widget.CardView>
    +
    + +

    如需詳細資訊,請參閱建立清單和卡片。 +

    + + +

    自訂動畫

    + +

    Android 5.0 (API 級別 21) 包含許多新的 API 以便在應用程式中建立自訂動畫。例如,您可以啟用操作行為轉換,並在操作行為內部定義離開轉換: + +

    + +
    +public class MyActivity extends Activity {
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        // enable transitions
    +        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
    +        setContentView(R.layout.activity_my);
    +    }
    +
    +    public void onSomeButtonClicked(View view) {
    +        getWindow().setExitTransition(new Explode());
    +        Intent intent = new Intent(this, MyOtherActivity.class);
    +        startActivity(intent,
    +                      ActivityOptions
    +                          .makeSceneTransitionAnimation(this).toBundle());
    +    }
    +}
    +
    + +

    當您從這個操作行為開始另一個操作行為時,就會啟動離開轉換。

    + +

    如需深入了解新的動畫 API,請參閱定義自訂動畫

    diff --git a/docs/html-intl/intl/zh-tw/training/material/index.jd b/docs/html-intl/intl/zh-tw/training/material/index.jd new file mode 100644 index 0000000000000000000000000000000000000000..5599d5936973c2c56d6c741e240616e1b875bb39 --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/index.jd @@ -0,0 +1,61 @@ +page.title=開發人員材料設計 +page.type=設計 +page.image=images/cards/material_2x.png +page.metaDescription=學習如何套用材料設計至您的應用程式。 + + +@jd:body + +
    +
    +

    相依性和先決條件

    +
      +
    • Android 5.0 (API 級別 21)
    • +
    +
    +
    + +

    材料設計是一份廣泛綜合性的指南,引導您跨平台、跨裝置進行視覺、動態和互動的設計。 +如要在 Android 應用程式中使用材料設計,請依照材料設計規格中的指示,使用 Android 5.0 (API 級別 21) 中的新元件和新功能。 + + + +

    + +

    本課程示範如何使用下列元素建立材料設計應用程式:

    + +
      +
    • 材料設計風格
    • +
    • 卡片和清單的小工具
    • +
    • 自訂陰影和裁剪視圖
    • +
    • 矢量可繪
    • +
    • 自訂動畫
    • +
    + +

    本課程也示範當您在應用程式中使用材料設計功能時,如何與 Android 5.0 (API 級別 21) 之前的版本維持相容性。 +

    + +

    課程

    + +
    +
    開始使用
    +
    了解如何更新應用程式以納入材料設計功能。
    + +
    使用材料設計風格
    +
    了解如何對應用程式套用材料設計樣式。
    + +
    建立清單和卡片
    +
    了解如何使用系統小工具,建立外觀和操作方式一致的清單和卡片。
    + +
    定義陰影和裁剪視圖
    +
    了解如何對視圖設定高度建立自訂陰影,以及如何裁剪視圖。
    + +
    使用可繪項目
    +
    了解如何建立矢量可繪項目,以及如何對可繪資源著色。
    + +
    定義自訂動畫
    +
    了解如何使用分享元素對視圖建立自訂動畫和操作行為轉換。
    + +
    維持相容性
    +
    了解如何與 Android 5.0 之前的平台版本維持相容性。
    +
    diff --git a/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd b/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd new file mode 100644 index 0000000000000000000000000000000000000000..71adffbd4c82c727e0fc98a0166fdcb433b8e4bc --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/lists-cards.jd @@ -0,0 +1,266 @@ +page.title=建立清單和卡片 + +@jd:body + +
    +
    +

    本課程示範

    +
      +
    1. 建立清單
    2. +
    3. 建立卡片
    4. +
    5. 新增相依性
    6. +
    +

    您也應該閱讀

    + +
    +
    + + +

    如要在應用程式中使用材料設計樣式建立複雜的清單和卡片,可以使用 {@link android.support.v7.widget.RecyclerView} 和 {@link android.support.v7.widget.CardView} 小工具。 + +

    + + +

    建立清單

    + +

    {@link android.support.v7.widget.RecyclerView} 小工具是比較進階和彈性的 {@link android.widget.ListView} 版本。 +這個小工具是一個用來顯示大型資料集的容器,只要維護少數幾個視圖,就可極有效率地捲動資料集。 +當您的資料集元素在執行階段會根據使用者操作動作或網路事件而變更,請使用 {@link android.support.v7.widget.RecyclerView} 小工具。 + +

    + +

    {@link android.support.v7.widget.RecyclerView} 類別會提供下列項目,簡化大型資料集的顯示和處理方式: +

    + +
      +
    • 版面配置管理員,用來將項目定位
    • +
    • 常見項目操作 (例如移除或新增項目) 的預設動畫
    • +
    + +

    您也可針對 {@link +android.support.v7.widget.RecyclerView} 小工具定義自訂的版面配置管理員。

    + + +

    +圖 1.RecyclerView 小工具。 +

    + +

    如果要使用 {@link android.support.v7.widget.RecyclerView} 小工具,您必須指定配接器和版面配置管理員。 +如要建立配接器,請延伸 {@link +android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter} 類別。實作的詳細情形根據您的資料集和檢視類型而定。 +如需詳細資訊,請參閱下面的範例。 +

    + +
    + +

    +圖 2 - 含有 RecyclerView 的清單。 +

    +
    + +

    版面配置管理員會將 {@link +android.support.v7.widget.RecyclerView} 內的項目視圖定位,並判斷何時要重複使用使用者不會再看到的項目視圖。 +如要重複使用 (或回收) 某個視圖,版面配置管理員會要求配接器以資料集中不同的元素,取代視圖的內容。 +使用此方式回收視圖可避免建立不必要的視圖或執行耗費資源的 {@link android.app.Activity#findViewById findViewById()} 查詢,以增進效能。 + +

    + +

    {@link android.support.v7.widget.RecyclerView} 提供下列內建的版面配置管理員:

    + +
      +
    • {@link android.support.v7.widget.LinearLayoutManager} 在垂直或水平捲動清單中顯示項目。 +
    • +
    • {@link android.support.v7.widget.GridLayoutManager} 會在網格中顯示項目。
    • +
    • {@link android.support.v7.widget.StaggeredGridLayoutManager} 會在交錯網格中顯示項目。
    • +
    + +

    如要建立自訂版面配置管理員,請延伸 {@link +android.support.v7.widget.RecyclerView.LayoutManager RecyclerView.LayoutManager} 類別。

    + +

    動畫

    + +

    在 {@link +android.support.v7.widget.RecyclerView} 中,預設已啟用新增和移除項目的動畫。如果要自訂這些動畫,請延伸 {@link android.support.v7.widget.RecyclerView.ItemAnimator RecyclerView.ItemAnimator} 類別並使用 {@link android.support.v7.widget.RecyclerView#setItemAnimator RecyclerView.setItemAnimator()} 方法。 + + +

    + +

    範例

    + +

    下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.RecyclerView}: +

    + +
    +<!-- A RecyclerView with some commonly used attributes -->
    +<android.support.v7.widget.RecyclerView
    +    android:id="@+id/my_recycler_view"
    +    android:scrollbars="vertical"
    +    android:layout_width="match_parent"
    +    android:layout_height="match_parent"/>
    +
    + +

    一旦您在版面配置中加入 {@link android.support.v7.widget.RecyclerView} 小工具之後,先取得該物件的控制代碼、將控制代碼連線到版面配置管理員,再連接配接器以便顯示資料: + +

    + +
    +public class MyActivity extends Activity {
    +    private RecyclerView mRecyclerView;
    +    private RecyclerView.Adapter mAdapter;
    +    private RecyclerView.LayoutManager mLayoutManager;
    +
    +    @Override
    +    protected void onCreate(Bundle savedInstanceState) {
    +        super.onCreate(savedInstanceState);
    +        setContentView(R.layout.my_activity);
    +        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
    +
    +        // use this setting to improve performance if you know that changes
    +        // in content do not change the layout size of the RecyclerView
    +        mRecyclerView.setHasFixedSize(true);
    +
    +        // use a linear layout manager
    +        mLayoutManager = new LinearLayoutManager(this);
    +        mRecyclerView.setLayoutManager(mLayoutManager);
    +
    +        // specify an adapter (see also next example)
    +        mAdapter = new MyAdapter(myDataset);
    +        mRecyclerView.setAdapter(mAdapter);
    +    }
    +    ...
    +}
    +
    + +

    配接器可存取資料集中的項目、建立項目的視圖,並在原來的項目無法再顯示時,使用新的資料項目取代某些視圖的內容。 + +下列程式碼範例簡單實作一個由使用 {@link android.widget.TextView} 小工具顯示的字串陣列所組成的資料集: +

    + +
    +public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    +    private String[] mDataset;
    +
    +    // Provide a reference to the views for each data item
    +    // Complex data items may need more than one view per item, and
    +    // you provide access to all the views for a data item in a view holder
    +    public static class ViewHolder extends RecyclerView.ViewHolder {
    +        // each data item is just a string in this case
    +        public TextView mTextView;
    +        public ViewHolder(TextView v) {
    +            super(v);
    +            mTextView = v;
    +        }
    +    }
    +
    +    // Provide a suitable constructor (depends on the kind of dataset)
    +    public MyAdapter(String[] myDataset) {
    +        mDataset = myDataset;
    +    }
    +
    +    // Create new views (invoked by the layout manager)
    +    @Override
    +    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
    +                                                   int viewType) {
    +        // create a new view
    +        View v = LayoutInflater.from(parent.getContext())
    +                               .inflate(R.layout.my_text_view, parent, false);
    +        // set the view's size, margins, paddings and layout parameters
    +        ...
    +        ViewHolder vh = new ViewHolder(v);
    +        return vh;
    +    }
    +
    +    // Replace the contents of a view (invoked by the layout manager)
    +    @Override
    +    public void onBindViewHolder(ViewHolder holder, int position) {
    +        // - get element from your dataset at this position
    +        // - replace the contents of the view with that element
    +        holder.mTextView.setText(mDataset[position]);
    +
    +    }
    +
    +    // Return the size of your dataset (invoked by the layout manager)
    +    @Override
    +    public int getItemCount() {
    +        return mDataset.length;
    +    }
    +}
    +
    + + +
    + +

    +圖 3.卡片範例。 +

    +
    + +

    建立卡片

    + +

    {@link android.support.v7.widget.CardView} 會延伸{@link android.widget.FrameLayout} 類別,讓您跨平台以一致的外觀顯示卡片內部的資訊。{@link +android.support.v7.widget.CardView} 小工具可以有陰影和圓形邊角。 +

    + +

    如要建立有陰影的卡片,請使用 card_view:cardElevation 屬性。在 Android 5.0 (API 級別 21) 以及以上版本,{@link android.support.v7.widget.CardView} 使用實際高度和動態陰影,但在較舊的版本上只能有計畫地實作陰影。如需詳細資訊,請參閱維持相容性。 + + + +

    + +

    使用下列屬性可自訂 {@link android.support.v7.widget.CardView} 小工具的外觀: +

    + +
      +
    • 如果要在版面配置中設定圓角的半徑,請使用 card_view:cardCornerRadius 屬性。 +
    • +
    • 如果要在程式碼中設定圓角的半徑,請使用 CardView.setRadius 方法。
    • +
    • 如果要設定卡片的背景顏色,請使用 card_view:cardBackgroundColor 屬性。 +
    • +
    + +

    下列程式碼範例示範如何在版面配置中加入 {@link android.support.v7.widget.CardView}小工具: +

    + +
    +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    +    xmlns:tools="http://schemas.android.com/tools"
    +    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    +    ... >
    +    <!-- A CardView that contains a TextView -->
    +    <android.support.v7.widget.CardView
    +        xmlns:card_view="http://schemas.android.com/apk/res-auto"
    +        android:id="@+id/card_view"
    +        android:layout_gravity="center"
    +        android:layout_width="200dp"
    +        android:layout_height="200dp"
    +        card_view:cardCornerRadius="4dp">
    +
    +        <TextView
    +            android:id="@+id/info_text"
    +            android:layout_width="match_parent"
    +            android:layout_height="match_parent" />
    +    </android.support.v7.widget.CardView>
    +</LinearLayout>
    +
    + +

    如需詳細資訊,請參閱 {@link android.support.v7.widget.CardView} 的 API 參考資料。

    + + +

    新增相依性

    + +

    {@link android.support.v7.widget.RecyclerView} 和 {@link android.support.v7.widget.CardView} 小工具都屬於 v7 支援程式庫。 + +如要在專案中使用這些小工具,請在應用程式的模組中加入下列 Gradle 相依性: + +

    + +
    +dependencies {
    +    ...
    +    compile 'com.android.support:cardview-v7:21.0.+'
    +    compile 'com.android.support:recyclerview-v7:21.0.+'
    +}
    +
    diff --git a/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd b/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd new file mode 100644 index 0000000000000000000000000000000000000000..d8c996d0255b6ef0ce9300ae74e62effcbe7d6dd --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/shadows-clipping.jd @@ -0,0 +1,133 @@ +page.title=定義陰影和裁剪視圖 + +@jd:body + + + +

    材料設計為 UI 元素引入高度的概念。高度有助使用者了解每個元素的相對重要性並將焦點放在手邊的工作上。 +

    + +

    視圖的高度以 Z 屬性表示,決定其陰影的視覺外觀:視圖的 Z 值越高,投射範圍越大,陰影也就越柔和。 +Z 值較高的視圖會遮住 Z 值較低的視圖。然而,視圖的 Z 值不會影響視圖的大小。 +

    + +

    陰影是由較高的上層視圖繪製,因此也和標準視圖裁剪一樣,預設由上層視圖裁剪陰影。 +

    + +

    當小工具在執行某些操作動作而暫時升高到視圖平面的上方時,高度對於建立動畫也非常實用。 +

    + +

    如需有關材料設計高度的詳細資訊,請參閱 3D 空間中的物件。 + +

    + + +

    指定視圖的高度

    + +

    視圖的 Z 值有兩個部分: + +

      +
    • 高度:靜態部分。
    • +
    • 解譯:用在動畫的動態部分。
    • +
    + +

    Z = elevation + translationZ

    + + +

    圖 1 - 不同視圖高度的陰影。

    + +

    如要在版面配置定義中設定視圖的高度,請使用 android:elevation 屬性。 +如果要在操作行為的程式碼中設定檢視的高度,請使用 {@link android.view.View#setElevation View.setElevation()} 方法。 +

    + +

    如要在操作行為的程式碼中設定檢視的高度,請使用 {@link android.view.View#setTranslationZ +View.setTranslationZ()} 方法。

    + +

    新的 {@link android.view.ViewPropertyAnimator#z ViewPropertyAnimator.z()} 和 {@link +android.view.ViewPropertyAnimator#translationZ ViewPropertyAnimator.translationZ()} 方法可以很輕易地讓視圖高度變得栩栩如生。 +如需詳細資訊,請參閱 {@link android.view.ViewPropertyAnimator}的 API 參考資料和屬性動畫開發人員指南。 + +

    + +

    您也可以使用 {@link android.animation.StateListAnimator} 以宣告的方式指定這些動畫。 +當狀態變更觸發動畫時, 例如使用者按下按鈕,這會特別實用。 +如需詳細資訊,請參閱動畫顯示視圖狀態變更。 +

    + +

    Z 值的測量單位為 dp (密度獨立像素)。

    + + +

    自訂視圖陰影和外框

    + +

    檢視背景可繪項目的邊界決定了視圖陰影的預設形狀。外框代表圖形物件的外部形狀,並可定義輕觸回饋的漣漪區域。 + +

    + +

    我們來看看這個使用背景可繪項目定義的視圖:

    + +
    +<TextView
    +    android:id="@+id/myview"
    +    ...
    +    android:elevation="2dp"
    +    android:background="@drawable/myrect" />
    +
    + +

    背景可繪項目定義為有圓形邊角的長方形:

    + +
    +<!-- res/drawable/myrect.xml -->
    +<shape xmlns:android="http://schemas.android.com/apk/res/android"
    +       android:shape="rectangle">
    +    <solid android:color="#42000000" />
    +    <corners android:radius="5dp" />
    +</shape>
    +
    + +

    因為背景可繪項目定義了視圖的外框,所以視圖投射出有圓形邊角的陰影。 +如果提供自訂的外框,則會覆寫視圖陰影的預設形狀。

    + +

    在程式碼中定義視圖的自訂外框:

    + +

      +
    1. 延伸 {@link android.view.ViewOutlineProvider} 類別。
    2. +
    3. 覆寫 {@link android.view.ViewOutlineProvider#getOutline getOutline()} 方法。
    4. +
    5. 使用 {@link +android.view.View#setOutlineProvider View.setOutlineProvider()} 方法對您的視圖指定新的外框提供者。
    6. +
    + +

    您可以使用 {@link android.graphics.Outline} 類別中的方法,建立橢圓形外框和有圓形邊角的長方形外框。 +視圖的預設外框提供者會從視圖的背景取得外框。 +如果要避免視圖投射出陰影,請將視圖的外框提供者設定為 null。 +

    + + +

    裁剪視圖

    + +

    裁剪視圖可以讓您輕鬆地變更視圖的形狀。您可以裁剪視圖,讓視圖與其他設計元素有一致的外觀,或變更視圖的形狀來回應使用者的輸入。您可以使用 {@link android.view.View#setClipToOutline +View.setClipToOutline()} 方法或 android:clipToOutline 屬性,將視圖裁剪為其外框區域。 + +根據 {@link android.graphics.Outline#canClip Outline.canClip()} 方法的定義,只有長方形、圓形和圓角長方形的外框才支援裁剪。 + +

    + +

    如要將視圖裁剪為可繪項目的形狀,請將可繪項目設定為視圖的背景 (如上所示),然後呼叫 {@link android.view.View#setClipToOutline View.setClipToOutline()} 方法。 + +

    + +

    裁剪視圖是一種耗費資源的操作,所以請不要將您用來裁剪視圖的形狀做成動畫。 +如果想要達到這種效果,請使用顯示效果動畫。

    diff --git a/docs/html-intl/intl/zh-tw/training/material/theme.jd b/docs/html-intl/intl/zh-tw/training/material/theme.jd new file mode 100644 index 0000000000000000000000000000000000000000..61cd8bdcfda589b4ed59c04d6449269741127c3e --- /dev/null +++ b/docs/html-intl/intl/zh-tw/training/material/theme.jd @@ -0,0 +1,131 @@ +page.title=使用材料設計風格 + +@jd:body + +
    + +
    + + +

    新的材料設計風格提供:

    + +
      +
    • 可設定其色板的系統小工具
    • +
    • 系統小工具的輕觸回饋動畫
    • +
    • 操作行為轉換動畫
    • +
    + +

    您可根據品牌特性,使用您控制的色板以自訂材料設計風格的外觀。 +您可以使用風格主題屬性,為行為欄和狀態列著色,如圖 3 所示。 +

    + +

    系統小工具經過全新設計,有輕觸回饋動畫。您可以為應用程式自訂色板、輕觸回饋動畫,以及操作行為轉換。 +

    + +

    材料設計風格定義為:

    + +
      +
    • @android:style/Theme.Material (深色版本)
    • +
    • @android:style/Theme.Material.Light (淺色版本)
    • +
    • @android:style/Theme.Material.Light.DarkActionBar
    • +
    + +

    如需您可以使用的材料樣式清單,請參閱 {@link android.R.style R.style} 的 API 參考資料。 +

    + + +
    +
    + +
    +

    圖 1.深色材料設計風格

    +
    +
    +
    + +
    +

    圖 2.淺色材料設計風格

    +
    +
    +
    +
    + +

    +注意:材料設計風格只能在 Android 5.0 (API 級別 21) 及以上版本中使用。 +v7 支援程式庫針對某些小工具提供使用材料設計樣式的設計風格,並支援自訂色板。 + +如需詳細資訊,請參閱維持相容性。 + +

    + + +

    自訂色板

    + +

    如要自訂設計風格的基礎顏色來搭配您的品牌,在繼承材料設計風格時,請使用設計風格屬性定義您的自訂顏色: +

    + +
    +<resources>
    +  <!-- inherit from the material theme -->
    +  <style name="AppTheme" parent="android:Theme.Material">
    +    <!-- Main theme colors -->
    +    <!--   your app branding color for the app bar -->
    +    <item name="android:colorPrimary">@color/primary</item>
    +    <!--   darker variant for the status bar and contextual app bars -->
    +    <item name="android:colorPrimaryDark">@color/primary_dark</item>
    +    <!--   theme UI controls like checkboxes and text fields -->
    +    <item name="android:colorAccent">@color/accent</item>
    +  </style>
    +</resources>
    +
    + +
    + +

    +圖 3.自訂材料設計風格。

    +
    + + +

    自訂狀態列

    + +

    材料設計風格可讓您輕鬆自訂狀態列,因此,您可指定符合品牌的顏色,並提供足夠的對比來顯示白色的狀態圖示。 +如要設定狀態列的自訂顏色,請在延伸材料設計風格時使用 android:statusBarColor 屬性。 + +android:statusBarColor 預設會繼承 android:colorPrimaryDark 的值。 +

    + +

    您也可以自行繪製狀態列。例如,若您想在相片上面顯示透明的狀態列,卻想搭配一點深色的漸層以確保白色的狀態圖示清晰可見。 + +請將 android:statusBarColor 屬性設定為 @android:color/transparent,並且視需要調整視窗標幟。 +您也可以使用 {@link android.view.Window#setStatusBarColor Window.setStatusBarColor()} 方法顯示動畫或進行淡化。 + +

    + +

    +注意:狀態列應該與主要工具列有清楚區分的輪廓,除非您想在這些列後面顯示全版的豐富圖像或媒體內容,或使用漸層以確保圖示清晰可見。 + + +

    + +

    當您自訂瀏覽列和狀態列時,請讓兩者都變成透明,或者只修改狀態列。 +在其他所有狀況下,瀏覽列都必須保持黑色。

    + + +

    設計風格個別視圖

    + +

    XML 版面配置定義中的元素可以指定 android:theme 屬性,參考設計風格資源。 +此屬性會修改元素和所有子元素的設計風格,對於改變介面特定部份的設計風格色板非常實用。 + +

    diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml index 5d59130bbbb888297ab97683892acbd4c8b2bf04..5ce98ff8d05d3004538f37546e0843a7023b4784 100644 --- a/docs/html/_redirects.yaml +++ b/docs/html/_redirects.yaml @@ -1,5 +1,5 @@ # WARNING: THIS FILE IS NOT USED IN PRODUCTION -# CHANGES MADE HERE **DO NOT EFFECT** developer.android.com +# CHANGES MADE HERE **DO NOT AFFECT** developer.android.com # Instead, update the following file in the current docs release branch: # /vendor/google/docs/app-engine-server/v3/redirects.yaml diff --git a/docs/html/about/_book.yaml b/docs/html/about/_book.yaml new file mode 100644 index 0000000000000000000000000000000000000000..04150bdf1c20ca34e37b0c1af76747e8ddac367e --- /dev/null +++ b/docs/html/about/_book.yaml @@ -0,0 +1,49 @@ +toc: +- title: Marshmallow + path: /about/versions/marshmallow/index.html + section: + - title: Android 6.0 APIs + path: /about/versions/marshmallow/android-6.0.html + - title: Android 6.0 Changes + path: /about/versions/marshmallow/android-6.0-changes.html + - title: Android 6.0 Samples + path: /about/versions/marshmallow/samples.html + +- title: Lollipop + path: /about/versions/lollipop.html + section: + - title: Android 5.1 APIs + path: /about/versions/android-5.1.html + - title: Android 5.0 APIs + path: /about/versions/android-5.0.html + custom_link_attributes: + - es-lang="API de Android 5.0" + - ja-lang="Android 5.0 API" + - ko-lang="Android 5.0 API" + - ru-lang="API для Android 5.0" + - zh-cn-lang="Android 5.0 API" + - zh-tw-lang="Android 5.0 API" + - title: Android 5.0 Changes + path: /about/versions/android-5.0-changes.html + +- title: KitKat + path: /about/versions/kitkat.html + section: + - title: Android 4.4 APIs + path: /about/versions/android-4.4.html + +- title: Jelly Bean + path: /about/versions/jelly-bean.html + section: + - title: Android 4.3 APIs + path: /about/versions/android-4.3.html + - title: Android 4.2 APIs + path: /about/versions/android-4.2.html + - title: Android 4.1 APIs + path: /about/versions/android-4.1.html + +- title: About Android + path: /about/android.html + +- title: Dashboards + path: /about/dashboards/index.html diff --git a/docs/html/about/about_toc.cs b/docs/html/about/about_toc.cs index 45e8eb3e295b32dce26e7fbfcf88459cfd563bea..a45601278d8800461a7502eda648d7eea8595db7 100644 --- a/docs/html/about/about_toc.cs +++ b/docs/html/about/about_toc.cs @@ -1,5 +1,16 @@
    - - -
    \ No newline at end of file + diff --git a/docs/html/distribute/googleplay/guide.jd b/docs/html/distribute/googleplay/guide.jd index 8317206d1c122bf559b855615068f5ab030a3161..6cb8cc0a68c2eb806180017f8238763318e93918 100644 --- a/docs/html/distribute/googleplay/guide.jd +++ b/docs/html/distribute/googleplay/guide.jd @@ -1,7 +1,8 @@ -page.title=Finding Success on Google Play -page.metaDescription=A guide to help you find success with your app or game business on Google Play. -meta.tags="distribute", "bestpractices" -page.tags="google play", "business", "monetize", "engagement" +page.title=Find Success on Google Play +page.metaDescription=The updated guide that helps you find success with your app or game business on Google Play. +page.tags="play,protips" +page.timestamp=1447437450 +meta.tags="secrets, success, play, google" page.image=distribute/images/play_dev_guide.png @jd:body @@ -9,63 +10,34 @@ page.image=distribute/images/play_dev_guide.png

    We’ve created a downloadable guide to help you find success with your app or game business on Google Play. In it, you’ll find features, tips, and best - practices to help you build an effective strategy. + practices to help you build an effective strategy to improve the quality, + reach, retention, and monetization of your apps and games.

    -

    - The guide is separated into the following sections: -

    -
      -
    • - Publishing on Google Play — using the Google Play - Developer Console to distribute your app to over 1 billion Android users - worldwide. -
    • - -
    • - Quality — The fundamentals of building a great app - and an insight into the Google Play guidelines and policies. -
    • - -
    • - Discoverability & reach — Maximizing your app's - discoverability and reaching the widest audience possible. -
    • - -
    • - Engagement & retention — Converting - installations into active users and improving user retention. -
    • - -
    • - Monetization — Monetization strategies to generate - ongoing, growing revenue streams. -
    • - -
    • - Measurement with Google Analytics — Understanding - your users and improving your app experience, conversions, and marketing. -
    • - -
    • - Going global — Launching your app in local markets - around the world. -
    • -
    - -

    - Download the guide by clicking the image below or get it on Google Play. + + + + +

    + +

    Sign + up to be notified when the guide is released in the following languages: + Bahasa Indonesia, Deutsch, español (Latinoamérica), le français, português do + Brasil, tiếng Việt, русский язы́к, ไทย, + 한국어, 中文 + (简体), 中文 (繁體), and 日本語.

    -

    - We’ll release the guide in more languages in the coming months. Check back to - this website regularly as we post information on new features and best - practices to help you distribute and monetize your app. +

    You can also download + the pdf.

    -
    -
    - diff --git a/docs/html/distribute/googleplay/index.jd b/docs/html/distribute/googleplay/index.jd index 3b5966f915d346da844b5bb411ec93426d3b46bd..c69ce23f65bd26b614cd11cef1810d238d18de99 100644 --- a/docs/html/distribute/googleplay/index.jd +++ b/docs/html/distribute/googleplay/index.jd @@ -11,22 +11,24 @@ nonavpage=true help you gain traction in the marketplace.

    -

    Overview

    +
    +

    Overview

    -
    +
    -

    Opportunities & Programs

    +
    +

    Opportunities & Programs

    -
    +
    - -
    - -

    Zombie Ragdoll: Improved user engagement
    with localized versions

    - - - -
    - -
    About the app
    - - - -
    Localization Results
    - -
      -
    • Increased engagement because of appeal of the localized version
    • -
    • 80% of installs came from users of non-English languages
    • -
    - -
    - - Android app on Google Play - - -
    -
    - -
    - -

    - The 2013 Google I/O talks about Building Android - Apps for a Global Audience and What’s New for - Developers in Google Play inspired developers at RV AppStudios to go global - from very beginning for their new game, Zombie Ragdoll. They launched Zombie - Ragdoll in August 2013, localized into 20 languages. -

    - -

    - They quickly saw the impact of their decision to ship simultaneously in - multiple languages through increased non-English installs and improved - engagement with users worldwide. In addition, they started getting - significant usage in countries where their apps had not been as popular - before. They are seeing great traction in countries like Vietnam, Russia, - Philippines and Thailand. -

    - -

    - Vivek Dave, founder of RV AppStudios, credits the success of Zombie Ragdoll - to localization: -

    - -

    - "The value of localization is clear, it helps discoverability and helps - connect with the users in other countries. So when the localization - opportunity arose, we immediately jumped on it. Android is worldwide, and we - would be severely limiting ourselves if we focused on English as the only - language. -

    - -

    - "The App Translation Service offered in the Google Play Developer Console is - extremely easy to use and the pricing is very attractive. Developers with - limited localization experience can easily create, upload, and translate - their app." -

    - - -

    - RV AppStudios not only localizes the text within the game, but also localizes - the game assets to a specific country/culture. Dave says, “Users want a - personalized experience, and by offering a localized game with translation of - text and graphic assets, we believe users will connect at a much deeper level - with the game.” -

    - - -
    - -
    - - -
    -

    Hindi version of Zombie Ragdoll: - Localized screenshots and videos in the app's Google Play listing go a - long way toward increasing the number of installs.

    -
    - -
    - -
    - - - -
    - -

    SayHi Chat: Install growth and user engagement
    - from professional translations

    - - - -
    - -
    About the app
    - - - -
    Localization Results
    - -
      -
    • 120% growth in language installs for new languages added
    • -
    • ~20% increase in revenue and ~50% increase in User Reviews in the new - languages
    • -
    - -
    - - Android app on Google Play - - -
    -
    - -
    - -

    - The SayHi Chat app started out only in Japanese, Chinese and English. It soon - became one of the most popular apps in Japan, Hong Kong, and Taiwan. The - SayHi team realized it was time to launch in more languages, as the language - barrier was restricting how fast SayHi could grow globally.

    - -

    Yan Shi, senior - developer at SayHi, says: "We checked Google Analytics for our DAU and user - growth numbers in each country, we also looked at total Android and iOS users - in those markets before finalizing our next set of languages. -

    - -
    - -
    - -

    - SayHi used the App Translation Service to launch in 13 additional languages - in August 2013 and immediately saw 120% increase in install rates. In - addition, they are seeing their app ranked in Top 10 apps in countries like - Poland and Italy. -

    - -

    Notably, they saw steady growth in Spain after replacing their previous - non-professional Spanish translation with a professional one produced through - the App Translation Service.

    - -

    - Yan Shi adds, “The App Translation Service is really easy to use and - the completion time for translation requests is very good.” -

    - -
    -

    Arabic version of SayHi Chat: - User engagement increased significantly with - the localized version.

    -
    - -
    -
    - - -
    - -

    G4A Indian Rummy: Benefitting from ease-of-use and
    fast turnaround time

    - - - -
    - -
    About the app
    - -
      -
    • G4A Indian Rummy
    • -
    • A card game in which the players try to form sets and sequences of cards
    • -
    - -
    Localization Results
    - -
      -
    • Double the number of users in French and German languages
    • -
    • 300% increase in user engagement with localized version
    • -
    - -
    - - Android app on Google Play - -
    -
    - -
    - -

    - Games4All (G4A) is the developer of Indian Rummy and a variety of games that - they distribute broadly to users around the world. After noticing that - certain apps had become especially popular in specific countries, they - decided to localize those apps. Initially they used a local agency to do - the translation and got great results — the number of users in - that language increased tremendously when they released the localized - version. -

    - -

    - Building on that success, G4A expanded their localization goals but - found that translation quality varied across their vendors and costs limited the - language/game combinations they could try. That's when G4A decided to try the - App Translation Service. -

    - -

    - Founder Pieter Olivier says, "When we heard that the App Translation - Service was available in the Developer Console, we jumped at the opportunity. - We've now been using the App Translation Service for several months and found - that the cost per translation is much lower than with local companies and the - process is much easier." -

    - -

    So far, G4A has translated the game Indian Rummy into five languages through - the App Translation Service.

    - -

    - Olivier continues, "The first thing we did was convert all of our texts into - the strings.xml format. After that using the service was extremely easy and - straightforward. In contrast, our previous experiences with translation - agencies were much more difficult: files often required extensive conversion - operations to make them usable, and turnaround times varied wildly. -

    - -

    - "With the App Translation Service, the turnaround time is usually measured in - days instead of weeks that we were used to with traditional translation - agencies." -

    - -
    - -
    - -
    -

    Dutch - version of Indian Rummy: Making slight changes to games rules based on - local nuances was key to success of the game.

    -
    - -
    - - - -
    \ No newline at end of file diff --git a/docs/html/distribute/stories/stories_toc.cs b/docs/html/distribute/stories/stories_toc.cs index 54b7639d6c2dbcc7f492ff34b5c527741c935ab9..cd86af88ee273e0d374002bf6452f704be506be0 100644 --- a/docs/html/distribute/stories/stories_toc.cs +++ b/docs/html/distribute/stories/stories_toc.cs @@ -1,30 +1,27 @@ - - -
    - - -

     

    - -   - -

     or

    - -   - -

    - - -
    - - -      - - -
    - -
    - - -      - - -
    - - -
    -
    - - - diff --git a/docs/html/distribute/tools/promote/brand.jd b/docs/html/distribute/tools/promote/brand.jd index cf83a5e7d727c9bba2a37f564d23786b41c5ec34..409dfdda27561e9a8c5454338a6c3cd2a24ccf3c 100644 --- a/docs/html/distribute/tools/promote/brand.jd +++ b/docs/html/distribute/tools/promote/brand.jd @@ -14,6 +14,12 @@ Partner Marketing team. Use the Android and Google Play Brand Permissions Inquiry form to submit your marketing for review.

    +

    + If you need Google Play badges or guidelines, please see the + Google Play Badge page. +

    + +

    Android

    The following are guidelines for the Android brand @@ -101,103 +107,10 @@ used according to terms described in the Creative Commons 3.0 Attribution Licens

    The custom typeface may not be used.

    -

    The following are guidelines for the Google Play brand.

    - -

    Google Play™ Badges

    - -
    - -

    - 129x45 | - 172x60

    -
    - -
    - -

    - 129x45 | - 172x60

    -
    - -

    Use the "Get it on Google Play" and "Android App on Google Play" badges on your website and - promotional materials to point to your products on Google Play. These badges are both available - in over 40 languages. Additional Google Play badge formats and - badges for music, books, magazines, movies, and TV shows are also available. - Use the Android - and Google Play Brand Permissions Inquiry form to request - those badges.

    - -

    Google Play badge guidelines:

    -
      -
    • Don't modify the color, proportions, spacing, or any other aspect of the badge.
    • -
    • When used alongside logos for other application marketplaces, the Google Play badge - should be of equal or greater size.
    • -
    • When used online, the badge should link to either:
    • -
        -
      • A list of products published by you, for example:
        - http://play.google.com/store/search?q=publisherName -
      • -
      • A specific app product details page within Google Play, for example:
        - http://play.google.com/store/apps/details?id=packageName -
      • -
      -
    • You do not need to include a legal attribution if you are only using a Google Play badge. - However, keep in mind that:
    • -
        -
      • If you make any mention of Google Play or Android outside of the badge a legal attribution - must be included.
      • -
      • If you are including another app store’s legal attribution then include this legal - attribution:
      • -
          -
        • Google Play is a trademark of Google Inc.
        • -
        - -
      • Use of the Google Play badge does not need to be reviewed or approved by the Google Play - brand team unless the marketing campaign will have over 1 million impressions.
      • -
      - -

      To quickly create a badge that links to your apps on Google Play, - use the Google Play badge generator - (badges available in over 40 languages).

      - -

      For details on all the ways that you can link to your product details page in Google Play, - see Linking to your products.

      - -

      Google Play in Text

      - -

      Any use of Google Play in text must be reviewed and approved by the Google Play brand team. -Submit your marketing via the -Android and Google Play Partner Brand Inquiry Form.

      - -

      Always include a ™ symbol on the first or most prominent instance of Google Play™ in text.

      - -

      When mentioning that a product is available on Google Play always say “on Google Play”

      - -
        -
      • Incorrect: Our app is now available from Google Play.
      • -
      • Correct: Our app is now available on Google Play.
      • -
      -
      -

      Only refer to Google Play as the Google Play™ store app in instructional text meant to inform a -customer about how to find or download your product on Google Play.

      - -
        -
      • Incorrect: Download our games using the Google Play™ store app.
      • -
      • Correct: This is how you download our app using the Google -Play™ store app.
      • -
      -
      - -

      Any use of the Google Play name in your marketing or communications needs to be accompanied by -this legal attribution:

      - -

      Google Play is a trademark of Google Inc.

      Marketing Reviews and Brand Inquiries

      Use the Android and Google Play Brand Permissions Inquiry form to submit any marketing -reviews or brand inquires. Typical response time is at least one week.

      \ No newline at end of file +reviews or brand inquires. Typical response time is at least one week.

      diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_back.png new file mode 100644 index 0000000000000000000000000000000000000000..6407fbee6a30dc219940db4b8566a2f2909c0045 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_back.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..b831f482c5d2418dbc9f3156e7f62454364a970f Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_fore.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..79b5930e0a4c0997b91f4c2c1e96bb806f981da4 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/land_shadow.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_back.png new file mode 100644 index 0000000000000000000000000000000000000000..33691b24bee46b7741269687a1f255f301e659bb Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_back.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..c227a925ec7f3a71be8079d75ae4c7bd546780a9 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_fore.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..3a19d6fe1768851b5ba5622bdd719ac7a0c06f8a Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/port_shadow.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..4f56fee048aceaeeb305401ded63ca600bb443ab Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_5x/thumb.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_back.png new file mode 100644 index 0000000000000000000000000000000000000000..3495b13b05f2531e1e6119661a8bc292751c22b4 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_back.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..e8c74134a27415638f32e3dcdec11ce58afb9549 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_fore.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..e88906b9e45e4804ad2f7eb1baac844c636e4e6e Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/land_shadow.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_back.png new file mode 100644 index 0000000000000000000000000000000000000000..9c311fd19b68326abb350ad7f8712873543ed99a Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_back.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_fore.png new file mode 100644 index 0000000000000000000000000000000000000000..442ab87a55224289179cf611baf3b5fb689853ac Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_fore.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..4b4e526e97669e8de6fbd829047012711dd8ac30 Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/port_shadow.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..5ef009850210e62180e925144bbc50617cdd11af Binary files /dev/null and b/docs/html/distribute/tools/promote/device-art-resources/nexus_6p/thumb.png differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_back.png deleted file mode 100644 index 697fb7dfede26d93e3b5f6df41301098a69ce6b5..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_back.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_fore.png deleted file mode 100644 index 735262f696e4619ce78fca5d94ab9bb21a532edc..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_fore.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_shadow.png deleted file mode 100644 index cfb7952d4b7dec44099f1b7ca4a855da0556a31f..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/land_shadow.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_back.png deleted file mode 100644 index 5bb815a57f3d1ff453deb2af67bf17f87ecfe849..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_back.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_fore.png deleted file mode 100644 index 1be3b210a77dd224e7a7972b62d50c09de5f8156..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_fore.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_shadow.png deleted file mode 100644 index 7e8aff2bfa7e7b6521b8895c9def8279bee2cb05..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/port_shadow.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/thumb.png deleted file mode 100644 index b5db82e3f4626ed9e1d6a530859d1a46ce5b0ba2..0000000000000000000000000000000000000000 Binary files a/docs/html/distribute/tools/promote/device-art-resources/nexus_7_2012/thumb.png and /dev/null differ diff --git a/docs/html/distribute/tools/promote/device-art.jd b/docs/html/distribute/tools/promote/device-art.jd index d321074a9c5af327bb2cd8404f44404c43b55e4a..9b4dd1481aeb604ded3315c87a8b847bf74914e7 100644 --- a/docs/html/distribute/tools/promote/device-art.jd +++ b/docs/html/distribute/tools/promote/device-art.jd @@ -186,7 +186,7 @@ feature image or screenshots for your Google Play app listing.

      { id: 'nexus_5', title: 'Nexus 5', - url: 'http://www.google.com/nexus/5/', + url: 'https://www.google.com/nexus/5/', physicalSize: 5, physicalHeight: 5.43, density: 'XXHDPI', @@ -195,11 +195,25 @@ feature image or screenshots for your Google Play app listing.

      portRes: ['shadow', 'back', 'fore'], portOffset: [304,436], portSize: [1080,1920], + archived: true + }, + { + id: 'nexus_5x', + title: 'Nexus 5X', + url: 'https://www.google.com/nexus/5x/', + physicalSize: 5.2, + physicalHeight: 5.625, + density: '420DPI', + landRes: ['shadow', 'back', 'fore'], + landOffset: [485,313], + portRes: ['shadow', 'back', 'fore'], + portOffset: [305,485], + portSize: [1080,1920], }, { id: 'nexus_6', title: 'Nexus 6', - url: 'http://www.google.com/nexus/6/', + url: 'https://www.google.com/nexus/6/', physicalSize: 6, physicalHeight: 6.27, density: '560DPI', @@ -207,7 +221,21 @@ feature image or screenshots for your Google Play app listing.

      landOffset: [489,327], portRes: ['shadow', 'back', 'fore'], portOffset: [327,489], - portSize: [1440, 2560], + portSize: [1440, 2560], + archived: true + }, + { + id: 'nexus_6p', + title: 'Nexus 6P', + url: 'https://www.google.com/nexus/6p/', + physicalSize: 5.7, + physicalHeight: 6.125, + density: '560DPI', + landRes: ['shadow', 'back', 'fore'], + landOffset: [579,321], + portRes: ['shadow', 'back', 'fore'], + portOffset: [312,579], + portSize: [1440, 2560] }, { id: 'nexus_7', @@ -221,12 +249,13 @@ feature image or screenshots for your Google Play app listing.

      landOffset: [326,245], portRes: ['shadow', 'back', 'fore'], portOffset: [244,326], - portSize: [800,1280] + portSize: [800,1280], + archived: true }, { id: 'nexus_9', title: 'Nexus 9', - url: 'http://www.google.com/nexus/9/', + url: 'https://www.google.com/nexus/9/', physicalSize: 9, physicalHeight: 8.98, actualResolution: [1536,2048], @@ -240,7 +269,7 @@ feature image or screenshots for your Google Play app listing.

      { id: 'nexus_10', title: 'Nexus 10', - url: 'http://www.google.com/nexus/10/', + url: 'https://www.google.com/nexus/10/', physicalSize: 10, physicalHeight: 7, actualResolution: [1600,2560], @@ -252,24 +281,10 @@ feature image or screenshots for your Google Play app listing.

      portSize: [800,1280], archived: true }, - { - id: 'nexus_7_2012', - title: 'Nexus 7 (2012)', - url: 'http://www.google.com/nexus/7/', - physicalSize: 7, - physicalHeight: 7.81, - density: '213dpi', - landRes: ['shadow', 'back', 'fore'], - landOffset: [315,270], - portRes: ['shadow', 'back', 'fore'], - portOffset: [264,311], - portSize: [800,1280], - archived: true - }, { id: 'nexus_4', title: 'Nexus 4', - url: 'http://www.google.com/nexus/4/', + url: 'https://www.google.com/nexus/4/', physicalSize: 4.7, physicalHeight: 5.27, density: 'XHDPI', @@ -283,7 +298,7 @@ feature image or screenshots for your Google Play app listing.

      { id: 'wear', title: 'Android Wear', - url: 'http://www.android.com/wear/', + url: 'https://www.android.com/wear/', physicalSize: 1.8, physicalHeight: 1.8, density: 'HDPI', @@ -296,7 +311,7 @@ feature image or screenshots for your Google Play app listing.

      { id: 'wear_square', title: 'Android Wear Square', - url: 'http://www.android.com/wear/', + url: 'https://www.android.com/wear/', physicalSize: 1.8, physicalHeight: 1.8, density: 'HDPI', @@ -310,7 +325,7 @@ feature image or screenshots for your Google Play app listing.

      { id: 'wear_round', title: 'Android Wear Round', - url: 'http://www.android.com/wear/', + url: 'https://www.android.com/wear/', physicalSize: 1.8, physicalHeight: 1.8, density: 'HDPI', diff --git a/docs/html/distribute/users/_book.yaml b/docs/html/distribute/users/_book.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0febb0281f8702085dc3a6d42003893f40e8b2f6 --- /dev/null +++ b/docs/html/distribute/users/_book.yaml @@ -0,0 +1,33 @@ +toc: +- title: Create a Great Listing + path: /distribute/users/your-listing.html + +- title: Promote with Ads + path: /distribute/users/promote-with-ads.html + +- title: Drive Installs from Search + path: /distribute/users/appindexing.html + +- title: Expand to New Markets + path: /distribute/users/expand-to-new-markets.html + +- title: Offer Over-the-air Installs + path: /distribute/users/ota-installs.html + +- title: Smarter App Invites + path: /distribute/users/app-invites.html + +- title: Drive installs from YouTube + path: /distribute/users/youtube.html + +- title: Cross-Sell with House Ads + path: /distribute/users/house-ads.html + +- title: Increase installs with Store Listing Experiments + path: /distribute/users/experiments.html + +- title: Add an App Install Banner On Your Mobile Website + path: /distribute/users/banners.html + +- title: Understand Where Your Users Come From + path: /distribute/users/user-acquisition.html diff --git a/docs/html/distribute/users/appindexing.jd b/docs/html/distribute/users/appindexing.jd deleted file mode 100644 index 04114e32d7eb1dc2a11fdbf7d400ff2862d3ad99..0000000000000000000000000000000000000000 --- a/docs/html/distribute/users/appindexing.jd +++ /dev/null @@ -1,47 +0,0 @@ -page.title=Drive installs from Google Search -page.metaDescription=Surface the content of your apps in Google Search and link it to app installs. -meta.tags="getusers", "search", "appindexing" -page.tags="app indexing", "search", "get users" -page.image=images/cards/google-search_2x.png -@jd:body - -

      Google Search now helps users discover your app, with App Indexing. When users -search with Google on their Android device the results will include details of -relevant, indexed apps. The search results will include an install button that -takes users to your app in the Google Play store. And when your app is -installed, the user will be taken straight to the right content within it.

      - -
      - -
      - -

      This free service expands your opportunities to turn the investment you’ve made -in creating an outstanding app or game into installs.

      - -

      Start now by adding deep linking support to your app, verifying your app’s -official website, and defining deep links. To learn how, check out these instructions.

      - -

      Once your app is index, links to its content will join the 30 billion app index -links already available to Google Search.

      - -

      Tips

      - -
        -
      • App Indexing will also be used as a ranking signal for all users on Android, -regardless of whether they have your app installed or not. -
      • App Indexing also helps drive use of your app — when your app is installed on a -user’s device, search results will include deep links to your app that bring -the user straight to the relevant app content. -
      • Use the App Indexing API to help users discover relevant content in your app -before they complete a query with auto-completions. -
      - - - -
      - diff --git a/docs/html/distribute/users/banners.jd b/docs/html/distribute/users/banners.jd new file mode 100644 index 0000000000000000000000000000000000000000..d889ac7f4efb58a1b236ec0efa76645a5bca19cf --- /dev/null +++ b/docs/html/distribute/users/banners.jd @@ -0,0 +1,64 @@ +page.title=Add An App Install Banner On Your Mobile Website +page.metaDescription=Add a native app install banner to your mobile website and allow visitors to install your app straight from your site. +page.image=images/cards/card-install-banners_16x9_2x.png +page.tags="banner, chrome, web" +Xnonavpage=true + +@jd:body + + + +
      + +
      + +

      + Add a native app install banner to your mobile website and offer visitors the + added convenience of installing your app or game straight from your site. +

      + +

      + Learn how to add a native app install banner to your mobile + site. +

      + +

      How it Works

      + +

      + App install banners are offered as an extension of Chrome’s ability to + deliver Web App install banners. You can setup this feature in a web app + manifest file. In the manifest you define how your app will appear on the + user’s system, how it should be launched, and whether its banner has priority + over the banner for your Web App. Then, as long as your site is delivered + over HTTPS, after a user visits your site on two separate days within a two + week period they’ll be shown your app’s install banner. +

      + +

      +

      + +
      +
      + +
      + +
      +
      diff --git a/docs/html/distribute/users/build-community.jd b/docs/html/distribute/users/build-community.jd deleted file mode 100644 index 23b9d880c510478249ccc9b73a861a118e45938e..0000000000000000000000000000000000000000 --- a/docs/html/distribute/users/build-community.jd +++ /dev/null @@ -1,183 +0,0 @@ -page.title=Build Community -page.metaDescription=Build a loyal following with great support and communication. -page.tags="users, growth, community" -page.image=/images/cards/card-build-community_16-9_2x.png - -@jd:body - - -

      - Fans of your apps love to help others, turn newer users into fans, and bring - you more users as they talk about your app. Building a community can help you - tap into those influencers to help you improve your app and provide support - to others. -

      - -
      - -
      - -

      - Building your own community can help you bring content that will delight - users and get them talking about your apps to friends, family and others in - their social network. -

      - -
      -

      - Starting Your Community -

      - - -
      - -

      - In conjunction with your apps’ design and development, you should start - defining and building your community infrastructure. There’s no one approach - that fits all, and the approach for each of your apps may need to be a little - different. You should start by thinking about your potential users and asking - questions such as: -

      - -
        -
      • -

        - How will my users prefer to interact? Game users may prefer a modern feed - style community, users of a financial management app a more traditional - discussion forum. -

        -
      • - -
      • -

        - Should I have a community for all my apps or should each app have its - own? Will users be turned off if the community isn’t just about the app - that interests them or can I make it a way to turn them onto my other - apps? -

        -
      • - -
      • -

        - Will different countries or territories, or speakers of particular - languages need separate forums? How will I handle feedback in languages I - don’t know? -

        -
      • - -
      • -

        - Do I need any additional policies beyond those governing the tool used to - host the community? -

        -
      • -
      - -

      - Any way you do it, starting your community early helps you build momentum as - you turn happy users into influencers. -

      - -

      - Consider inviting your existing users through a rich notification or an - opt-in on your website. Don’t overlook inviting your critics too. If you have - been able to address their earlier issues you may convert them into - supporters — it’s not unknown for your harshest critics to become your most - enthusiastic fans if you address their concerns. -

      - -

      - When you use the beta-testing - feature in Google Play, you’ll create a testers group through a Google Group or Google+ Community to - define who gets your software for testing. Consider managing these groups as - communities in their own right. -

      - -
      -

      - Tools to Build Your Community -

      - - -
      - -

      - There are many tools you can use to build your community. Before you launch, - inviting Google+ users or - Google - Groups to beta-test - your app can help you kickstart your community while you listen to and - respond to your user feedback. -

      - -

      - Once you’ve launched, your Google+ or other social media presence can help - you continue to gather feedback, answer questions, and get input on updates. - Use social media to get the conversation started. Post updates to your - followers, announce new apps, and host contests. Ask followers to re-post so - that they bring new users into the conversation. Fans love to profess their - passion for great apps, so be sure to give them plenty of reason to do so. -

      - -

      - Forums like Google Groups are - particularly well suited to help you and your users provide support to - others. By helping out your community, you’re building your fan base who will - share their experiences with other prospective customers. -

      - -

      - Respond to comments and reviews on both your product details page on Google - Play and YouTube pages. Prospective - customers are influenced by reviews and comments, so be sure to manage your - brand in every channel you can. -

      - -
      -

      - Managing Your Community -

      - - -
      - -
      - -
      - -

      - Engaged users want you to succeed. Let them know you’re listening! Responding - to posts, comments, and other social media mentions improves your ratings by - letting users know you care. -

      - -

      - Update the product based on user feedback and announce new releases. Users - often change their original star ratings after feeling heard, inspiring more - users to install your apps. -

      - -

      - There are many ways to make your community feel special. Consider polls to - let users influence product updates. Use competitions to inspire and reward - your community. Giving a special member of the week badge is an easy - way to recognize those that help others. Or get users involved in testing new - versions or new apps to make them feel special. -

      - -
      - - -
      -
      \ No newline at end of file diff --git a/docs/html/distribute/users/experiments.jd b/docs/html/distribute/users/experiments.jd new file mode 100644 index 0000000000000000000000000000000000000000..215d3a2ee904b7f734a50963fad75db8654978a1 --- /dev/null +++ b/docs/html/distribute/users/experiments.jd @@ -0,0 +1,121 @@ +page.title=Increase installs with Store Listing Experiments +page.metaDescription=Run experiments to find the best possible Play Store listing page. +page.image=images/cards/distribute/users/experiments.png +page.tags="ab testing, experiments, testing" +Xnonavpage=true + +@jd:body + +

      + First impressions matter, so having the best possible Play Store listing page + for your app is vital. Store Listing Experiments let you test combinations of + text and graphics to find the ones that bring you the most installs. +

      + + + +

      + Learn how to get started with Store Listing Experiments. +

      + +

      Tips for planning a successful experiment

      +
        +
      • Make sure that you have a question or objective in mind when designing + your experiments. +
      • + +
      • Test one attribute at a time to get the clearest results, but test + similar changes in icons and feature graphics together, for example comparing + use of character art against objects from a game. +
      • + +
      • Start testing with your app icon, because it can have a huge impact on + whether users pick your app from search results and category lists. +
      • + +
      • Test all of your listing’s attributes; after icons, test screenshots and + short descriptions as they are seen by users from search results and category + lists. Test the feature graphic as a priority, as it has such a presence on + your store listing’s page. +
      • +
      + +

      Tips for your graphics and text

      +
        +
      • Create up to three alternatives for your hi-res icon, feature graphic, + screenshots, promo video, short description, and full description. +
      • + +
      • Set up a Global experiment for graphics against your default language or + a Localized experiment for both text and graphics in a specific language. + Experiment with screenshots and their order. +
      • + +
      • Be bold about testing different artwork, but iterate on the results to + confirm and improve your findings. +
      • + +
      • Make sure short descriptions clearly demonstrate an app or game’s value + to users. +
      • +
      + +

      Tips for running successful experiments

      +
        +
      • Test one asset at a time, unless testing completely different designs for + a store listing page against one another. +
      • + +
      • Run your experiment for at least 7 days, even if you have enough traffic + to get meaningful results in a few days. Depending on the percentage of your + audience you’re showing the experiment too and the traffic on your listings + page it may take anything from a few days to several weeks to get a + result. +
      • + +
      • Run your experiments with 50 percent of your audience, the maximum + allowed, to get results faster; particularly for localized tests that may be + visible to only a small percentage of your users. +
      • + +
      • Check the banner on the Experiments page to see progress. Once enough + data has been collected the winner is displayed and you can apply it to your + store listing. +
      • +
      + +

      +

      + + + +
      +
      + +
      + +
      +
      diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd index f0e32568c5ae6be7641013a85383f321328708e7..2db4ca30610790946b63d4404f5c9864b147005c 100644 --- a/docs/html/distribute/users/promote-with-ads.jd +++ b/docs/html/distribute/users/promote-with-ads.jd @@ -29,10 +29,8 @@ app install ads.

      - Search ads on Google Play are still undergoing testing and not yet - available to buy. - Find out more. + Promote your app on Google Play when users are searching and browsing + for apps.

    diff --git a/docs/html/distribute/users/user-acquisition.jd b/docs/html/distribute/users/user-acquisition.jd new file mode 100644 index 0000000000000000000000000000000000000000..3f073f55f76067bbaec8e461b5e626ee90ef0b72 --- /dev/null +++ b/docs/html/distribute/users/user-acquisition.jd @@ -0,0 +1,102 @@ +page.title=Understand Where Your Users Come From +page.metaDescription=Use the Developer Console to identify the channels that bring the greatest volume of high-value users. +page.image=images/cards/distribute/users/user-acquisition.jpg +page.tags="user acquisition, reports, cohorts" +Xnonavpage=true + +@jd:body + + + +

    + Whether you pay to acquire users or not, you want to focus your acquisition + efforts on the channels that bring you the greatest volume of high-value + users. It’s now easy to identify those channels from the User Acquisition + page in the Developer Console. +

    + + + +

    + From the performance report you can follow users — whether originating as + organic or paid traffic — from discovery to installation then onto their + purchase of your in-app products, with convenient predefined reporting + criteria. With the improved understanding of where your most valuable users + come from, you can better focus your efforts. +

    + +

    + Learn more about how to measure your app’s user acquisition channels. +

    + +

    How to use the User Acquisition performance report

    + +
      +
    • + Compare cohorts: In this report, a cohort is a + group of users who’ve visited your app’s store listing during a selected + date range. For example, if you update your app’s icon on the Play Store, + you’ll want to see how the change affects installs and in-app purchases. + You can do this by selecting cohorts before and after your app update, and + measure how your change impacted conversion rates. +
    • + +
    • + Examine acquisition channels: For your defined cohort, you + can explore how user behaviour and value differ between acquisition + channels, such as organic Play Store traffic, AdWords campaigns, tracked + channels, and searches. Drill down into specific channels to explore user + behaviour in detail. +
    • + +
    • + See users: Get stats for all the store listing + visitors (users who visited your app’s store listing) and + installers. This helps you understand how prospects are + converting into users. +
    • + +
    • + See buyers: If you sell in-app products, the funnel report + can also tell you about new buyers and repeat + buyers. This helps you understand how quickly your in-app products + and subscriptions are gaining traction with users. +
    • +
    + +

    + Tip: If you’re directing your users to your mobile app from + your website, you can track conversions by adding a UTM campaign source and a + campaign name tag to the Google Play store URL. +

    + +

    +

    + +
    +
    + +
    + +
    +
    diff --git a/docs/html/distribute/users/users_toc.cs b/docs/html/distribute/users/users_toc.cs index 1e4085bb644dc51dced9aea1790909e786300364..f84c9269f14dcf08a99b44b8a7435742b7b24c03 100644 --- a/docs/html/distribute/users/users_toc.cs +++ b/docs/html/distribute/users/users_toc.cs @@ -47,6 +47,26 @@
    + + + + + diff --git a/docs/html/google/_book.yaml b/docs/html/google/_book.yaml new file mode 100644 index 0000000000000000000000000000000000000000..01efa4f9fe93a2f3741fbd246ea366492fde3f4d --- /dev/null +++ b/docs/html/google/_book.yaml @@ -0,0 +1,78 @@ +toc: +- title: Google Play In-app Billing + path: /google/play/billing/index.html + custom_link_attributes: + - zh-cn-lang="应用内结算" + section: + - title: Overview + path: /google/play/billing/billing_overview.html + custom_link_attributes: + - zh-cn-lang="应用内结算概述" + - title: Version 3 API + path: /google/play/billing/api.html + custom_link_attributes: + - zh-cn-lang="应用内结算 API" + section: + - title: Implementing the API + path: /google/play/billing/billing_integrate.html + - title: Reference + path: /google/play/billing/billing_reference.html + - title: Subscriptions + path: /google/play/billing/billing_subscriptions.html + - title: Promotions + path: /google/play/billing/billing_promotions.html + - title: Security and Design + path: /google/play/billing/billing_best_practices.html + custom_link_attributes: + - zh-cn-lang="安全性和设计" + - title: Testing In-app Billing + path: /google/play/billing/billing_testing.html + custom_link_attributes: + - zh-cn-lang="测试应用内结算" + - title: Administering In-app Billing + path: /google/play/billing/billing_admin.html + custom_link_attributes: + - zh-cn-lang="管理应用内结算" + - title: Version Notes + path: /google/play/billing/versions.html + +- title: Filters on Google Play + path: /google/play/filters.html + custom_link_attributes: + - es-lang="Filtros en Google Play" + - ja-lang="Google Play 上のフィルタ" + - ko-lang="Google Play 필터" + - pt-br-lang="Filtros no Google Play" + - ru-lang="Фильтры в Google Play" + - zh-cn-lang="Google Play 上的筛选器" + - zh-tw-lang="Google Play 上的篩選器" + +- title: Google Play Developer API + path: /google/play/developer-api.html + +- title: Advertising ID + path: /google/play-services/id.html + +- title: Multiple APK Support + path: /google/play/publishing/multiple-apks.html + +- title: APK Expansion Files + path: /google/play/expansion-files.html + +- title: Application Licensing + path: /google/play/licensing/index.html + section: + - title: Licensing Overview + path: /google/play/licensing/overview.html + - title: Setting Up for Licensing + path: /google/play/licensing/setting-up.html + - title: Adding Licensing to Your App + path: /google/play/licensing/adding-licensing.html + - title: Licensing Reference + path: /google/play/licensing/licensing-reference.html + +- title: Android Backup Service + path: /google/backup/index.html + section: + - title: Register + path: /google/backup/signup.html diff --git a/docs/html/google/backup/signup.jd b/docs/html/google/backup/signup.jd index f9ad6002b0d75a5b25d9b1bd93a2b70538d58096..598003dbe23b2d4462a32de52b1e04f2c2ed2244 100644 --- a/docs/html/google/backup/signup.jd +++ b/docs/html/google/backup/signup.jd @@ -208,7 +208,7 @@ with the Android Backup Service Terms of Service

    -

    Register with Android Backup Service

    diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs index ffdc22dd9911753f445bf5066c65f868f716b995..e5d8c137a190b14db5b299d267dd0df29c8522ab 100644 --- a/docs/html/google/google_toc.cs +++ b/docs/html/google/google_toc.cs @@ -17,20 +17,13 @@ Reference - +
  • Subscriptions
  • +
  • + Promotions +
  • Security and Design
  • @@ -63,6 +56,11 @@ + +
    -

    A typical purchase flow with the Version 3 API is as follows: +

    A typical purchase flow with the Version 3 API is as follows:

    +
      -
    1. Your application sends a {@code isBillingSupported} request to Google Play to determine that the target version of the In-app Billing API that you are using is supported.
    2. -
    3. When your application starts or user logs in, it's good practice to check with Google Play to determine what items are owned by the user. To query the user's in-app purchases, send a {@code getPurchases} request. If the request is successful, Google Play returns a {@code Bundle} containing a list of product IDs of the purchased items, a list of the individual purchase details, and a list of the signatures for the purchases.
    4. -
    5. Usually, you'll want to inform the user of the products that are available for purchase. To query the details of the in-app products that you defined in Google Play, your application can send a {@code getSkuDetails} request. You must specify a list of product IDs in the query request. If the request is successful, Google Play returns a {@code Bundle} containing product details including the product’s price, title, description, and the purchase type. -
    6. -
    7. If an in-app product is not owned by the user, you can initiate a purchase for it. To start a purchase request, your application sends a {@code getBuyIntent} request, specifying the product ID of the item to purchase, along with other parameters. You should record the product ID when you create a new in-app product in the Developer Console. -
        -
      1. Google Play returns a {@code Bundle} that contains a {@code PendingIntent} which you application uses to start the checkout UI for the purchase.
      2. -
      3. Your application launches the pending intent by calling the {@code startIntentSenderForResult} method.
      4. -
      5. When the checkout flow finishes (that is, the user successfully purchases the item or cancels the purchase), Google Play sends a response {@code Intent} to your {@code onActivityResult} method. The result code of the {@code onActivityResult} has a result code that indicates whether the purchase was successful or canceled. The response {@code Intent} contains information about the purchased item, including a {@code purchaseToken} String that is generated by Google Play to uniquely identify this purchase transaction. The {@code Intent} also contains the signature of the purchase, signed with your private developer key.
      6. -
      -
    8. +
    9. Your application sends a {@code isBillingSupported} request to Google + Play to determine that the target version of the In-app Billing API that you + are using is supported. +
    10. + +
    11. When your application starts or user logs in, it's good practice to check + with Google Play to determine what items are owned by the user. To query the + user's in-app purchases, send a {@code getPurchases} request. If the request + is successful, Google Play returns a {@code Bundle} containing a list of + product IDs of the purchased items, a list of the individual purchase + details, and a list of the signatures for the purchases. +
    12. + +
    13. Usually, you'll want to inform the user of the products that are + available for purchase. To query the details of the in-app products that you + defined in Google Play, your application can send a {@code getSkuDetails} + request. You must specify a list of product IDs in the query request. If the + request is successful, Google Play returns a {@code Bundle} containing + product details including the product’s price, title, description, and the + purchase type. +
    14. + +
    15. If an in-app product is not owned by the user, you can initiate a + purchase for it. To start a purchase request, your application sends a {@code + getBuyIntent} request, specifying the product ID of the item to purchase, + along with other parameters. You should record the product ID when you create + a new in-app product in the Developer Console. +
        +
      1. Google Play returns a {@code Bundle} that contains a {@code + PendingIntent} which your application uses to start the checkout UI for + the purchase. +
      2. + +
      3. Your application launches the pending intent by calling the {@code + startIntentSenderForResult} method. +
      4. + +
      5. When the checkout flow finishes (that is, the user successfully + purchases the item or cancels the purchase), Google Play sends a response + {@code Intent} to your {@code onActivityResult} method. The result code + of the {@code onActivityResult} has a result code that indicates whether + the purchase was successful or canceled. The response {@code Intent} + contains information about the purchased item, including a {@code + purchaseToken} String that is generated by Google Play to uniquely + identify this purchase transaction. The {@code Intent} also contains the + signature of the purchase, signed with your private developer key. +
      6. +
      +
    + +

    + To learn more about the Version 3 API calls and server responses, see + In-app Billing + Reference.

    -

    To learn more about the Version 3 API calls and server responses, see In-app Billing Reference.

    Consuming In-app Products

    -

    You can use the consumption mechanism to track the user's ownership of in-app -products.

    -

    In Version 3, all in-app products are managed. This means that the user's -ownership of all in-app item purchases is maintained by Google Play, and your -application can query the user's purchase information when needed. When the user -successfully purchases an in-app product, that purchase is recorded in Google -Play. Once an in-app product is purchased, it is considered to be "owned". -In-app products in the "owned" state cannot be purchased from Google Play. You -must send a consumption request for the "owned" in-app product before Google -Play makes it available for purchase again. Consuming the in-app product reverts -it to the "unowned" state, and discards the previous purchase data.

    + +

    + You can use the consumption mechanism to track the user's ownership of in-app + products. +

    + +

    + In Version 3, all in-app products are managed. This means that the user's + ownership of all in-app item purchases is maintained by Google Play, and your + application can query the user's purchase information when needed. When the + user successfully purchases an in-app product, that purchase is recorded in + Google Play. Once an in-app product is purchased, it is considered to be + "owned". In-app products in the "owned" state cannot be purchased from Google + Play. You must send a consumption request for the "owned" in-app product + before Google Play makes it available for purchase again. Consuming the + in-app product reverts it to the "unowned" state, and discards the previous + purchase data. +

    +
    - +

    Figure 2. The basic sequence for a consumption request.

    -

    To retrieve the list of product's owned by the user, your application sends a {@code getPurchases} call to Google Play. Your application can make a consumption request by sending a {@code consumePurchase} call. In the request argument, you must specify the in-app product's unique {@code purchaseToken} String that you obtained from Google Play when it was purchased. Google Play returns a status code indicating if the consumption was recorded successfully.

    + +

    + To retrieve the list of product's owned by the user, your application sends a + {@code getPurchases} call to Google Play. Your application can make a + consumption request by sending a {@code consumePurchase} call. In the request + argument, you must specify the in-app product's unique {@code purchaseToken} + String that you obtained from Google Play when it was purchased. Google Play + returns a status code indicating if the consumption was recorded + successfully. +

    Non-consumable and Consumable In-app Products

    -

    It's up to you to decide if you want to handle your in-app products as non-consumable or consumable items.

    + +

    + It's up to you to decide if you want to handle your in-app products as + non-consumable or consumable items. +

    +
    -
    Non-consumable Items
    -
    Typically, you would not implement consumption for in-app products that can only be purchased once in your application and provide a permanent benefit. Once purchased, these items will be permanently associated to the user's Google account. An example of a non-consumable in-app product is a premium upgrade or a level pack.
    -
    Consumable items
    -
    In contrast, you can implement consumption for items that can be made available for purchase multiple times. Typically, these items provide certain temporary effects. For example, the user's in-game character might gain life points or gain extra gold coins in their inventory. Dispensing the benefits or effects of the purchased item in your application is called provisioning the in-app product. You are responsible for controlling and tracking how in-app products are provisioned to the users. -

    Important: Before provisioning the consumable in-app product in your application, you must send a consumption request to Google Play and receive a successful response indicating that the consumption was recorded.

    -
    +
    + Non-consumable Items +
    + +
    + Typically, you would not implement consumption for in-app products that can + only be purchased once in your application and provide a permanent benefit. + Once purchased, these items will be permanently associated to the user's + Google account. An example of a non-consumable in-app product is a premium + upgrade or a level pack. +
    + +
    + Consumable items +
    + +
    + In contrast, you can implement consumption for items that can be made + available for purchase multiple times. Typically, these items provide + certain temporary effects. For example, the user's in-game character might + gain life points or gain extra gold coins in their inventory. Dispensing + the benefits or effects of the purchased item in your application is called + provisioning the in-app product. You are responsible for + controlling and tracking how in-app products are provisioned to the users. +

    + Important: Before provisioning the consumable in-app + product in your application, you must send a consumption request to + Google Play and receive a successful response indicating that the + consumption was recorded. +

    +
    +

    Managing consumable purchases in your application

    Here is the basic flow for purchasing a consumable in-app product:

    +
      -
    1. Launch a purchase flow with a {@code getBuyIntent} call
    2. -
    3. Get a response {@code Bundle}from Google Play indicating if the purchase completed successfully.
    4. -
    5. If the purchase was successful, consume the purchase by making a {@code consumePurchase} call.
    6. -
    7. Get a response code from Google Play indicating if the consumption completed successfully.
    8. -
    9. If the consumption was successful, provision the product in your application.
    10. +
    11. Launch a purchase flow with a {@code getBuyIntent} call +
    12. + +
    13. Get a response {@code Bundle}from Google Play indicating if the purchase + completed successfully. +
    14. + +
    15. If the purchase was successful, consume the purchase by making a {@code + consumePurchase} call. +
    16. + +
    17. Get a response code from Google Play indicating if the consumption + completed successfully. +
    18. + +
    19. If the consumption was successful, provision the product in your + application. +
    -

    Subsequently, when the user starts up or logs in to your application, you should check if the user owns any outstanding consumable in-app products; if so, make sure to consume and provision those items. Here's the recommended application startup flow if you implement consumable in-app products in your application:

    + +

    + Subsequently, when the user starts up or logs in to your application, you + should check if the user owns any outstanding consumable in-app products; if + so, make sure to consume and provision those items. Here's the recommended + application startup flow if you implement consumable in-app products in your + application: +

    +
      -
    1. Send a {@code getPurchases} request to query the owned in-app products for the user.
    2. -
    3. If there are any consumable in-app products, consume the items by calling {@code consumePurchase}. This step is necessary because the application might have completed the purchase order for the consumable item, but stopped or got disconnected before the application had the chance to send a consumption request.
    4. -
    5. Get a response code from Google Play indicating if the consumption completed successfully.
    6. -
    7. If the consumption was successful, provision the product in your application.
    8. +
    9. Send a {@code getPurchases} request to query the owned in-app products + for the user. +
    10. + +
    11. If there are any consumable in-app products, consume the items by calling + {@code consumePurchase}. This step is necessary because the application might + have completed the purchase order for the consumable item, but stopped or got + disconnected before the application had the chance to send a consumption + request. +
    12. + +
    13. Get a response code from Google Play indicating if the consumption + completed successfully. +
    14. + +
    15. If the consumption was successful, provision the product in your + application. +

    Local Caching

    -

    Because the Google Play client now caches In-app Billing information locally on the device, you can use the Version 3 API to query for this information more frequently, for example through a {@code getPurchases} call. Unlike with previous versions of the API, many Version 3 API calls will be serviced through cache lookups instead of through a network connection to Google Play, which significantly speeds up the API's response time.

    - +

    + Because the Google Play client now caches In-app Billing information locally + on the device, you can use the Version 3 API to query for this information + more frequently, for example through a {@code getPurchases} call. Unlike with + previous versions of the API, many Version 3 API calls will be serviced + through cache lookups instead of through a network connection to Google Play, + which significantly speeds up the API's response time. +

    diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd index 009495b8aef1b9f7bd431d92b75c8e6c7138b6cf..ff7acc92b7d15ddc913cf0331c48c1cf58f3e7d3 100644 --- a/docs/html/google/play/billing/billing_admin.jd +++ b/docs/html/google/play/billing/billing_admin.jd @@ -130,9 +130,10 @@ number of in-app items.

    a product ID.

  • Product Type -

    The product type can be Managed per user account, Unmanaged, - or Subscription. You can never change an item's product type after you set it. For more - information, see Choosing a product type later in this +

    The product type can be Managed per user account, + Unmanaged, or Subscription. You can never + change an item's product type after you set it. For more information, see + Choosing a product type later in this document.

  • Publishing State @@ -154,14 +155,15 @@ number of in-app items.

    application.

  • Title -

    The title is a short descriptor for the item. For example, "Sleeping potion." Titles must be - unique across an application's namespace. Every item must have a title. The title is visible to +

    The title is a short descriptor for the item. For example, "Sleeping potion." + Every item must have a title. The title is visible to users during checkout. For optimum appearance, titles should be no longer than 25 characters; however, titles can be up to 55 characters in length.

  • Description

    The description is a long descriptor for the item. For example, "Instantly puts creatures to - sleep. Does not work on angry elves." Every item must have a description. Descriptions can be up to 80 characters in length.

    + sleep. Does not work on angry elves." Every item must have a description. Descriptions can be + up to 80 characters in length.

  • Price

    You must provide a default price in your home currency. You can also provide prices in other @@ -171,22 +173,25 @@ number of in-app items.

    To specify prices in other currencies, you can manually enter the price for each currency or you can click Auto Fill and let Google Play do a one-time conversion from your home currency to the currencies you are targeting (see figure 3).

    -

    For subscription items, note that you can not change the item's price once you have published it.

    +

    For subscription items, note that you can not change the item's price once you have published + it.

  • - +

    Figure 3. Specifying additional currencies for an in-app product.

    For more information about product IDs and product lists, see Creating In-App Product -IDs. For more information about pricing, see In-App Billing -Pricing.

    +href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1072599"> +Creating In-App Product IDs. For more information about pricing, see +In-App Billing Pricing.

    Note: Be sure to plan your product ID namespace. You cannot reuse or modify product IDs after you save them.

    @@ -196,14 +201,15 @@ or modify product IDs after you save them.

    To add a batch of items to a product list using a CSV file, you first need to create your CSV file. The data values that you specify in the CSV file represent the same data values you specify manually through the In-app Products UI (see Adding items one at a time -to a product list). +to a product list).

    If you are importing and exporting CSV files with in-app products, please keep tax-inclusive pricing in mind. If you use auto-fill, you can provide a tax-exclusive default price and tax-inclusive prices will be auto-filled. If you do not use auto-fill, prices you provide must include tax.

    -

    Note: Batch upload of product lists containing subscriptions is not yet supported.

    +

    Note: Batch upload of product lists containing +subscriptions is not yet supported.

    The CSV file uses commas (,) and semi-colons (;) to separate data values. Commas are used to separate primary data values, and semi-colons are used to separate subvalues. For @@ -287,7 +293,8 @@ example, the syntax for the CSV file is as follows:

  • price

    This is equivalent to the Price in the In-app Products UI. The price must be specified in - micro-units. To convert a currency value to micro-units, you multiply the real value by 1,000,000. + micro-units. To convert a currency value to micro-units, you multiply the real value by + 1,000,000. For example, if you want to sell an in-app item for $1.99 you specify 1990000 in the price field.

  • @@ -413,8 +420,8 @@ refund notification from Google payments, and Google Play sends a refund message application. For more information, see Handling IN_APP_NOTIFY messages and In-app Billing -Pricing.

    +href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=1153485"> +In-app Billing Pricing.

    Important: You cannot use the API to issue refunds or cancel In-app Billing transactions. You must do this manually through your Google @@ -442,7 +449,7 @@ Merchant Order Number (rather than a Google Order Number) and reports the Mercha Order Number as the value of orderID. Here's an example:

    -
    "orderId" : "12999556515565155651.5565135565155651"
    +
    "orderId" : "GPA.1234-5678-9012-34567"

    For transactions dated previous to 5 December 2012, Google checkout assigned a Google Order Number and reported that number as the value of @@ -453,10 +460,11 @@ Google Order Number:

    Setting Up Test Accounts

    -

    The Google Play Developer Console lets you set up one or more test accounts. A test account is a -regular Google account that you register on the Developer Console as a test account. Test accounts are -authorized to make in-app purchases from applications that you have uploaded to the Google Play -Developer Console but have not yet published.

    +

    The Google Play Developer Console lets you set up one or more test accounts. +A test account is a regular Google account that you register on the Developer +Console as a test account. Test accounts are authorized to make in-app purchases +from applications that you have uploaded to the Google Play Developer Console +but have not yet published.

    You can use any Google account as a test account. Test accounts are useful if you want to let multiple people test In-app Billing on applications without giving them access to your publisher @@ -547,6 +555,3 @@ project issue tracker

    For general information about how to post to the groups listed above, see Developer Forums document in the Resources tab.

    - - - diff --git a/docs/html/google/play/billing/billing_best_practices.jd b/docs/html/google/play/billing/billing_best_practices.jd index 015e7c3d63c254ac15bbb8a6e8926cbc32979efc..70084b8abbcd0c7ce6658d3b4d0791d0dcf83758 100644 --- a/docs/html/google/play/billing/billing_best_practices.jd +++ b/docs/html/google/play/billing/billing_best_practices.jd @@ -72,7 +72,7 @@ attacks that can compromise your In-app Billing implementation.

    line to your Proguard configuration file:

    -keep class com.android.vending.billing.**

    - +

    Modify all sample application code

    The In-app Billing sample application is publicly distributed and can be downloaded by anyone, which means it is relatively easy for an attacker to reverse engineer your application if you use @@ -90,16 +90,33 @@ replay attacks.

    nonces on the server.

    Set the developer payload string when making purchase requests

    -

    With the In-app Billing Version 3 API, you can include a 'developer payload' string token when sending your purchase request to Google Play. Typically, this is used to pass in a string token that uniquely identifies this purchase request. If you specify a string value, Google Play returns this string along with the purchase response. Subsequently, when you make queries about this purchase, Google Play returns this string together with the purchase details.

    -

    You should pass in a string token that helps your application to identify the user who made the purchase, so that you can later verify that this is a legitimate purchase by that user. For consumable items, you can use a randomly generated string, but for non-consumable items you should use a string that uniquely identifies the user.

    -

    When you get back the response from Google Play, make sure to verify that the developer payload string matches the token that you sent previously with the purchase request. As a further security precaution, you should perform the verification on your own secure server.

    +

    With the In-app Billing Version 3 API, you can include a 'developer payload' +string token when sending your purchase request to Google Play. Typically, this +is used to pass in a string token that uniquely identifies this purchase request. +If you specify a string value, Google Play returns this string along with the +purchase response. Subsequently, when you make queries about this purchase, +Google Play returns this string together with the purchase details.

    +

    You should pass in a string token that helps your application to identify the user who +made the purchase, so that you can later verify that this is a legitimate purchase by +that user. For consumable items, you can use a randomly generated string, but for non- +consumable items you should use a string that uniquely identifies the user.

    + +

    + Note: Do not use the user's + email address in the payload string, since that address may change. +

    + +

    When you get back the response from Google Play, make sure to verify that the +developer payload string matches the token that you sent previously with the purchase +request. As a further security precaution, you should perform the verification on your +own secure server.

    Take action against trademark and copyright infringement

    If you see your content being redistributed on Google Play, act quickly and decisively. File a -trademark notice -of infringement or a copyright notice of -infringement.

    + +trademark notice of infringement or a +copyright notice of infringement.

    Implement a revocability scheme for unlocked content

    If you are using a remote server to deliver or manage content, have your application verify the @@ -112,4 +129,3 @@ literal string. Instead, construct the string at runtime from pieces or use bit example, XOR with some other string) to hide the actual key. The key itself is not secret information, but you do not want to make it easy for a hacker or malicious user to replace the public key with another key.

    - diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd old mode 100644 new mode 100755 index 52973a85f052cd0386bc5b5e12fdbb52a5afe6cb..c658f702dce3f13f0ccac87d0867baf59b08cfaf --- a/docs/html/google/play/billing/billing_integrate.jd +++ b/docs/html/google/play/billing/billing_integrate.jd @@ -16,8 +16,8 @@ page.tags="inapp, billing, iap"
  • Querying Items Available for Purchase
  • Purchasing an Item
  • Querying Purchased Items
  • -
  • Consuming a Purchase
  • -
  • Implementing Subscriptions
  • +
  • Consuming a Purchase
  • +
  • Implementing Subscriptions
  • Securing Your App @@ -38,15 +38,35 @@ page.tags="inapp, billing, iap"
  • -

    In-app Billing on Google Play provides a straightforward, simple interface for sending In-app Billing requests and managing In-app Billing transactions using Google Play. The information below covers the basics of how to make calls from your application to the In-app Billing service using the Version 3 API.

    +

    + In-app Billing on Google Play provides a straightforward, simple interface + for sending In-app Billing requests and managing In-app Billing transactions + using Google Play. The information below covers the basics of how to make + calls from your application to the In-app Billing service using the Version 3 + API. +

    -

    Note: To see a complete implementation and learn how to test your application, see the Selling In-app Products training class. The training class provides a complete sample In-app Billing application, including convenience classes to handle key tasks related to setting up your connection, sending billing requests and processing responses from Google Play, and managing background threading so that you can make In-app Billing calls from your main activity.

    +

    + Note: To see a complete implementation and learn how to test + your application, see the Selling In-app Products + training class. The training class provides a complete sample In-app Billing + application, including convenience classes to handle key tasks related to + setting up your connection, sending billing requests and processing responses + from Google Play, and managing background threading so that you can make + In-app Billing calls from your main activity. +

    -

    Before you start, be sure that you read the In-app Billing Overview to familiarize yourself with -concepts that will make it easier for you to implement In-app Billing.

    +

    + Before you start, be sure that you read the In-app Billing + Overview to familiarize yourself with concepts that will make it easier + for you to implement In-app Billing. +

    To implement In-app Billing in your application, you need to do the following:

    +
    1. Add the In-app Billing library to your project.
    2. Update your {@code AndroidManifest.xml} file.
    3. @@ -70,51 +90,81 @@ method calls.

    4. Select Google Play Billing Library.
    5. Click Install packages to complete the download.
    -

    The {@code IInAppBillingService.aidl} file will be installed to {@code /extras/google/play_billing/}.

    +

    The {@code IInAppBillingService.aidl} file will be installed to {@code <sdk>/extras/google/play_billing/}.

    To add the AIDL to your project:

    +
      -
    1. Copy the {@code IInAppBillingService.aidl} file to your Android project. -
        -
      • If you are using Eclipse: -
          -
        1. If you are starting from an existing Android project, open the project -in Eclipse. If you are creating a new Android project from scratch, click -File > New > Android Application -Project, then follow the instructions in the New Android -Application wizard to create a new project in your workspace.
        2. -
        3. In the {@code /src} directory, click File > -New > Package, then create a package named {@code com.android.vending.billing}.
        4. -
        5. Copy the {@code IInAppBillingService.aidl} file from {@code /extras/google/play_billing/} and paste it into the {@code src/com.android.vending.billing/} -folder in your workspace.
        6. -
        +
      • First, download the Google Play Billing Library to your Android project: +
          +
        1. Select Tools > Android > SDK Manager.
        2. +
        3. Under Appearance & Behavior > System Settings > Android SDK, + select the SDK Tools tab to select and download Google Play Billing + Library.
        + +
      • Next, copy the {@code IInAppBillingService.aidl} file to your project. +
          +
        • If you are using Android Studio: +
            +
          1. Navigate to {@code src/main} in the Project tool window.
          2. + +
          3. Select File > New > Directory and enter {@code aidl} in the + New Directory window, then select OK. + +
          4. Select File > New > Package and enter + {@code com.android.vending.billing} in the New Package window, then select + OK.
          5. + +
          6. Using your operating system file explorer, navigate to + {@code <sdk>/extras/google/play_billing/}, copy the + {@code IInAppBillingService.aidl} file, and paste it into the + {@code com.android.vending.billing} package in your project. +
          7. +
          +
        • + +
        • If you are developing in a non-Android Studio environment: Create the + following directory {@code /src/com/android/vending/billing} and copy the + {@code IInAppBillingService.aidl} file into this directory. Put the AIDL + file into your project and use the Gradle tool to build your project so that + the IInAppBillingService.java file gets generated. +
        • +
      • -
      • If you are developing in a non-Eclipse environment: Create the following -directory {@code /src/com/android/vending/billing} and copy the -{@code IInAppBillingService.aidl} file into this directory. Put the AIDL file -into your project and use the Ant tool to build your project so that the -IInAppBillingService.java file gets generated.
      • -
      -
    2. -
    3. Build your application. You should see a generated file named -{@code IInAppBillingService.java} in the {@code /gen} directory of your -project.
    4. -
    +
  • Build your application. You should see a generated file named {@code + IInAppBillingService.java} in the {@code /gen} directory of your project. +
  • +

    Updating Your Application's Manifest

    -

    In-app billing relies on the Google Play application, which handles all communication between your application and the Google Play server. To use the Google Play application, your application must request the proper permission. You can do this by adding the {@code com.android.vending.BILLING} permission to your AndroidManifest.xml file. If your application does not declare the In-app Billing permission, but attempts to send billing requests, Google Play will refuse the requests and respond with an error.

    +

    + In-app billing relies on the Google Play application, which handles all + communication between your application and the Google Play server. To use the + Google Play application, your application must request the proper permission. + You can do this by adding the {@code com.android.vending.BILLING} permission + to your AndroidManifest.xml file. If your application does not declare the + In-app Billing permission, but attempts to send billing requests, Google Play + will refuse the requests and respond with an error. +

    + +

    + To give your app the necessary permission, add this line in your {@code + Android.xml} manifest file: +

    -

    To give your app the necessary permission, add this line in your {@code Android.xml} manifest file:

     <uses-permission android:name="com.android.vending.BILLING" />
     

    Creating a ServiceConnection

    -

    Your application must have a {@link android.content.ServiceConnection} to facilitate messaging between -your application and Google Play. At a minimum, your application must do the following:

    +

    + Your application must have a {@link android.content.ServiceConnection} to + facilitate messaging between your application and Google Play. At a minimum, + your application must do the following: +

    • Bind to {@code IInAppBillingService}. @@ -123,8 +173,18 @@ your application and Google Play. At a minimum, your application must do the fol

    Binding to IInAppBillingService

    -

    To establish a connection with the In-app Billing service on Google Play, implement a {@link android.content.ServiceConnection} to bind your activity to {@code IInAppBillingService}. Override the {@link android.content.ServiceConnection#onServiceDisconnected onServiceDisconnected} and {@link -android.content.ServiceConnection#onServiceConnected onServiceConnected} methods to get a reference to the {@code IInAppBillingService} instance after a connection has been established.

    + +

    + To establish a connection with the In-app Billing service on Google Play, + implement a {@link android.content.ServiceConnection} to bind your activity + to {@code IInAppBillingService}. Override the {@link + android.content.ServiceConnection#onServiceDisconnected + onServiceDisconnected} and {@link + android.content.ServiceConnection#onServiceConnected onServiceConnected} + methods to get a reference to the {@code IInAppBillingService} instance after + a connection has been established. +

    +
     IInAppBillingService mService;
     
    @@ -142,20 +202,52 @@ ServiceConnection mServiceConn = new ServiceConnection() {
     };
     
    -

    In your activity’s {@link android.app.Activity#onCreate onCreate} method, perform the binding by calling the {@link android.content.Context#bindService bindService} method. Pass the method an {@link android.content.Intent} that references the In-app Billing service and an instance of the {@link android.content.ServiceConnection} that you created, and explicitly set the Intent's target package name to com.android.vending — the package name of Google Play app.

    +

    + In your activity’s {@link android.app.Activity#onCreate onCreate} method, + perform the binding by calling the {@link android.content.Context#bindService + bindService} method. Pass the method an {@link android.content.Intent} that + references the In-app Billing service and an instance of the {@link + android.content.ServiceConnection} that you created, and explicitly set the + Intent's target package name to com.android.vending — the + package name of Google Play app. +

    -

    Caution: To protect the security of billing transactions, always make sure to explicitly set the intent's target package name to com.android.vending, using {@link android.content.Intent#setPackage(java.lang.String) setPackage()} as shown in the example below. Setting the package name explicitly ensures that only the Google Play app can handle billing requests from your app, preventing other apps from intercepting those requests.

    +

    + Caution: To protect the security of billing transactions, + always make sure to explicitly set the intent's target package name to + com.android.vending, using {@link + android.content.Intent#setPackage(java.lang.String) setPackage()} as shown in + the example below. Setting the package name explicitly ensures that + only the Google Play app can handle billing requests from your app, + preventing other apps from intercepting those requests. +

    @Override
     public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
    -  Intent serviceIntent = new Intent("com.android.vending.billing.InAppBillingService.BIND");
    +  Intent serviceIntent =
    +      new Intent("com.android.vending.billing.InAppBillingService.BIND");
       serviceIntent.setPackage("com.android.vending");
       bindService(serviceIntent, mServiceConn, Context.BIND_AUTO_CREATE);
    +}
     
    -

    You can now use the mService reference to communicate with the Google Play service.

    -

    Important: Remember to unbind from the In-app Billing service when you are done with your {@link android.app.Activity}. If you don’t unbind, the open service connection could cause your device’s performance to degrade. This example shows how to perform the unbind operation on a service connection to In-app Billing called {@code mServiceConn} by overriding the activity’s {@link android.app.Activity#onDestroy onDestroy} method.

    + +

    + You can now use the mService reference to communicate with the Google Play + service. +

    + +

    + Important: Remember to unbind from the In-app Billing + service when you are done with your {@link android.app.Activity}. If you + don’t unbind, the open service connection could cause your device’s + performance to degrade. This example shows how to perform the unbind + operation on a service connection to In-app Billing called {@code + mServiceConn} by overriding the activity’s {@link + android.app.Activity#onDestroy onDestroy} method. +

    +
     @Override
     public void onDestroy() {
    @@ -166,16 +258,37 @@ public void onDestroy() {
     }
     
    -

    For a complete implementation of a service connection that binds to the {@code IInAppBillingService}, -see the Selling In-app -Products training class and associated sample.

    - +

    + For a complete implementation of a service connection that binds to the + {@code IInAppBillingService}, see the Selling In-app + Products training class and associated sample. +

    Making In-app Billing Requests

    -

    Once your application is connected to Google Play, you can initiate purchase requests for in-app products. Google Play provides a checkout interface for users to enter their payment method, so your application does not need to handle payment transactions directly. When an item is purchased, Google Play recognizes that the user has ownership of that item and prevents the user from purchasing another item with the same product ID until it is consumed. You can control how the item is consumed in your application, and notify Google Play to make the item available for purchase again. You can also query Google Play to quickly retrieve the list of purchases that were made by the user. This is useful, for example, when you want to restore the user's purchases when your user launches your app. +

    + Once your application is connected to Google Play, you can initiate purchase + requests for in-app products. Google Play provides a checkout interface for + users to enter their payment method, so your application does not need to + handle payment transactions directly. When an item is purchased, Google Play + recognizes that the user has ownership of that item and prevents the user + from purchasing another item with the same product ID until it is consumed. + You can control how the item is consumed in your application, and notify + Google Play to make the item available for purchase again. You can also query + Google Play to quickly retrieve the list of purchases that were made by the + user. This is useful, for example, when you want to restore the user's + purchases when your user launches your app.

    Querying for Items Available for Purchase

    -

    In your application, you can query the item details from Google Play using the In-app Billing Version 3 API. To pass a request to the In-app Billing service, first create a {@link android.os.Bundle} that contains a String {@link java.util.ArrayList} of product IDs with key "ITEM_ID_LIST", where each string is a product ID for an purchasable item.

    + +

    + In your application, you can query the item details from Google Play using + the In-app Billing Version 3 API. To pass a request to the In-app Billing + service, first create a {@link android.os.Bundle} that contains a String + {@link java.util.ArrayList} of product IDs with key "ITEM_ID_LIST", where + each string is a product ID for an purchasable item. +

    +
     ArrayList<String> skuList = new ArrayList<String> ();
     skuList.add("premiumUpgrade");
    @@ -183,19 +296,51 @@ skuList.add("gas");
     Bundle querySkus = new Bundle();
     querySkus.putStringArrayList(“ITEM_ID_LIST”, skuList);
     
    -

    To retrieve this information from Google Play, call the {@code getSkuDetails} method on the In-app Billing Version 3 API, and pass the method the In-app Billing API version (“3”), the package name of your calling app, the purchase type (“inapp”), and the {@link android.os.Bundle} that you created.

    + +

    + To retrieve this information from Google Play, call the {@code getSkuDetails} + method on the In-app Billing Version 3 API, and pass the method the In-app + Billing API version (“3”), the package name of your calling app, the purchase + type (“inapp”), and the {@link android.os.Bundle} that you created. +

    +
     Bundle skuDetails = mService.getSkuDetails(3,
        getPackageName(), "inapp", querySkus);
     
    -

    If the request is successful, the returned {@link android.os.Bundle}has a response code of {@code BILLING_RESPONSE_RESULT_OK} (0).

    -

    Warning: Do not call the {@code getSkuDetails} method on the main thread. Calling this method triggers a network request which could block your main thread. Instead, create a separate thread and call the {@code getSkuDetails} method from inside that thread.

    -

    To see all the possible response codes from Google Play, see In-app Billing Reference.

    +

    + If the request is successful, the returned {@link android.os.Bundle}has a + response code of {@code BILLING_RESPONSE_RESULT_OK} (0). +

    + +

    + Warning: Do not call the {@code getSkuDetails} method on the + main thread. Calling this method triggers a network request which could block + your main thread. Instead, create a separate thread and call the {@code + getSkuDetails} method from inside that thread. +

    + +

    + To see all the possible response codes from Google Play, see In-app + Billing Reference. +

    -

    The query results are stored in a String ArrayList with key {@code DETAILS_LIST}. The purchase information is stored in the String in JSON format. To see the types of product detail information that are returned, see In-app Billing Reference.

    +

    + The query results are stored in a String ArrayList with key {@code + DETAILS_LIST}. The purchase information is stored in the String in JSON + format. To see the types of product detail information that are returned, see + In-app + Billing Reference. +

    + +

    + In this example, you are retrieving the prices for your in-app items from the + skuDetails {@link android.os.Bundle} returned from the previous code snippet. +

    -

    In this example, you are retrieving the prices for your in-app items from the skuDetails {@link android.os.Bundle} returned from the previous code snippet.

     int response = skuDetails.getInt("RESPONSE_CODE");
     if (response == 0) {
    @@ -213,31 +358,69 @@ if (response == 0) {
     

    Purchasing an Item

    -

    To start a purchase request from your app, call the {@code getBuyIntent} method on the In-app Billing service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, the product ID for the item to purchase, the purchase type (“inapp” or "subs"), and a {@code developerPayload} String. The {@code developerPayload} String is used to specify any additional arguments that you want Google Play to send back along with the purchase information.

    +

    + To start a purchase request from your app, call the {@code getBuyIntent} + method on the In-app Billing service. Pass in to the method the In-app + Billing API version (“3”), the package name of your calling app, the product + ID for the item to purchase, the purchase type (“inapp” or "subs"), and a + {@code developerPayload} String. The {@code developerPayload} String is used + to specify any additional arguments that you want Google Play to send back + along with the purchase information. +

     Bundle buyIntentBundle = mService.getBuyIntent(3, getPackageName(),
        sku, "inapp", "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
     
    +

    -If the request is successful, the returned {@link android.os.Bundle} has a response code of {@code BILLING_RESPONSE_RESULT_OK} (0) and a {@link android.app.PendingIntent} that you can use to start the purchase flow. To see all the possible response codes from Google Play, see In-app Billing Reference. Next, extract a {@link android.app.PendingIntent} from the response {@link android.os.Bundle} with key {@code BUY_INTENT}. + If the request is successful, the returned {@link android.os.Bundle} has a + response code of {@code BILLING_RESPONSE_RESULT_OK} (0) and a {@link + android.app.PendingIntent} that you can use to start the purchase flow. To + see all the possible response codes from Google Play, see In-app + Billing Reference. Next, extract a {@link android.app.PendingIntent} from + the response {@link android.os.Bundle} with key {@code BUY_INTENT}.

    +
     PendingIntent pendingIntent = buyIntentBundle.getParcelable("BUY_INTENT");
     
    +

    -To complete the purchase transaction, call the {@link android.app.Activity#startIntentSenderForResult startIntentSenderForResult} method and use the {@link android.app.PendingIntent} that you created. In this example, you are using an arbitrary value of 1001 for the request code.

    + To complete the purchase transaction, call the {@link + android.app.Activity#startIntentSenderForResult startIntentSenderForResult} + method and use the {@link android.app.PendingIntent} that you created. In + this example, you are using an arbitrary value of 1001 for the request code. +

    +
     startIntentSenderForResult(pendingIntent.getIntentSender(),
        1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
        Integer.valueOf(0));
     
    -

    Google Play sends a response to your {@link android.app.PendingIntent} to the {@link android.app.Activity#onActivityResult onActivityResult} method of your application. The {@link android.app.Activity#onActivityResult onActivityResult} method will have a result code of {@code Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To see the types of order information that is returned in the response {@link android.content.Intent}, see In-app Billing Reference.

    -

    The purchase data for the order is a String in JSON format that is mapped to the {@code INAPP_PURCHASE_DATA} key in the response {@link android.content.Intent}, for example: +

    + Google Play sends a response to your {@link android.app.PendingIntent} to the + {@link android.app.Activity#onActivityResult onActivityResult} method of your + application. The {@link android.app.Activity#onActivityResult + onActivityResult} method will have a result code of {@code + Activity.RESULT_OK} (1) or {@code Activity.RESULT_CANCELED} (0). To see the + types of order information that is returned in the response {@link + android.content.Intent}, see In-app + Billing Reference. +

    + +

    + The purchase data for the order is a String in JSON format that is mapped to + the {@code INAPP_PURCHASE_DATA} key in the response {@link + android.content.Intent}, for example: +

    +
     '{
    -   "orderId":"12999763169054705758.1371079406387615",
    +   "orderId":"GPA.1234-5678-9012-34567",
        "packageName":"com.example.app",
        "productId":"exampleSku",
        "purchaseTime":1345678900000,
    @@ -246,17 +429,22 @@ startIntentSenderForResult(pendingIntent.getIntentSender(),
        "purchaseToken":"opaque-token-up-to-1000-characters"
      }'
     
    + +

    + Note: Google Play generates a token for the purchase. This + token is an opaque character sequence that may be up to 1,000 characters + long. Pass this entire token to other methods, such as when you consume the + purchase, as described in Consume + a Purchase. Do not abbreviate or truncate this token; you must save and + return the entire token.

    -

    Note: Google Play generates a token for the -purchase. This token is an opaque character sequence that may be up to 1,000 -characters long. Pass this entire token to other methods, such as when you -consume the purchase, as described in -Consume -a Purchase. Do not abbreviate or truncate this token; you must save and -return the entire token.

    +

    + Continuing from the previous example, you get the response code, purchase + data, and signature from the response {@link android.content.Intent}. +

    -

    Continuing from the previous example, you get the response code, purchase data, and signature from the response {@link android.content.Intent}.

     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    @@ -280,16 +468,59 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        }
     }
     
    -

    Security Recommendation: When you send a purchase request, create a String token that uniquely identifies this purchase request and include this token in the {@code developerPayload}.You can use a randomly generated string as the token. When you receive the purchase response from Google Play, make sure to check the returned data signature, the {@code orderId}, and the {@code developerPayload} String. For added security, you should perform the checking on your own secure server. Make sure to verify that the {@code orderId} is a unique value that you have not previously processed, and the {@code developerPayload} String matches the token that you sent previously with the purchase request.

    + +

    + Security Recommendation: When you send a purchase request, + create a String token that uniquely identifies this purchase request and + include this token in the {@code developerPayload}.You can use a randomly + generated string as the token. When you receive the purchase response from + Google Play, make sure to check the returned data signature, the {@code + orderId}, and the {@code developerPayload} String. For added security, you + should perform the checking on your own secure server. Make sure to verify + that the {@code orderId} is a unique value that you have not previously + processed, and the {@code developerPayload} String matches the token that you + sent previously with the purchase request. +

    Querying for Purchased Items

    -

    To retrieve information about purchases made by a user from your app, call the {@code getPurchases} method on the In-app Billing Version 3 service. Pass in to the method the In-app Billing API version (“3”), the package name of your calling app, and the purchase type (“inapp” or "subs").

    + +

    + To retrieve information about purchases made by a user from your app, call + the {@code getPurchases} method on the In-app Billing Version 3 service. Pass + in to the method the In-app Billing API version (“3”), the package name of + your calling app, and the purchase type (“inapp” or "subs"). +

    +
     Bundle ownedItems = mService.getPurchases(3, getPackageName(), "inapp", null);
     
    -

    The Google Play service returns only the purchases made by the user account that is currently logged in to the device. If the request is successful, the returned {@link android.os.Bundle} has a response code of 0. The response {@link android.os.Bundle} also contains a list of the product IDs, a list of the order details for each purchase, and the signatures for each purchase.

    -

    To improve performance, the In-app Billing service returns only up to 700 products that are owned by the user when {@code getPurchase} is first called. If the user owns a large number of products, Google Play includes a String token mapped to the key {@code INAPP_CONTINUATION_TOKEN} in the response {@link android.os.Bundle} to indicate that more products can be retrieved. Your application can then make a subsequent {@code getPurchases} call, and pass in this token as an argument. Google Play continues to return a continuation token in the response {@link android.os.Bundle} until all products that are owned by the user has been sent to your app.

    -

    For more information about the data returned by {@code getPurchases}, see In-app Billing Reference. The following example shows how you can retrieve this data from the response. + +

    + The Google Play service returns only the purchases made by the user account + that is currently logged in to the device. If the request is successful, the + returned {@link android.os.Bundle} has a response code of 0. The response + {@link android.os.Bundle} also contains a list of the product IDs, a list of + the order details for each purchase, and the signatures for each purchase. +

    + +

    + To improve performance, the In-app Billing service returns only up to 700 + products that are owned by the user when {@code getPurchase} is first called. + If the user owns a large number of products, Google Play includes a String + token mapped to the key {@code INAPP_CONTINUATION_TOKEN} in the response + {@link android.os.Bundle} to indicate that more products can be retrieved. + Your application can then make a subsequent {@code getPurchases} call, and + pass in this token as an argument. Google Play continues to return a + continuation token in the response {@link android.os.Bundle} until all + products that are owned by the user has been sent to your app. +

    + +

    For more information about the data returned by {@code getPurchases}, see + + In-app Billing Reference. The following example shows how you can + retrieve this data from the response. +

    +
     int response = ownedItems.getInt("RESPONSE_CODE");
     if (response == 0) {
    @@ -302,7 +533,7 @@ if (response == 0) {
        String continuationToken =
           ownedItems.getString("INAPP_CONTINUATION_TOKEN");
     
    -   for (int i = 0; i < purchaseDataList.size(); ++i) {
    +   for (int i = 0; i < purchaseDataList.size(); ++i) {
           String purchaseData = purchaseDataList.get(i);
           String signature = signatureList.get(i);
           String sku = ownedSkus.get(i);
    @@ -314,43 +545,76 @@ if (response == 0) {
        // if continuationToken != null, call getPurchases again
        // and pass in the token to retrieve more items
     }
    -
     
    +

    Consuming a Purchase

    -

    You can use the In-app Billing Version 3 API to track the ownership of -purchased in-app products in Google Play. Once an in-app product is purchased, -it is considered to be "owned" and cannot be purchased from Google Play. You -must send a consumption request for the in-app product before Google Play makes -it available for purchase again.

    -

    Important: Managed in-app products are -consumable, but subscriptions are not.

    -

    How you use the consumption mechanism in your app is up to you. Typically, -you would implement consumption for in-app products with temporary benefits that -users may want to purchase multiple times (for example, in-game currency or -equipment). You would typically not want to implement consumption for in-app -products that are purchased once and provide a permanent effect (for example, -a premium upgrade).

    -

    To record a purchase consumption, send the {@code consumePurchase} method to -the In-app Billing service and pass in the {@code purchaseToken} String value -that identifies the purchase to be removed. The {@code purchaseToken} is part -of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google -Play service following a successful purchase request. In this example, you are -recording the consumption of a product that is identified with the -{@code purchaseToken} in the {@code token} variable.

    + +

    + You can use the In-app Billing Version 3 API to track the ownership of + purchased in-app products in Google Play. Once an in-app product is + purchased, it is considered to be "owned" and cannot be purchased from Google + Play. You must send a consumption request for the in-app product before + Google Play makes it available for purchase again. +

    + +

    + Important: Managed in-app products are consumable, but + subscriptions are not. +

    + +

    + How you use the consumption mechanism in your app is up to you. Typically, + you would implement consumption for in-app products with temporary benefits + that users may want to purchase multiple times (for example, in-game currency + or equipment). You would typically not want to implement consumption for + in-app products that are purchased once and provide a permanent effect (for + example, a premium upgrade). +

    + +

    + To record a purchase consumption, send the {@code consumePurchase} method to + the In-app Billing service and pass in the {@code purchaseToken} String value + that identifies the purchase to be removed. The {@code purchaseToken} is part + of the data returned in the {@code INAPP_PURCHASE_DATA} String by the Google + Play service following a successful purchase request. In this example, you + are recording the consumption of a product that is identified with the {@code + purchaseToken} in the {@code token} variable. +

    +
     int response = mService.consumePurchase(3, getPackageName(), token);
     
    -

    Warning: Do not call the {@code consumePurchase} method on the main thread. Calling this method triggers a network request which could block your main thread. Instead, create a separate thread and call the {@code consumePurchase} method from inside that thread.

    -

    It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.

    -

    Security Recommendation: You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.

    + +

    + Warning: Do not call the {@code consumePurchase} method on + the main thread. Calling this method triggers a network request which could + block your main thread. Instead, create a separate thread and call the {@code + consumePurchase} method from inside that thread. +

    + +

    + It's your responsibility to control and track how the in-app product is + provisioned to the user. For example, if the user purchased in-game currency, + you should update the player's inventory with the amount of currency + purchased. +

    + +

    + Security Recommendation: You must send a consumption request + before provisioning the benefit of the consumable in-app purchase to the + user. Make sure that you have received a successful consumption response from + Google Play before you provision the item. +

    Implementing Subscriptions

    +

    Launching a purchase flow for a subscription is similar to launching the purchase flow for a product, with the exception that the product type must be set to "subs". The purchase result is delivered to your Activity's {@link android.app.Activity#onActivityResult onActivityResult} method, exactly as in the case of in-app products.

    +
     Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
        MY_SKU, "subs", developerPayload);
    @@ -363,12 +627,15 @@ if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
            Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
     }
     
    +

    To query for active subscriptions, use the {@code getPurchases} method, again with the product type parameter set to "subs".

    +
     Bundle activeSubs = mService.getPurchases(3, "com.example.myapp",
                        "subs", continueToken);
     
    +

    The call returns a {@code Bundle} with all the active subscriptions owned by the user. Once a subscription expires without renewal, it will no longer appear in the returned {@code Bundle}.

    @@ -383,7 +650,7 @@ Developer Console generates an RSA key pair for each application.

    Note:To find the public key portion of this key pair, open your application's details in the Developer Console, then click on -Services & APIs, and look at the field titled +Services & APIs, and look at the field titled Your License Key for This Application.

    The Base64-encoded RSA public key generated by Google Play is in binary @@ -399,13 +666,5 @@ to a secure remote server then we recommend that you perform the signature verification on that server.

    For more information about best practices for security and design, see Security and Design.

    - - - - - - - - - +href="{@docRoot}google/play/billing/billing_best_practices.html">Security and +Design.

    diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd index 732e3280225a44b3037d6bf736769b58f9760fa7..cc564689b5cd5227d8ea9db43715dd7d18048ff7 100644 --- a/docs/html/google/play/billing/billing_overview.jd +++ b/docs/html/google/play/billing/billing_overview.jd @@ -29,7 +29,13 @@ same look-and-feel as for app purchases.

    Related Samples

      -
    1. Sample Application (V3)
    2. +
    3. Sample + Application (V3)
    4. +
    +

    Related Videos

    +
      +
    1. Implementing + Freemium
    @@ -40,12 +46,13 @@ Billing features into your application.

    Note: Ensure that you comply with applicable laws in the countries where you distribute apps. For example, in EU countries, laws based on the -Unfair -Commercial Practices Directive prohibit direct exhortations to children to buy advertised + +Unfair Commercial Practices Directive prohibit direct exhortations to children to buy advertised products or to persuade their parents or other adults to buy advertised products for them. See the -position -of the EU consumer protection authorities for more information on this and other topics. + +position of the EU consumer protection authorities for more information on this and other +topics.

    In-app Billing API

    @@ -65,7 +72,9 @@ must be able to access the Google Play server over the network.

    In-app billing Version 3 is the latest version, and maintains very broad compatibility across the range of Android devices. In-app Billing Version 3 is supported on devices running Android 2.2 or higher that have the latest version -of the Google Play store installed (a vast majority of active devices).

    +of the Google Play store installed (a vast majority of active +devices).

    Version 3 features

      @@ -107,7 +116,9 @@ the Google Play Developer Console.

      You can specify these types of products for your In-app Billing application — managed in-app products and subscriptions. Google Play handles and tracks ownership for in-app products and subscriptions on your -application on a per user account basis. Learn more about the product types supported by In-app Billing Version 3.

      +application on a per user account basis. +Learn more about +the product types supported by In-app Billing Version 3.

      Google Play Developer Console

      The Developer Console is where you can publish your @@ -146,7 +157,9 @@ Google Play.

      provides a sample application that demonstrates how to sell in-app products and subscriptions from inside an app.

      -

      The TrivialDrive sample for the Version 3 API sample shows how to use the In-app Billing Version 3 API +

      The +TrivialDrive sample for the Version 3 API sample shows how to use the In-app +Billing Version 3 API to implement in-app product and subscription purchases for a driving game. The application demonstrates how to send In-app Billing requests, and handle synchronous responses from Google Play. The application also shows how to record @@ -160,7 +173,7 @@ code in your application before you publish it. For more information, see and Design.

      Migration Considerations

      -

      The In-app Billing Version 2 API is deprecated and will be discontinued in January 2015. +

      The In-app Billing Version 2 API was discontinued in January 2015. If you have an existing In-app Billing implementation that uses API Version 2 or earlier, you must migrate to In-app Billing Version 3.

      @@ -173,11 +186,5 @@ work with Version 3 as before. treated as managed products if you make a purchase request for these items using the Version 3 API. You do not need to create a new product entry in Developer Console for these items, and you can use the same product IDs to purchase these -items. They will still continue to be treated as unmanaged items if you make a -purchase request for them using the Version 2 or earlier API. +items.
    - - - - - diff --git a/docs/html/google/play/billing/billing_promotions.jd b/docs/html/google/play/billing/billing_promotions.jd new file mode 100644 index 0000000000000000000000000000000000000000..ccf50fc601c30b65b22ff7c13857360f128d25a9 --- /dev/null +++ b/docs/html/google/play/billing/billing_promotions.jd @@ -0,0 +1,301 @@ +page.title=In-app Promotions +parent.title=In-app Billing +parent.link=index.html +page.metaDescription=Support promo codes in your app, which let you give content or features away to a limited number of users free of charge. +page.image=/images/play_dev.jpg +page.tags="promotions, billing, promo codes" +meta.tags="monetization, inappbilling, promotions" +@jd:body + + + +

    + Promo codes let you give content or features away to a limited number of + users free of charge. Once you create a promo code, you can distribute it + subject to the + terms of + service. The user enters the promo code in your app or in the Play Store app, + and gets the item at no cost. You can use promo codes in many ways to + creatively engage with users. For example: +

    + +
      +
    • A game could have a special item, such as a character or decoration, + that's only available to players who attend an event. The developer could + distribute cards with promo codes at the event, and users would enter their + promo code to unlock the item. +
    • + +
    • An app developer might distribute promo codes at local businesses, to + encourage potential users to try the app. +
    • + +
    • An app developer might give out "friends and family" codes to its employees to + share with their friends. +
    • +
    + +

    + Every promo code is associated with a particular product ID (also + known as a SKU). You can create promo codes for your existing in-app + products. You can also keep a SKU off the Play Store, so the only way to get + that item is by entering that SKU's promo code. When a user enters the promo + code in the Play Store or in their app, the user gets the item, just as if + they paid full price for it. If your app already uses In-app Billing version 3 to + support in-app purchases, it's easy to add support for promo codes. +

    + +

    Creating and Redeeming Promo Codes

    + +

    + You create promo codes through the Google Play + Developer Console. Each promo code is associated with a single product item + registered in the developer console. +

    + +

    + When a user gets a promo code, they redeem it in one of two ways: +

    + +
      +
    • The user can enter the promo code as part of the app's ordinary purchase flow, as + described in + Implementing In-app Billing. As far as the app is concerned, this is + just like an ordinary purchase, except that the user makes payment with a + promo code instead of with money. +
    • + +
    • The user can redeem the code in the Google Play Store app. Once the user + enters the code, the Play Store prompts the user to open the app (if they have + the latest version installed) or to download or update it. (We do not + currently support redeeming promo codes from the Google Play web store.) +
    • +
    + +

    + If the promo code is for a consumable product, + the user can apply an additional code for the same product after the first + product is consumed. For example, a game might offer promo codes for a bundle + of extra lives. Betty has two different promo codes for that bundle. She + redeems a single promo code, then launches the game. When the game launches, + the her character receives the lives, consuming the item. She can now redeem + the second promo code for another bundle of lives. (She cannot redeem the + second promo code until after she consumes the item she purchased with the + first promo code.) +

    + +

    Supporting Promo Codes In Your App

    + +

    + To support promotion codes, your app should call the getPurchases() + method whenever the app starts or resumes. This method returns a bundle of all + current, unconsumed purchases, including purchases the user made by redeeming + a promo code. This simplest approach is to call getPurchases() + in your activity's {@link android.app.Activity#onResume onResume()} method, + since that callback fires when the activity is created, as well as when the + activity is unpaused. Calling getPurchases() + on startup and resume guarantees that your app will find out about all + purchases and redemptions the user may have made while the app wasn't + running. Furthermore, if a user makes a purchase while the app is running and + your app misses it for any reason, your app will still find out about the + purchase the next time the activity resumes and calls getPurchases(). +

    + +

    + In addition, your app should allow users to redeem promo codes inside the app + itself. If your app supports the in-app purchase workflow (described in + Making + In-app Billing Requests), your app automatically supports in-app + redemption of promo codes. When you launch the in-app purchase UI, + the user has the option to pay for the purchase with + a promo code. Your activity's {@link android.app.Activity#onActivityResult + onActivityResult()} method receives a response intent telling the app whether the + purchase was completed. However, your app should still call getPurchases() + on startup and resume, just in case the purchase and consumption workflow + didn't complete. For example, if the user successfully redeems a promo code, + and then your app crashes before the item is consumed, your app still gets + information about the purchase when the app calls getPurchases() on its next startup. +

    + +

    + Your app should also support the scenario where a user redeems a promo code + in the Play Store app while the app is running. Your app can find out right + away when the user redeems a code by registering a listener for the + PURCHASES_UPDATED intent. The Play Store fires this intent + whenever a user redeems a promo code. +

    + +

    + To listen for the PURCHASES_UPDATED intent, dynamically create a + {@link android.content.BroadcastReceiver} object and register it to listen + for "com.android.vending.billing.PURCHASES_UPDATED". Register + the receiver by putting code like this in your activity's {@link + android.app.Activity#onResume onResume()} method: +

    + +
    IntentFilter promoFilter =
    +    new IntentFilter("com.android.vending.billing.PURCHASES_UPDATED");
    +registerReceiver(myPromoReceiver, promoFilter);
    + +

    + When the user makes a purchase, the system invokes your broadcast receiver's + {@link android.content.BroadcastReceiver#onReceive onReceive()} method. That + method should call getPurchases() + to see what purchases the user has made. +

    + +

    + Your activity's {@link android.app.Activity#onPause onPause()} method should + unregister the broadcast receiver, to reduce system overhead when your app + isn't running: +

    + +
    unRegisterReceiver(myPromoReceiver);
    + +

    + Note: You should not register this broadcast receiver in the + app manifest. Declaring the receiver in the manifest can cause the system to + launch the app to handle the intent if the user makes a purchase while the app + isn't running. This behavior is not necessary, and may be annoying to the + user. Instead, your app should call getPurchases() + when the user launches it, to find out about any purchases the user made + while the app wasn't running. +

    + +

    Testing In-app Promotions

    + +

    + If your app supports in-app promotions, you should test the following use + cases. +

    + +

    User redeems promo code in the app

    + +

    + If the user redeems a promo code within the app's purchase flow, as described + in Making + In-app Billing Requests, the system invokes your activity's {@link + android.app.Activity#onActivityResult onActivityResult()} method to handle + the purchase. Verify that {@link android.app.Activity#onActivityResult + onActivityResult()} handles the purchase properly, whether the user uses cash + or a promo code. +

    + +

    User redeems promo code in the Play Store

    + +

    + If the user redeems a promo code in the Play Store, there are several + possible workflows. You should verify each one of these. +

    + +

    App is not installed

    + +

    + If the user redeems a promo code for an app that is not installed on the + device, the Play Store prompts the user to install the app. (If the app is + installed but not up-to-date, the Play Store prompts the user to update the + app.) You should test the following sequence on a device that does not + have your app installed. +

    + +
      +
    1. User redeems a promo code for the app in the Play Store. The Play Store + prompts the user to install your app. +
    2. + +
    3. User installs and launches your app. Verify that on startup, the app + calls getPurchases() + and correctly detects the purchase the user made with the promo code. +
    4. +
    + +

    App is installed, but not running

    + +

    + If the user redeems a promo code for an app that is installed on the device, + the Play Store prompts the user to switch to the app. You should test the + following sequence on a device that has your app installed but not running: +

    + +
      +
    1. User redeems a promo code for the app in the Play Store. The Play Store + prompts the user to switch to your app. +
    2. + +
    3. User launches your app. Verify that on startup, the app calls getPurchases() + and correctly detects the purchase the user made with the promo code. +
    4. +
    + +

    App is installed and running +

    + +

    + If the user redeems a promo code for an app that is currently running on the + device, the Play Store notifies the app via a PURCHASES_UPDATED + intent. You should test the following sequence: +

    + +
      +
    1. User launches the app. Verify that the app has properly registered itself to + receive the PURCHASES_UPDATED intent. +
    2. + +
    3. User launches the Play Store app and redeems a promo code for the app. The Play + Store fires a PURCHASES_UPDATED intent. Verify that your app's + {@link android.content.BroadcastReceiver#onReceive + BroadcastReceiver.onReceive()} callback fires to handle the intent. +
    4. + +
    5. Your {@link android.content.BroadcastReceiver#onReceive onReceive()} + method should respond to the intent by calling getPurchases(). Verify that it calls this method, and that + it correctly detects the purchase the user made with the promo code. +
    6. + +
    7. User switches back to your app. Verify that the user has the purchased + item. +
    8. +
    diff --git a/docs/html/google/play/billing/billing_reference.jd b/docs/html/google/play/billing/billing_reference.jd index b06d8749068ff72c12ead93be2cc13d2a5c1532b..45ec7854b59d1c4e348c874d8aaa06ec6f43fb9a 100644 --- a/docs/html/google/play/billing/billing_reference.jd +++ b/docs/html/google/play/billing/billing_reference.jd @@ -29,11 +29,17 @@ parent.link=index.html
    -

    This documentation provides technical reference information for using the In-app Billing Version 3 API.

    +

    This documentation provides technical reference information for using the +In-app Billing Version 3 API.

    Server Response Codes

    -

    The following table lists all of the server response codes that are sent from Google Play to your application. Google Play sends the response code synchronously as an integer mapped to the {@code RESPONSE_CODE} key in the response {@code Bundle}. Your application must handle all of these response codes.

    - +

    + The following table lists all of the server response codes that are sent from + Google Play to your application. Google Play sends the response code + synchronously as an integer mapped to the {@code RESPONSE_CODE} key in the + response {@code Bundle}. Your application must handle all of these response + codes. +

    Table 1. Summary of response codes for In-app Billing Version 3 API calls.

    @@ -70,7 +76,10 @@ parent.link=index.html - + @@ -90,13 +99,23 @@ parent.link=index.html
    {@code BILLING_RESPONSE_RESULT_DEVELOPER_ERROR} 5Invalid arguments provided to the API. This error can also indicate that the application was not correctly signed or properly set up for In-app Billing in Google Play, or does not have the necessary permissions in its manifestInvalid arguments provided to the API. This error can also indicate that + the application was not correctly signed or properly set up for In-app + Billing in Google Play, or does not have the necessary permissions in its + manifest
    {@code BILLING_RESPONSE_RESULT_ERROR}

    API Reference

    -

    The In-app Billing Version 3 API is defined in the {@code IInAppBillingService.aidl} file, which is included with the Version 3 sample application.

    +

    The In-app Billing Version 3 API is defined in the {@code + IInAppBillingService.aidl} file, which is included with the Version 3 + + sample application.

    The getSkuDetails() method

    -

    This method returns product details for a list of product IDs. In the response {@code Bundle} sent by Google Play, the query results are stored in a String {@code ArrayList} mapped to the {@code DETAILS_LIST} key. Each String in the details list contains product details for a single product in JSON format. The fields in the JSON string with the product details are summarized in table 2.

    - +

    + This method returns product details for a list of product IDs. In the + response {@code Bundle} sent by Google Play, the query results are stored in + a String {@code ArrayList} mapped to the {@code DETAILS_LIST} key. Each + String in the details list contains product details for a single product in + JSON format. The fields in the JSON string with the product details are + summarized in table 2. +

    -Table 2. Description of JSON fields with product item details returned from a {@code getSkuDetails} request.

    +Table 2. Description of JSON fields with product item details +returned from a {@code getSkuDetails} request.

    @@ -179,7 +198,8 @@ RSASSA-PKCS1-v1_5 scheme.
    Key

    -

    Table 4 describes the JSON fields that are returned in the response data for a purchase order.

    +

    Table 4 describes the JSON fields that are returned in the response data for +a purchase order.

    Table 4. Descriptions of the JSON fields for {@code INAPP_PURCHASE_DATA}.

    @@ -208,7 +228,9 @@ RSASSA-PKCS1-v1_5 scheme. + corresponds to the Google payments order ID. If the order is a test + purchase made through the In-app Billing Sandbox, {@code orderId} is + blank. @@ -216,7 +238,9 @@ RSASSA-PKCS1-v1_5 scheme. - + @@ -304,7 +328,11 @@ subscriptions.

    The getPurchases() method

    -

    This method returns the current un-consumed products owned by the user. Table 5 lists the response data that is returned in the {@code Bundle}.

    +

    This method returns the current un-consumed products owned by the user, + including both purchased items and items acquired by redeeming a promo code. + Table 5 lists the response data that is returned in the + {@link android.os.Bundle}.

    +

    Table 6. Response data from a {@code getPurchases} request.

    {@code orderId} A unique order identifier for the transaction. This identifier - corresponds to the Google payments order ID.
    {@code packageName}
    {@code productId}The item's product identifier. Every item has a product ID, which you must specify in the application's product list on the Google Play Developer Console.The item's product identifier. Every item has a product ID, which you + must specify in the application's product list on the Google Play + Developer Console.
    {@code purchaseTime}
    @@ -330,8 +358,13 @@ subscriptions.

    - +
    {@code INAPP_CONTINUATION_TOKEN}String containing a continuation token to retrieve the next set of in-app products owned by the user. This is only set by the Google Play service if the number of products owned by the user is very large. When a continuation token is present in the response, you must make another call to {@code getPurchases} and pass in the continuation token that you received. The subsequent {@code getPurchases} call returns more purchases and possibly another continuation token.String containing a continuation token to retrieve the next set of + in-app products owned by the user. This is only set by the Google Play + service if the number of products owned by the user is very large. When a + continuation token is present in the response, you must make another call + to {@code getPurchases} and pass in the continuation token that you + received. The subsequent {@code getPurchases} call returns more purchases + and possibly another continuation token.

    - diff --git a/docs/html/google/play/billing/billing_subscriptions.jd b/docs/html/google/play/billing/billing_subscriptions.jd index 13660b02fe578fa5112713c5fc7a0b887c79f29a..069947deba9cd1bd30d4daef47b3c94ee560d18e 100644 --- a/docs/html/google/play/billing/billing_subscriptions.jd +++ b/docs/html/google/play/billing/billing_subscriptions.jd @@ -21,8 +21,10 @@ meta.tags="monetization, inappbilling, subscriptions"

    See also

      -
    1. Implementing Subscriptions (V3)
    2. -
    3. Google Play Developer API
    4. +
    5. Implementing + Subscriptions (V3)
    6. +
    7. Google Play + Developer API
    @@ -109,11 +111,12 @@ content.

    In general the same basic policies and terms apply to subscriptions as to standard in-app products, however there are some differences. For complete information about the current policies and terms, please read the policies document.

    +href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504"> +policies document.

    To learn about the minimum system requirements for -subscriptions, see the Version Notes.

    +subscriptions, see the +Version Notes.

    Configuring Subscription Items

    @@ -167,9 +170,14 @@ automated recurring billing at your choice of intervals:

  • Monthly — Google Play bills the customer’s Google payments account at the time of purchase and monthly subsequent to the purchase date (exact billing intervals can vary slightly over time).
  • +
  • 3 Months — Google Play bills the customer’s Google payments account at + the time of purchase and every three months after that (exact billing + intervals can vary slightly over time).
  • +
  • 6 Months — Google Play bills the customer’s Google payments account at + the time of purchase and every six months after that (exact billing + intervals can vary slightly over time).
  • Annually — Google Play bills the customer's Google payments account at the time of purchase and again on the same date in subsequent years.
  • -
  • Seasonal — Google Play bills the customer's Google payments account at the beginning of each "season" (you specify the season beginning and end dates). This @@ -194,15 +202,8 @@ which can then use it to validate or cancel the subscription remotely using the href="{@docRoot}google/play/developer-api.html">Google Play Developer API.

    If a recurring payment fails (for example, because the customer’s credit -card has become invalid), the subscription does not renew. How your app is -notified depends on the In-app Billing API version that you are using:

    -
      -
    • With In-app Billing Version 3, the failed or expired subscription is no longer -returned when you call {@code getPurchases}.
    • -
    • With In-app Billing Version 2, Google Play notifies your app at the end of -the active cycle that the purchase state of the subscription is now "Expired". -
    • -
    +card has become invalid), the subscription does not renew. The {@code +getPurchases()} method does not return failed or expired subscriptions.

    Recommendation: Include business logic in your app to notify your backend servers of subscription purchases, tokens, and any @@ -312,7 +313,8 @@ date to 15 August 2015 14:00:00 UTC.

    try your subscription content before buying it. The trial period runs for the period of time that you set and then automatically converts to a full subscription managed according to the subscription's billing interval and -price. Free trials are supported for monthly and annual subscriptions only, and are not supported for seasonal subscriptions.

    +price. Google Play supports free trials for all subscription types, including +seasonal subscriptions.

    To take advantage of a free trial, a user must "purchase" the full subscription through the standard In-app Billing flow, providing a valid form of @@ -330,10 +332,10 @@ against the credit card that the user provided during the initial purchase, at the amount set for the full subscription, and continuing at the subscription interval. If necessary, the user can cancel the subscription at any time during the trial -period. In this case, Google Play marks the subscription as expired immediately, -rather than waiting until the end of the trial period. The user has not -paid for the trial period and so is not entitled to continued access after -cancellation.

    +period. In this case, the subscription remains active until the end of the +trial period, but Google Play sets the subscription not to renew +automatically; at the end of the trial period the subscription expires, and +Google Play does not charge the user.

    You can set up a trial period for a subscription in the Developer Console, without needing to modify or update your APK. Just locate and edit the @@ -369,6 +371,24 @@ products available for purchase inside the app.

    product from the product list offered in your app to prevent users from seeing or purchasing it.

    +

    Prorated Seasonal Subscription Prices

    + +

    You can set up prorated prices for users who buy seasonal subscriptions +after the season's start date. You specify the date on which the discounted +price takes effect. The discounted price takes effect at 0:00 UTC on +the specified date. You can set multiple prorated prices, dropping the +subscription price lower and lower as the season goes on. If a user purchases +a prorated seasonal subscription and remains subscribed until the start of the +next season, Google Play charges them the full subscription price when the next +season starts.

    + +

    For example, the professional checkers season runs from March 1 to August 31. +The Checkers Dilettante app offers a seasonal subscription for €10. The +app also offers two prorated prices: €7.50 for users who sign up on or after +June 1, and €5 for users who sign up on or after +August 15. Regardless of when the user signs up, the seasonal subscription ends +on August 31.

    +

    Subscription Cancellation

    Users can view the status of all of their subscriptions and cancel them if @@ -398,7 +418,8 @@ and revoke API to revoke each subscriber's subscription (one by one) and refund their subscription payments. Removing content that any subscriber is entitled to access will result in penalties. Please see the policies document for more information.

    +href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504"> +policies document for more information.

    App uninstallation

    @@ -443,8 +464,8 @@ Billing to handle the transaction and may not provide links to a purchase flow outside of the app and Google Play (such as to a web site).

    For complete details about terms and policies, see the policies -document.

    +href="http://support.google.com/googleplay/android-developer/bin/answer.py?hl=en&answer=140504"> +policies document.

    Subscription order numbers

    @@ -453,10 +474,11 @@ payments provides a base Merchant Order Number for all recurrences of the subscription and denotes each recurring transaction by appending an integer as follows:

    -

    12999556515565155651.5565135565155651 (base order number)
    -12999556515565155651.5565135565155651..0 (first recurrence orderID)
    -12999556515565155651.5565135565155651..1 (second recurrence orderID)
    -12999556515565155651.5565135565155651..2 (third recurrence orderID)
    +

    GPA.1234-5678-9012-34567 +(base order number)
    +GPA.1234-5678-9012-34567..0 (first recurrence orderID)
    +GPA.1234-5678-9012-34567..1 (second recurrence orderID)
    +GPA.1234-5678-9012-34567..2 (third recurrence orderID)
    ...

    Google Play provides the order number as the value of the diff --git a/docs/html/google/play/billing/billing_testing.jd b/docs/html/google/play/billing/billing_testing.jd index f0eb2545f992facb59227882747901c24e923f09..018276defb849cfd71d7f2e6c3557e5af7ea90a8 100644 --- a/docs/html/google/play/billing/billing_testing.jd +++ b/docs/html/google/play/billing/billing_testing.jd @@ -26,16 +26,27 @@ implementation:

    • Test purchases, which let license-test users purchase your published in-app - items, but without any actual charges to their accounts.
    • + items, without any actual charges to their accounts.
    • Static billing responses from Google Play, for testing in early development

    -

    To test In-app Billing in an application you must install the application on an Android-powered -device. You cannot use the Android emulator to test In-app Billing. The device you use for testing -must run a standard version of the Android 1.6 or later platform (API level 4 or higher), and have -the most current version of the Google Play application installed. If a device is not running the -most current Google Play application, your application won't be able to send In-app Billing -requests to Google Play. For general information about how to set up a device for use in +

    To test in-app billing, you need to publish your app to an alpha +or beta channel in Google Play.

    + +

    After you publish an app to an alpha or beta channel, it can take a few hours +for the app to be available for testers. The version code of an APK on a test device +must match the version currently uploaded to the alpha or beta channel on Google Play.

    + +

    Important: To test in-app products or make in-app +purchases in your alpha or beta channel app, each tester +needs to opt-in +to your app’s alpha or beta test. On your test’s opt-in URL, your +testers will get an explanation of what it means to be a tester and a link to opt-in.

    + +

    You can test on any Android-powered hardware device running Android 1.6 or higher. +The most current version of the Google Play application must be installed on the device. +For general information about how to set up a device for use in developing Android applications, see Using Hardware Devices.

    @@ -43,18 +54,23 @@ Devices.

    When your In-app Billing implementation is ready, you can test purchasing of your in-app SKUs in two ways:

    -
    • Test purchases, which let your selected license-test +
        +
      • Test purchases, which let your selected license-test users purchase your in-app products without any resulting charges to the user. -Test purchases can be used in alpha/beta releases or in published apps.
      • +Test purchases can be used in alpha/beta releases only.
      • Real purchases, which let regular users make real purchases -of your in-app products with actual charges to the user’s payment instruments. -You can use Google Play’s alpha and beta release groups to manage -the users who can make live purchases using your implementation.
      • +of your in-app products with actual charges to the user’s payment instruments.
      -

      The sections below provide more detail about how to use these approaches for +

      In either case, you need to publish your app to Google Play's +alpha + and beta release channels to manage the users who can make purchases.

      + +

      The sections below provide more detail about how to use these approaches for testing and validation.

      + +

      Test Purchases (In-app Billing Sandbox)

      Test purchases offer a secure, convenient way to enable larger-scale testing @@ -66,15 +82,18 @@ accounts.

      Once authorized for testing access, those users can make purchases without being charged. Test purchases are real orders and Google Play processes them in the same way as -other orders. When purchases are complete, Google Play prevents the orders from +other orders. However, the orderId field for test purchases is +blank. +When purchases are complete, Google Play prevents the orders from going to financial processing, ensuring that there are no actual charges to user -accounts, and automatically canceling the completed orders after 14 days.

      +accounts, and automatically canceling the completed orders after 14 days.

      Note: Test subscription purchases recur daily, regardless of the product's subscription period.

      +

      Setting up test purchases

      It’s easy to set up test purchases—any user account can be chosen to be @@ -83,7 +102,7 @@ available payment method (even though there’s no charge to the payment method).

      First, upload and publish in-app products that you want testers to be able to -purchase. You can upload and publish in-app products in the Developer Console. +purchase. You can upload and publish in-app products in the Developer Console. Note that you can upload and publish your in-app items before you publish the APK itself.

      @@ -97,13 +116,25 @@ href="#billing-testing-test">Setting Up for Test Purchases.

      within 15 minutes those users can begin making test purchases of your in-app products.

      -

      Note: To make test purchases, the license test -account must be on the user’s Android device. If the device has more than one +

      Important: After you publish your app +to an alpha or beta channel, your testers need to opt-in +to your app’s alpha or beta test using the provided opt-in URL before they +can make test purchases.

      + +

      Note: License test accounts +must be on the user’s Android device. If the device has more than one account, the purchase will be made with the account that downloaded the app. If none of the accounts has downloaded the app, the purchase is made with the first account. Users can confirm the account that is making a purchase by expanding the purchase dialog.

      +

      + Note: For test subscription purchases, leave the {@code orderId} + field blank. You can use the {@code purchaseToken} field to identify test purchases. +

      + +

      Test purchases and developer account

      Authorized license test accounts are associated with your developer account in Google Play, rather than with a specific APK or package name. Identifying an @@ -133,6 +164,22 @@ center, look up the transaction, and then cancel it. You can find transactions by looking up their order numbers.

    +

    + You can cancel test subscriptions purchases from the app page in the Play Store, + or use the + + {@code cancel} method. +

    + +

    + Important: The + + {@code refund} and + + {@code revoke} methods do not support test purchases. +

    + +

    Testing with real transactions

    As you prepare to launch an app that uses In-app Billing, you can make use of Google Play alpha/beta release options to do validation and load testing on your @@ -145,13 +192,19 @@ payment methods in Google Play to make purchases. Note that if you include test license accounts in your alpha and beta distribution groups, those users will only be able to make test purchases.

    +

    Important: After you publish your app +to an alpha or beta channel, your testers need to opt-in +to your app’s alpha or beta test using the provided opt-in URL before they +can make test purchases.

    +

    Testing with Static Responses

    We recommend that you first test your In-app Billing implementation using static responses from Google Play. This enables you to verify that your application is handling the primary Google -Play responses correctly and that your application is able to verify signatures correctly. You can do this -even if the app hasn't been published yet.

    +Play responses correctly and that your application is able to verify signatures correctly. You can +do this even if the app hasn't been published yet.

    To test your implementation with static responses, you make an In-app Billing request using a special item that has a reserved product ID. Each reserved product ID returns a specific static @@ -199,9 +252,11 @@ href="#draft_apps">Draft Apps are No Longer Supported. billing service. Refunds must be initiated by you (the merchant). After you process a refund request through your Google payments merchant account, a refund message is sent to your application by Google Play. This occurs only when Google Play gets notification from Google payments that - a refund has been made. For more information about refunds, see Handling -IN_APP_NOTIFY messages and In-app Billing -Pricing.

    + a refund has been made. For more information about refunds, see Handling + IN_APP_NOTIFY messages and In-app Billing Pricing.

  • android.test.item_unavailable

    When you make an In-app Billing request with this product ID, Google Play responds as diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd index ae6e22209259b8f041ceae27b685498a9349ceb8..b2e9fe4369d4466dcfbe0c3d3ade29a280276dea 100644 --- a/docs/html/google/play/billing/index.jd +++ b/docs/html/google/play/billing/index.jd @@ -14,6 +14,18 @@ and features, and more. You can use In-app Billing to sell products as

  • @@ -261,9 +262,6 @@
  • Menus
  • -
  • - Action Bar -
  • Settings
  • diff --git a/docs/html/guide/practices/app-design/seamlessness.jd b/docs/html/guide/practices/app-design/seamlessness.jd old mode 100644 new mode 100755 index ec6b7fdff73c9326a33616925930f107fa100561..16879625f6780a0c6b2f8e0e543b39c8aae61a05 --- a/docs/html/guide/practices/app-design/seamlessness.jd +++ b/docs/html/guide/practices/app-design/seamlessness.jd @@ -41,10 +41,10 @@ display the dialog in front of whatever the user was doing (such as dialing a phone call, for example). That behavior would not work for your application or for the user.

    -

    To avoid these problems, your application should use the proper system -facility for notifying the user — the +

    To avoid these problems, your application should use the proper system +facility for notifying the user — the {@link android.app.Notification Notification} classes. Using -notifications, your application can signal the user that an event has +notifications, your application can signal the user that an event has taken place, by displaying an icon in the status bar rather than taking focus and interrupting the user.

    @@ -140,7 +140,7 @@ the application is hung and offers to kill it for the user.

    on the event handler thread, effectively blocking the event handler. This will delay input processing, and result in the ANR dialogs. To avoid this, move your computations to a thread. This Design for Responsiveness document +href="responsiveness.html">Design for Responsiveness document discusses how to do that..

    Don't Overload a Single Activity Screen

    @@ -175,16 +175,16 @@ the details, read Styles and The

    Design Your UI to Work with Multiple Screen Resolutions

    -

    Different Android-powered devices will support different screen resolutions. -Some will even be able to change resolutions on the fly, such as by switching -to landscape mode. It's important to make sure your layouts and drawables +

    Different Android-powered devices will support different screen resolutions. +Some will even be able to change resolutions on the fly, such as by switching +to landscape mode. It's important to make sure your layouts and drawables are flexible enough to display properly on a variety of device screens.

    -

    Fortunately, this is very easy to do. In brief, what you must do is -provide different versions of your artwork (if you use any) for the key -resolutions, and then design your layout to accommodate various dimensions. -(For example, avoid using hard-coded positions and instead use relative -layouts.) If you do that much, the system handles the rest, and your +

    Fortunately, this is very easy to do. In brief, what you must do is +provide different versions of your artwork (if you use any) for the key +resolutions, and then design your layout to accommodate various dimensions. +(For example, avoid using hard-coded positions and instead use relative +layouts.) If you do that much, the system handles the rest, and your application looks great on any device.

    Assume the Network is Slow

    @@ -208,9 +208,9 @@ likely to be unpopular.

    you're using the emulator, since the emulator uses your desktop computer's network connection. That's almost guaranteed to be much faster than a cell network, so you'll want to change the settings on the emulator that simulate -slower network speeds. You can do this in Eclipse, in the "Emulator Settings" -tab of your launch configuration or via a
    command-line +slower network speeds. You can do this in Android Studio via the AVD Manager or +via a command-line option when starting the emulator.

    Don't Assume Touchscreen or Keyboard

    diff --git a/docs/html/guide/practices/compatibility.jd b/docs/html/guide/practices/compatibility.jd index 1bfc99d3f54d2955d007fa488cd8e760ecc8bc0d..83e841c39b7b68cc2d4ebf9b752537a379704c63 100644 --- a/docs/html/guide/practices/compatibility.jd +++ b/docs/html/guide/practices/compatibility.jd @@ -74,15 +74,12 @@ users who install your app from Google Play Store are using an Android compatibl

    However, you do need to consider whether your app is compatible with each potential -device configuration. Because Android runs on a wide range of device configurations, some features are not -available on all devices. For example, some devices may not include a +device configuration. Because Android runs on a wide range of device configurations, some features +are not available on all devices. For example, some devices may not include a compass sensor. If your app's core functionality requires the use of a compass sensor, then your app is compatible only with devices that include a compass sensor.

    - - -

    Controlling Your App's Availability to Devices

    Android supports a variety of features your app can leverage through platform APIs. Some @@ -91,7 +88,6 @@ widgets), and some are dependent on the platform version. Not every device suppo so you may need to control your app's availability to devices based on your app's required features.

    -

    To achieve the largest user-base possible for your app, you should strive to support as many device configurations as possible using a single APK. In most situations, you can do so by disabling optional features at runtime and

  • Screen configuration -

    Device features

    In order for you to manage your app’s availability based on device features, @@ -119,7 +114,7 @@ is {@link android.content.pm.PackageManager#FEATURE_APP_WIDGETS}.

    If necessary, you can prevent users from installing your app when their devices don't provide a given feature by declaring it with a {@code } +"{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>} element in your app's manifest file.

    @@ -127,11 +122,11 @@ file.

    you can declare the compass sensor as required with the following manifest tag:

    -<manifest ... >
    +<manifest ... >
         <uses-feature android:name="android.hardware.sensor.compass"
    -                  android:required="true" />
    +                  android:required="true" />
         ...
    -</manifest>
    +</manifest>
     

    Google Play Store compares the features your app requires to the features available on @@ -157,7 +152,7 @@ if (!pm.hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {

    For information about all the filters you can -use to control the availability of your app to users through Google Play Store, see the +use to control the availability of your app to users through Google Play Store, see the Filters on Google Play document.

    @@ -169,17 +164,11 @@ android.content.pm.PackageManager#FEATURE_BLUETOOTH} device feature. You can dis on this feature and make your app available to devices without Bluetooth by setting the {@code required} attribute to {@code "false"} in the {@code } tag. +"{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>} tag. For more information about implicitly required device features, read Permissions that Imply Feature Requirements.

    - - - - - -

    Platform version

    Different devices may run different versions of the Android platform, @@ -191,7 +180,9 @@ Android 1.0 is API level 1 and Android 4.4 is API level 19.

    The API level allows you to declare the minimum version with which your app is compatible, using the {@code -} manifest tag and its {@code minSdkVersion} attribute.

    +<uses-sdk>} manifest tag and its +{@code minSdkVersion} +attribute.

    For example, the Calendar Provider APIs were added in Android 4.0 (API level 14). If your app cannot function without @@ -199,10 +190,10 @@ these APIs, you should declare API level 14 as your app's minimum supported version like this:

    -<manifest ... >
    -    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
    +<manifest ... >
    +    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
         ...
    -</manifest>
    +</manifest>
     

    The {@code @@ -212,7 +203,7 @@ targetSdkVersion} attribute declares the highest version on which you've opt your app.

    Each successive version of Android provides compatibility for apps that were built using -the APIs from previous platform versions, so your app should always be compitible with future +the APIs from previous platform versions, so your app should always be compatible with future versions of Android while using the documented Android APIs.

    Note: @@ -241,18 +232,13 @@ codename constants in {@link android.os.Build.VERSION_CODES} that corresponds to API level you want to check. For example:

    -if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
    +if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
         // Running on something older than API level 11, so disable
         // the drag/drop features that use {@link android.content.ClipboardManager} APIs
         disableDragAndDrop();
     }
     
    - - - - -

    Screen configuration

    Android runs on devices of various sizes, from phones to tablets and TVs. @@ -279,13 +265,6 @@ and how to restrict your app to certain screen sizes when necessary, read Supporting Different Screens.

    - - - - - - -

    Controlling Your App's Availability for Business Reasons

    In addition to restricting your app's availability based on device characteristics, @@ -301,11 +280,6 @@ is always based on information contained within your APK file. But filtering for non-technical reasons (such as geographic locale) is always handled in the Google Play developer console.

    - - - - -

    Continue reading about:

    diff --git a/docs/html/guide/practices/seamlessness.jd b/docs/html/guide/practices/seamlessness.jd old mode 100644 new mode 100755 index 9679e2a8a2cee6a59cf58e98f6519ca71c3e17ab..db12ad2933ba59cb8ec2a6865733c16a9b94cbd6 --- a/docs/html/guide/practices/seamlessness.jd +++ b/docs/html/guide/practices/seamlessness.jd @@ -42,10 +42,10 @@ display the dialog in front of whatever the user was doing (such as dialing a phone call, for example). That behavior would not work for your application or for the user.

    -

    To avoid these problems, your application should use the proper system -facility for notifying the user — the +

    To avoid these problems, your application should use the proper system +facility for notifying the user — the {@link android.app.Notification Notification} classes. Using -notifications, your application can signal the user that an event has +notifications, your application can signal the user that an event has taken place, by displaying an icon in the status bar rather than taking focus and interrupting the user.

    @@ -141,7 +141,7 @@ the application is hung and offers to kill it for the user.

    on the event handler thread, effectively blocking the event handler. This will delay input processing, and result in the ANR dialogs. To avoid this, move your computations to a thread. This Design for Responsiveness document +href="responsiveness.html">Design for Responsiveness document discusses how to do that..

    Don't Overload a Single Activity Screen

    @@ -176,16 +176,16 @@ the details, read Styles and The

    Design Your UI to Work with Multiple Screen Resolutions

    -

    Different Android-powered devices will support different screen resolutions. -Some will even be able to change resolutions on the fly, such as by switching -to landscape mode. It's important to make sure your layouts and drawables +

    Different Android-powered devices will support different screen resolutions. +Some will even be able to change resolutions on the fly, such as by switching +to landscape mode. It's important to make sure your layouts and drawables are flexible enough to display properly on a variety of device screens.

    -

    Fortunately, this is very easy to do. In brief, what you must do is -provide different versions of your artwork (if you use any) for the key -resolutions, and then design your layout to accommodate various dimensions. -(For example, avoid using hard-coded positions and instead use relative -layouts.) If you do that much, the system handles the rest, and your +

    Fortunately, this is very easy to do. In brief, what you must do is +provide different versions of your artwork (if you use any) for the key +resolutions, and then design your layout to accommodate various dimensions. +(For example, avoid using hard-coded positions and instead use relative +layouts.) If you do that much, the system handles the rest, and your application looks great on any device.

    Assume the Network is Slow

    @@ -209,9 +209,9 @@ likely to be unpopular.

    you're using the emulator, since the emulator uses your desktop computer's network connection. That's almost guaranteed to be much faster than a cell network, so you'll want to change the settings on the emulator that simulate -slower network speeds. You can do this in Eclipse, in the "Emulator Settings" -tab of your launch configuration or via a
    command-line +slower network speeds. You can do this in Android Studio via the AVD Manager, +or via a command-line option when starting the emulator.

    Don't Assume Touchscreen or Keyboard

    diff --git a/docs/html/guide/topics/connectivity/index.jd b/docs/html/guide/topics/connectivity/index.jd index 385cf08b4a3c33942cf7c0747b84400a57b09baa..0795f79be29c31887be487e926bb21e2466e07c8 100644 --- a/docs/html/guide/topics/connectivity/index.jd +++ b/docs/html/guide/topics/connectivity/index.jd @@ -7,21 +7,9 @@ page.landing.image=images/develop/connectivity.png diff --git a/docs/html/guide/topics/connectivity/sip.jd b/docs/html/guide/topics/connectivity/sip.jd old mode 100644 new mode 100755 index 5154767e16ade6c1c1e64c10f3bf05a320c5c4da..d754894741e7f2af07c29012efbeb87b2f21f879 --- a/docs/html/guide/topics/connectivity/sip.jd +++ b/docs/html/guide/topics/connectivity/sip.jd @@ -478,14 +478,19 @@ wireless, so you must test on an actual device. Testing on AVD won't work.
    1. On your device, connect to wireless (Settings > Wireless & networks -> Wi-Fi > Wi-Fi settings)
    2. +> Wi-Fi > Wi-Fi settings).
    3. Set up your mobile device for testing, as described in Developing on a Device.
    4. Run your application on your mobile device, as described in Developing on a Device.
    5. -
    6. If you are using Eclipse, you can view the application log output in Eclipse -using LogCat (Window > Show View > Other > Android > -LogCat).
    7. +
    8. If you are using Android Studio, you can view the application log output by +opening the Event Log console (View > Tool Windows > Event Log). +
    9. Ensure your application is configured to launch Logcat automatically when it runs: +
        +
      1. Select Run > Edit Configurations. +
      2. Select the Miscellaneous tab in the Run/Debug Configurations window. +
      3. Under Logcat, select Show logcat automatically then +select OK.
    diff --git a/docs/html/guide/topics/connectivity/usb/host.jd b/docs/html/guide/topics/connectivity/usb/host.jd index f957b60f5fd6e5d2ccf85a832788cb053928e608..d2194e6d6f6dd89593fa69590e5d82fb0ce34b2a 100644 --- a/docs/html/guide/topics/connectivity/usb/host.jd +++ b/docs/html/guide/topics/connectivity/usb/host.jd @@ -276,7 +276,7 @@ UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); while(deviceIterator.hasNext()){ - UsbDevice device = deviceIterator.next() + UsbDevice device = deviceIterator.next(); //your code } diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index 95548356a0f7510be5611951dea06f9dbf97f87d..619c79021870b596df90ed9718931cb1f1c41e81 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -198,7 +198,7 @@ attribute to "8".

    href="http://code.google.com/android/backup/index.html">Android Backup Service for most Android-powered devices running Android 2.2 or greater.

    -

    In order for you application to perform backup using Android Backup Service, you must +

    In order for your application to perform backup using Android Backup Service, you must register your application with the service to receive a Backup Service Key, then declare the Backup Service Key in your Android manifest.

    diff --git a/docs/html/guide/topics/graphics/prop-animation.jd b/docs/html/guide/topics/graphics/prop-animation.jd old mode 100644 new mode 100755 index e45549678911e8efdd4db11d252775e75dd34e8c..aed533de16ca10a041bfe1248757c3800fa3e77e --- a/docs/html/guide/topics/graphics/prop-animation.jd +++ b/docs/html/guide/topics/graphics/prop-animation.jd @@ -920,10 +920,7 @@ in multiple activities and more easily edit the animation sequence.

    To distinguish animation files that use the new property animation APIs from those that use the legacy view animation framework, starting with Android 3.1, you should save the XML files for property animations in the {@code -res/animator/} directory (instead of {@code res/anim/}). Using the {@code animator} directory name -is optional, but necessary if you want to use the layout editor tools in the Eclipse ADT plugin (ADT -11.0.0+), because ADT only searches the {@code res/animator/} directory for property animation -resources.

    +res/animator/} directory.

    The following property animation classes have XML declaration support with the following XML tags:

    diff --git a/docs/html/guide/topics/location/strategies.jd b/docs/html/guide/topics/location/strategies.jd old mode 100644 new mode 100755 index f1eb66e878d9a5bd1b5b971879f4f4cd416b0a07..32be463e687cb8942928811b4cee12cf1531c808 --- a/docs/html/guide/topics/location/strategies.jd +++ b/docs/html/guide/topics/location/strategies.jd @@ -402,20 +402,23 @@ dynamic set of data is updated each time the user location updates.

    user location works. This is most easily done using a real Android-powered device. If, however, you don't have a device, you can still test your location-based features by mocking location data in the Android emulator. There are three different ways to send your application mock location -data: using Eclipse, DDMS, or the "geo" command in the emulator console.

    +data: using Android Studio, DDMS, or the "geo" command in the emulator console.

    Note: Providing mock location data is injected as GPS location data, so you must request location updates from GPS_PROVIDER in order for mock location data to work.

    -

    Using Eclipse

    +

    Using Android Studio

    -

    Select Window > Show View > Other > Emulator Control.

    +

    Select Tools > Android > AVD Manager. In the Android Virtual +Device Manager window, choose your AVD and launch it in the emulator by selecting the green +play arrow in the Actions column.

    -

    In the Emulator Control panel, enter GPS coordinates under Location Controls as individual -lat/long coordinates, with a GPX file for route playback, or a KML file for multiple place marks. -(Be sure that you have a device selected in the Devices panel—available from Window -> Show View > Other > Devices.)

    +

    Then, select Tools > Android > Android Device Monitor. +Select the Emulator Control tab in the Android Device Monitor window, and enter GPS coordinates +under Location Controls as individual lat/long coordinates, with a GPX file for route playback, +or a KML file for multiple place marks. +

    Using DDMS

    diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd index fd1729c789a631d48f18786f4857cf67d5ccd4cb..e0f5e3d2fc25859dc36563e5be0fce5c4c045611 100644 --- a/docs/html/guide/topics/manifest/activity-element.jd +++ b/docs/html/guide/topics/manifest/activity-element.jd @@ -982,16 +982,17 @@ href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes developer gui ValueDescription {@code "none"}No extra UI options. This is the default. {@code "splitActionBarWhenNarrow"}Add a bar at -the bottom of the screen to display action items in the {@link android.app.ActionBar}, when +the bottom of the screen to display action items in the app bar (also known as the +action bar), when constrained for horizontal space (such as when in portrait mode on a handset). Instead of a small -number of action items appearing in the action bar at the top of the screen, the action bar is +number of action items appearing in the app bar at the top of the screen, the app bar is split into the top navigation section and the bottom bar for action items. This ensures a reasonable amount of space is made available not only for the action items, but also for navigation and title elements at the top. Menu items are not split across the two bars; they always appear together. -

    For more information about the action bar, see the Action Bar developer guide.

    +

    For more information about the app bar, see the Adding the App Bar training class.

    This attribute was added in API level 14.

    diff --git a/docs/html/guide/topics/manifest/application-element.jd b/docs/html/guide/topics/manifest/application-element.jd index 887b4eabe2b78aad670e72ad1a619a6c7869b964..56d61f3f7235d6e129c69c4dcd787f1e881d74a4 100644 --- a/docs/html/guide/topics/manifest/application-element.jd +++ b/docs/html/guide/topics/manifest/application-element.jd @@ -434,16 +434,17 @@ href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes developer gui ValueDescription {@code "none"}No extra UI options. This is the default. {@code "splitActionBarWhenNarrow"}Add a bar at -the bottom of the screen to display action items in the {@link android.app.ActionBar}, when +the bottom of the screen to display action items in the app bar (also known as the +action bar), when constrained for horizontal space (such as when in portrait mode on a handset). Instead of a small -number of action items appearing in the action bar at the top of the screen, the action bar is +number of action items appearing in the app bar at the top of the screen, the app bar is split into the top navigation section and the bottom bar for action items. This ensures a reasonable amount of space is made available not only for the action items, but also for navigation and title elements at the top. Menu items are not split across the two bars; they always appear together. -

    For more information about the action bar, see the Action Bar developer guide.

    +

    For more information about the app bar, see the Adding the App Bar training class.

    This attribute was added in API level 14.

    diff --git a/docs/html/guide/topics/manifest/compatible-screens-element.jd b/docs/html/guide/topics/manifest/compatible-screens-element.jd index 9c7f0365199fe77aa59e1901abb648dd73de9abd..c1fac9481ed5ef6328fe2fb757f8d9feed1d8ae1 100644 --- a/docs/html/guide/topics/manifest/compatible-screens-element.jd +++ b/docs/html/guide/topics/manifest/compatible-screens-element.jd @@ -9,7 +9,8 @@ parent.link=manifest-intro.html
     <compatible-screens>
         <screen android:screenSize=["small" | "normal" | "large" | "xlarge"]
    -            android:screenDensity=["ldpi" | "mdpi" | "hdpi" | "xhdpi" | "xxhdpi" | "xxxhdpi"] />
    +            android:screenDensity=["ldpi" | "mdpi" | "hdpi" | "xhdpi"
    +                                   | "280" | "360" | "420" | "480" | "560" ] />
         ...
     </compatible-screens>
     
    @@ -90,12 +91,15 @@ href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple
    Required. Specifies the screen density for this screen configuration.

    Accepted values:

      -
    • {@code ldpi}
    • -
    • {@code mdpi}
    • -
    • {@code hdpi}
    • -
    • {@code xhdpi}
    • -
    • {@code xxhdpi}
    • -
    • {@code xxxhdpi}
    • +
    • {@code "ldpi"} (approximately 120 dpi)
    • +
    • {@code "mdpi"} (approximately 160 dpi)
    • +
    • {@code "hdpi"} (approximately 240 dpi)
    • +
    • {@code "xhdpi"} (approximately 320 dpi)
    • +
    • {@code "280"}
    • +
    • {@code "360"}
    • +
    • {@code "420"}
    • +
    • {@code "480"}
    • +
    • {@code "560"}

    For information about the different screen densities, see Supporting Multiple Screens.

    diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd index 77176963349b2390be79f407a854dd17f8881f69..5968548a5d3eb1ec6891d09d3280927b23a0a641 100644 --- a/docs/html/guide/topics/manifest/manifest-element.jd +++ b/docs/html/guide/topics/manifest/manifest-element.jd @@ -35,6 +35,7 @@ parent.link=manifest-intro.html
    <uses-configuration>
    <uses-feature>
    <uses-permission> +
    <uses-permission-sdk-23>
    <uses-sdk>

    diff --git a/docs/html/guide/topics/manifest/manifest-intro.jd b/docs/html/guide/topics/manifest/manifest-intro.jd index c8d68f5f03d554515cf1f4933866f5cf30f9cb80..d7b176e810a0e3811b309161c93b41dc203ec483 100644 --- a/docs/html/guide/topics/manifest/manifest-intro.jd +++ b/docs/html/guide/topics/manifest/manifest-intro.jd @@ -279,7 +279,7 @@ example, a label and an icon for an activity. The values of these attributes should be localized and therefore set from a resource or theme. Resource values are expressed in the following format,

    -

    @[package:]type:name

    +

    {@code @[package:]type/name}

    where the package name can be omitted if the resource is in the same package @@ -295,7 +295,7 @@ Values from a theme are expressed in a similar manner, but with an initial '{@co rather than '{@code @}':

    -

    ?[package:]type:name +

    {@code ?[package:]type/name}

    String values
    diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd old mode 100644 new mode 100755 index e746a671a4d182d9efcf817b04c33a5314f3121c..c2def69da4da33f341c421d0d5c07c3452d0a1de --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -486,10 +486,22 @@ determine the features that your application requires.

    1. First, build and export your application as an unsigned .apk. -If you are developing in Eclipse with ADT, right-click the project and select -Android Tools > Export Unsigned Application -Package. Select a destination filename and path and click -OK.
    2. +If you are developing in Android Studio, build your application with Gradle: +
        +
      1. Open the project and select Run > Edit Configurations. +
      2. Select the plus sign near the top-left corner of the Run/Debug +Configurations window. +
      3. Select Gradle. +
      4. Enter Unsigned APK in Name. +
      5. Choose your module from the Gradle project section. +
      6. Enter assemble in Tasks. +
      7. Select OK to complete the new configuration. +
      8. Make sure the Unsigned APK run configuration is selected +in the toolbar and select Run > Run 'Unsigned APK'.
      9. +
      +You can find your unsigned .apk in the +<ProjectName>/app/build/outputs/apk/ directory. +
    3. Next, locate the aapt tool, if it is not already in your PATH. If you are using SDK Tools r8 or higher, you can find aapt in the <SDK>/platform-tools/ directory. @@ -541,12 +553,27 @@ in a separate <uses-feature> element.

      Comments - Audio - android.hardware.audio.low_latency + Audio + android.hardware.audio.low_latency The application uses a low-latency audio pipeline on the device and -is sensitive to delays or lag in sound input or output. - - + is sensitive to delays or lag in sound input or output. + + + + android.hardware.audio.pro + The application uses high-end audio functionality and performance. + + + + android.hardware.microphone + The application records audio via a microphone. + + + + android.hardware.output + The application produces at least one form of audio output, such as speakers, audio jack + or streaming over bluetooth. + Bluetooth @@ -563,9 +590,9 @@ is sensitive to delays or lag in sound input or output. Camera android.hardware.camera The application uses the device's back-facing (main) camera. - Devices with only a front-facing camera do not list this feature, so the - android.hardware.camera.any feature should be - used instead if a camera facing any direction is acceptable for the + Devices with only a front-facing camera do not list this feature, so + the android.hardware.camera.any feature should be used + instead if a camera facing any direction is acceptable for the application. @@ -931,6 +958,12 @@ in a separate <uses-feature> element.

      The application uses or provides Live Wallpapers and should be installed only on devices that support Live Wallpapers. + + MIDI + android.software.midi + The application connects to musical instruments or outputs sound + using the Musical Instrument Digital Interface (MIDI) protocol. + SIP/VOIP android.software.sip diff --git a/docs/html/guide/topics/manifest/uses-permission-element.jd b/docs/html/guide/topics/manifest/uses-permission-element.jd index bb93a702b34ff0d5bed7f34501f90a2439b30fdc..32fe21eb89ea7335f50baa4546f8525c41782b1b 100644 --- a/docs/html/guide/topics/manifest/uses-permission-element.jd +++ b/docs/html/guide/topics/manifest/uses-permission-element.jd @@ -43,14 +43,15 @@ href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions-feat
      description:
      Requests a permission that the application must be granted in -order for it to operate correctly. Permissions are granted by the user when the -application is installed, not while it's running. +order for it to operate correctly. Permissions are granted by the user when the +application is installed (on devices running Android 5.1 and lower) or while the app is running (on devices running Android 6.0 and higher).

      For more information on permissions, see the Permissions -section in the introduction and the separate -Security and Permissions document. +section in the introduction and the separate +System +Permissions API guide. A list of permissions defined by the base platform can be found at {@link android.Manifest.permission android.Manifest.permission}. @@ -60,8 +61,10 @@ A list of permissions defined by the base platform can be found at

      The name of the permission. It can be a permission defined by the application with the <permission> element, a permission defined by another application, or one of the -standard system permissions, such as "{@code android.permission.CAMERA}" -or "{@code android.permission.READ_CONTACTS}". As these examples show, +standard system permissions (such as +{@link android.Manifest.permission#CAMERA "android.permission.CAMERA"} +or {@link android.Manifest.permission#READ_CONTACTS +"android.permission.READ_CONTACTS"}). As these examples show, a permission name typically includes the package name as a prefix.
      {@code android:maxSdkVersion}
      @@ -94,6 +97,8 @@ permission is needed only up to API level 18 with a declaration such as this:
      diff --git a/docs/html/guide/topics/manifest/uses-permission-sdk-23-element.jd b/docs/html/guide/topics/manifest/uses-permission-sdk-23-element.jd new file mode 100644 index 0000000000000000000000000000000000000000..860d30fd0087ca911690e4674d008bd67d6feab4 --- /dev/null +++ b/docs/html/guide/topics/manifest/uses-permission-sdk-23-element.jd @@ -0,0 +1,96 @@ +page.title=<uses-permission-sdk-23> +page.tags="uses-permission-sdk-23","permissions","uses-permission-sdk23" +parent.title=The AndroidManifest.xml File +parent.link=manifest-intro.html +@jd:body + +
      + +
      syntax:
      +
      <uses-permission-sdk-23 android:name="string"
      +        android:maxSdkVersion="integer" />
      + +
      Contained in:
      +
      <manifest>
      + +
      + Description: +
      + +
      + Specifies that an app wants a particular permission, but only if + the app is running on a device with API + level 23 or higher. If the device + is running API level 22 or lower, the app does not have the specified + permission. + +

      + This element is useful when you update an app to include a new + feature that requires an additional permission. If a user updates an app on a + device that is running API level 22 or lower, the system prompts the user + at install time to grant all new permissions that are declared in that + update. If a new feature is minor enough, you may prefer to disable + the feature altogether on those devices, so the user does not have to grant + additional permissions to update the app. By using the + <uses-permission-sdk-23> element instead of <uses-permission>, + you can request the permission only if the app is running on + platforms that support the runtime permissions + model, in which the user + grants permissions to the app while it is running. +

      + +

      + For more information on permissions, see the Permissions + section in the introduction and the separate System Permissions + API guide. A list of permissions defined by the base platform is available + at {@link android.Manifest.permission android.Manifest.permission}. +

      +
      + +
      Attributes:
      + +
      +
      + +
      {@code android:name}
      + +
      + The name of the permission. This permission can be defined by the + app with the <permission> + element, it can be a permission defined by another app, or it can be one + of the standard system permissions, such as + {@link android.Manifest.permission#CAMERA "android.permission.CAMERA"} + or {@link android.Manifest.permission#READ_CONTACTS + "android.permission.READ_CONTACTS"}. +
      + +
      {@code android:maxSdkVersion}
      +
      + The highest API level at which this permission should be granted to your + app. If the app is installed on a device with a later API level, the app + is not granted the permission and cannot use any related functionality. +
      +
      +
      + + +
      introduced in:
      +
      API Level 23
      + +
      see also:
      +
      + +
      + +
      diff --git a/docs/html/guide/topics/manifest/uses-sdk-element.jd b/docs/html/guide/topics/manifest/uses-sdk-element.jd old mode 100644 new mode 100755 index 642b820d1eb65dcb3634f0e4be2a2b3a4058eba5..94577ed53a2d58d1d228a79d1759d59f35b7ffa0 --- a/docs/html/guide/topics/manifest/uses-sdk-element.jd +++ b/docs/html/guide/topics/manifest/uses-sdk-element.jd @@ -226,10 +226,10 @@ Versions dashboards page.

      - + - + @@ -543,10 +543,7 @@ download other platform versions as necessary.

      To access the updater, use the android command-line tool, located in the <sdk>/tools directory. You can launch the SDK updater by executing android sdk. You can -also simply double-click the android.bat (Windows) or android (OS X/Linux) file. -In ADT, you can also access the updater by selecting -Window > Android SDK -Manager.

      +also simply double-click the android.bat (Windows) or android (OS X/Linux) file.

      To run your application against different platform versions in the emulator, create an AVD for each platform version that you want to test. For more diff --git a/docs/html/guide/topics/providers/content-provider-basics.jd b/docs/html/guide/topics/providers/content-provider-basics.jd index 199a671bb938e306eb5c7f800247eaeac556fab5..b7ae3d2cb85cfc309b4b56e248f8c58da6079fbb 100644 --- a/docs/html/guide/topics/providers/content-provider-basics.jd +++ b/docs/html/guide/topics/providers/content-provider-basics.jd @@ -328,7 +328,7 @@ content://user_dictionary/words

      where the user_dictionary string is the provider's authority, and - words string is the table's path. The string + the words string is the table's path. The string content:// (the scheme) is always present, and identifies this as a content URI.

      @@ -346,8 +346,8 @@ Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

      Note: The {@link android.net.Uri} and {@link android.net.Uri.Builder} classes - contain convenience methods for constructing well-formed Uri objects from strings. The - {@link android.content.ContentUris} contains convenience methods for appending id values to + contain convenience methods for constructing well-formed URI objects from strings. The + {@link android.content.ContentUris} class contains convenience methods for appending id values to a URI. The previous snippet uses {@link android.content.ContentUris#withAppendedId withAppendedId()} to append an id to the UserDictionary content URI.

      @@ -407,7 +407,7 @@ Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

      Constructing the query

      - The next step in retrieving data a provider is to construct a query. This first snippet + The next step in retrieving data from a provider is to construct a query. This first snippet defines some variables for accessing the User Dictionary Provider:

      @@ -1148,7 +1148,7 @@ vnd.android.cursor.item
       

      The subtype is provider-specific. The Android built-in providers usually have a simple - subtype. For example, the when the Contacts application creates a row for a telephone number, + subtype. For example, when the Contacts application creates a row for a telephone number, it sets the following MIME type in the row:

      diff --git a/docs/html/guide/topics/providers/content-provider-creating.jd b/docs/html/guide/topics/providers/content-provider-creating.jd
      old mode 100644
      new mode 100755
      index 6ec1e1be0244f78b7f9038f1a791cafb76440802..7cd3d6998a18cd51fa0f3e5cce77126a61528a39
      --- a/docs/html/guide/topics/providers/content-provider-creating.jd
      +++ b/docs/html/guide/topics/providers/content-provider-creating.jd
      @@ -879,8 +879,8 @@ vnd.android.cursor.item/vnd.com.example.provider.table1
           A contract class also helps developers because it usually has mnemonic names for its constants,
           so developers are less likely to use incorrect values for column names or URIs. Since it's a
           class, it can contain Javadoc documentation. Integrated development environments such as
      -    Eclipse can auto-complete constant names from the contract class and display Javadoc for the
      -    constants.
      +    Android Studio can auto-complete constant names from the contract class and display Javadoc for 
      +    the constants.
       

      Developers can't access the contract class's class file from your application, but they can diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd old mode 100644 new mode 100755 index eef8cda3858de539c4a0fd5bc7ed9252a38b733f..564215ead4afe8f96df7496f26df25cc374f4e90 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -192,19 +192,18 @@ precision (such as SIMD CPU instructions).

    4. Make sure you have the required Android SDK version and Build Tools version installed.
    5. Update the settings for the Android build process to include the RenderScript settings: -

      For Android Studio or Gradle-based builds

      • Open the {@code build.gradle} file in the app folder of your application module.
      • Add the following RenderScript settings to the file:
         android {
        -    compileSdkVersion 19
        -    buildToolsVersion "19.0.3"
        +    compileSdkVersion 23
        +    buildToolsVersion "23.0.3"
         
             defaultConfig {
                 minSdkVersion 8
        -        targetSdkVersion 16
        +        targetSdkVersion 19
         
                 renderscriptTargetApi 18
                 renderscriptSupportModeEnabled true
        @@ -218,9 +217,9 @@ android {
         
             
        • {@code renderscriptTargetApi} - Specifies the bytecode version to be generated. We - recommend you set this value to the highest available API level and set - {@code renderscriptSupportModeEnabled} - to {@code true}. Valid values for this setting are any integer value + recommend you set this value to the lowest API level able to provide all the functionality + you are using and set {@code renderscriptSupportModeEnabled} to {@code true}. + Valid values for this setting are any integer value from 11 to the most recently released API level. If your minimum SDK version specified in your application manifest is set to a different value, that value is ignored and the target value in the build file is used to set the minimum SDK version.
        • @@ -233,42 +232,8 @@ android { installed build tools version is used. You should always set this value to ensure the consistency of builds across development machines with different configurations.
        -
      • - -

        For Eclipse

        -
          -
        • Open the {@code project.properties} file in the root folder of your application project.
        • -
        • Add the following lines to the file: - -
          -renderscript.target=18
          -renderscript.support.mode=true
          -sdk.buildtools=18.1.0
          -
          - -

          The settings listed above control specific behavior in the Android build process:

          - -
            -
          • {@code renderscript.target} - Specifies the bytecode version to be generated. We - recommend you set this value to the highest available API level and set - {@code renderscript.support.mode} to {@code true}. Valid values for this setting are any - integer value from 11 to the most recently released API level. If your minimum SDK version - specified in your application manifest is set to a higher value, this value is ignored and - the target value is set to the minimum SDK version.
          • -
          • {@code renderscript.support.mode} - Specifies that the generated bytecode should fall - back to a compatible version if the device it is running on does not support the target version. -
          • -
          • {@code sdk.buildtools} - The version of the Android SDK build tools to use. This value - should be set to {@code 18.1.0} or higher. If this option is not specified, the highest - installed build tools version is used. You should always set this value to ensure the - consistency of builds across development machines with different configurations.
          • -
          -
        • - -
        - -
      +
    6. In your application classes that use RenderScript, add an import for the Support Library classes: @@ -279,7 +244,7 @@ import android.support.v8.renderscript.*;
    7. - +

      Using RenderScript from Java Code

      diff --git a/docs/html/guide/topics/resources/color-list-resource.jd b/docs/html/guide/topics/resources/color-list-resource.jd index 61f6665558d99ac947ddf22e5f1bbe4a9bffec72..ac73d642b8d306b6b3434a411b746e323395149b 100644 --- a/docs/html/guide/topics/resources/color-list-resource.jd +++ b/docs/html/guide/topics/resources/color-list-resource.jd @@ -17,7 +17,7 @@ parent.link=available-resources.html that you can apply as a color, but will actually change colors, depending on the state of the {@link android.view.View} object to which it is applied. For example, a {@link android.widget.Button} widget can exist in one of several different states (pressed, focused, -or niether) and, using a color state list, you can provide a different color during each state.

      +or neither) and, using a color state list, you can provide a different color during each state.

      You can describe the state list in an XML file. Each color is defined in an {@code } element inside a single {@code } element. Each {@code } diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd index dd2c4c28effd0cccf592d6dc016abc03e8c8a2d1..b489b43699a679d129b8d77a105ea4bf5756fedc 100644 --- a/docs/html/guide/topics/resources/drawable-resource.jd +++ b/docs/html/guide/topics/resources/drawable-resource.jd @@ -8,6 +8,7 @@ parent.link=available-resources.html

      See also

      1. 2D Graphics
      2. +
      3. Vector Asset Studio
      @@ -607,7 +608,7 @@ fit the size of the container, due to resizing caused by the offset images.

      that uses a several different images to represent the same graphic, depending on the state of the object. For example, a {@link android.widget.Button} widget can exist in one of several different states (pressed, focused, -or niether) and, using a state list drawable, you can provide a different background image for each +or neither) and, using a state list drawable, you can provide a different background image for each state.

      You can describe the state list in an XML file. Each graphic is represented by an {@code diff --git a/docs/html/guide/topics/resources/index.jd b/docs/html/guide/topics/resources/index.jd index b85170b27bba60c45c8b3d409166637ccc48aa13..60736bbebc516e0db63b287156a4121ce42e51b8 100644 --- a/docs/html/guide/topics/resources/index.jd +++ b/docs/html/guide/topics/resources/index.jd @@ -1,6 +1,6 @@ page.title=App Resources page.landing=true -page.landing.intro=It takes more than just code to build a great app. Resources are the additional files and static content that your code uses, such as bitmaps, layout definitions, user interface strings, animation instructions, and more. +page.landing.intro=It takes more than just code to build a great app. Resources are the additional files and static content that your code uses, such as bitmaps, layout definitions, user interface strings, animation instructions, and more. page.landing.image=images/develop/resources.png page.image=/images/develop/resources.png page.metaDescription=Developer guide about how to use resources in your Android apps. @@ -16,8 +16,8 @@ page.metaDescription=Developer guide about how to use resources in your Android href="http://android-developers.blogspot.com/2011/07/new-tools-for-managing-screen-sizes.html">

      New Tools For Managing Screen Sizes

      Android 3.2 includes new tools for supporting devices with a wide range of screen sizes. -One important result is better support for a new size of screen; what is typically called a ?7-inch? -tablet. This release also offers several new APIs to simplify developers? work in adjusting to +One important result is better support for a new size of screen; what is typically called a "7-inch" +tablet. This release also offers several new APIs to simplify developers' work in adjusting to different screen sizes.

      @@ -27,7 +27,7 @@ different screen sizes.

      difficult to design an app with a single predictable look and feel. We set out to improve this situation for the developer community in Ice Cream Sandwich and beyond.

      - +

      New Mode for Apps on Large Screens

      @@ -47,7 +47,7 @@ compatibility mode that makes these apps more usable on tablets.

      resources and other features so your app can provide an optimized user experience on a variety of Android-compatible devices, using a single application package (APK).

      - +

      Designing for Multiple Screens

      This class shows you how to implement a user interface that's optimized for several screen diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd old mode 100644 new mode 100755 index e60ca9f384e82ada4bafedfe3c0fe4d3493a1e33..afe4611d54fafccd9d5b6526e98d8b1d8a66c8bb --- a/docs/html/guide/topics/resources/localization.jd +++ b/docs/html/guide/topics/resources/localization.jd @@ -10,25 +10,29 @@ parent.link=index.html

      Quickview

        -
      • Use resource sets to create a localized app.
      • -
      • Android loads the correct resource set for the user's language and locale.
      • -
      • If localized resources are not available, Android loads your default resources.
      • +
      • Use resource sets to create a localized app.
      • +
      • Android loads the correct resource set for the user's language and locale.
      • +
      • If localized resources are not available, Android loads your default resources.

      In this document

        -
      1. Overview: Resource-Switching in Android
      2. +
      3. Overview: Resource-Switching in Android
      4. Using Resources for Localization
      5. Localization Tips
      6. Testing Localized Applications

      See also

      -
        -
      1. Localization Checklist
      2. -
      3. Providing Resources
      4. -
      5. Layouts
      6. -
      7. Activity Lifecycle
      8. +
          +
        1. +Localization Checklist
        2. +
        3. +Providing Resources
        4. +
        5. +Layouts
        6. +
        7. +Activity Lifecycle
        @@ -39,8 +43,7 @@ graphics in ways appropriate to the locales where your application will be used.

        This document describes best practices for localizing Android -applications. The principles apply whether you are developing your application -using ADT with Eclipse, Ant-based tools, or any other IDE.

        +applications.

        You should already have a working knowledge of Java and be familiar with Android resource loading, the declaration of user interface elements in XML, @@ -52,19 +55,21 @@ localized aspects of your application as much as possible from the core Java functionality:

          -
        • You can put most or all of the contents of your application's +
        • You can put most or all of the contents of your application's user interface into resource files, as described in this document and in Providing Resources.
        • -
        • The behavior of the user interface, on the other hand, is driven -by your Java code. +href="{@docRoot}guide/topics/resources/providing-resources.html"> +Providing Resources.
        • +
        • The behavior of the user interface, on the other hand, is driven +by your Java code. For example, if users input data that needs to be formatted or sorted differently depending on locale, then you would use Java to handle the data programmatically. This document does not cover how to localize your Java code.
        -

        For a short guide to localizing strings in your app, see the training lesson, Supporting Different Languages.

        +

        For a short guide to localizing strings in your app, see the training lesson, + +Supporting Different Languages.

        Overview: Resource-Switching in Android

        @@ -72,24 +77,25 @@ href="{@docRoot}training/basics/supporting-devices/languages.html">Supporting Di

        Resources are text strings, layouts, sounds, graphics, and any other static data that your Android application needs. An application can include multiple sets of resources, each customized for a different device configuration. When a -user runs the application, Android automatically selects and loads the +user runs the application, Android automatically selects and loads the resources that best match the device.

        (This document focuses on localization and locale. For a complete description of resource-switching and all the types of configurations that you can -specify — screen orientation, touchscreen type, and so on — see Providing -Alternative Resources.)

        +specify — screen orientation, touchscreen type, and so on — +see +Providing Alternative Resources.)

      Platform VersionAPI LevelVERSION_CODENotes
      Android 6.0
      Android 6.0 23 {@link android.os.Build.VERSION_CODES#M}API Changes
      Platform Highlights
      Android 5.1 22
      - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      - When you write your application: -

      - You create a set of default resources, plus alternatives to be used in - different locales.

      -

      right-arrow +

      +When you write your application: +

      +You create a set of default resources, plus alternatives to be used in +different locales.

      +

      +right-arrow

      When a user runs your application: @@ -109,36 +115,35 @@ specially named subdirectories of the project's res/ directory.

      Whenever the application runs in a locale for which you have not provided locale-specific text, Android will load the default strings from -res/values/strings.xml. If this default file is absent, or if it -is missing a string that your application needs, then your application will not run -and will show an error. -The example below illustrates what can happen when the default text file is incomplete.

      +res/values/strings.xml. If this default file is absent, or if it +is missing a string that your application needs, then your application will not run +and will show an error. +The example below illustrates what can happen when the default text file is +incomplete.

      Example: -

      An application's Java code refers to just two strings, text_a and - text_b. This application includes a localized resource file - (res/values-en/strings.xml) that defines text_a and - text_b in English. This application also includes a default +

      An application's Java code refers to just two strings, text_a and + text_b. This application includes a localized resource file + (res/values-en/strings.xml) that defines text_a and + text_b in English. This application also includes a default resource file (res/values/strings.xml) that includes a definition for text_a, but not for text_b:

        -
      • This application might compile without a problem. An IDE such as Eclipse - will not highlight any errors if a resource is missing.
      • -
      • When this application is launched on a device with locale set to English, - the application might run without a problem, because - res/values-en/strings.xml contains both of the needed text +
      • When this application is launched on a device with locale set to English, + the application might run without a problem, because + res/values-en/strings.xml contains both of the needed text strings.
      • -
      • However, the user will see an error message and a Force Close - button when this application is launched on a device set to a +
      • However, the user will see an error message and a Force Close + button when this application is launched on a device set to a language other than English. The application will not load.
      -

      To prevent this situation, make sure that a res/values/strings.xml - file exists and that it defines every needed string. The situation applies to - all types of resources, not just strings: You - need to create a set of default resource files containing all - the resources that your application calls upon — layouts, drawables, +

      To prevent this situation, make sure that a res/values/strings.xml + file exists and that it defines every needed string. The situation applies to + all types of resources, not just strings: You + need to create a set of default resource files containing all + the resources that your application calls upon — layouts, drawables, animations, etc. For information about testing, see Testing for Default Resources.

      @@ -154,26 +159,26 @@ a file with the following location and name:

      default language, which is the language that you expect most of your application's users to speak.

      -

      The default resource set must also include any default drawables and layouts, - and can include other types of resources such as animations. +

      The default resource set must also include any default drawables and layouts, + and can include other types of resources such as animations.
          res/drawable/(required directory holding at least one graphic file, for the application's icon on Google Play)
          res/layout/ (required directory holding an XML file that defines the default layout)
      -     res/anim/ (required if you have any +     res/anim/ (required if you have any res/anim-<qualifiers> folders)
      -     res/xml/ (required if you have any +     res/xml/ (required if you have any res/xml-<qualifiers> folders)
      -     res/raw/ (required if you have any +     res/raw/ (required if you have any res/raw-<qualifiers> folders)

      -

      Tip: In your code, examine each reference to +

      Tip: In your code, examine each reference to an Android resource. Make sure that a default resource is defined for each one. Also make sure that the default string file is complete: A - localized string file can contain a subset of the strings, but the - default string file must contain them all. + localized string file can contain a subset of the strings, but the + default string file must contain them all.

      How to Create Alternative Resources

      @@ -184,9 +189,9 @@ sounds, layouts, and other locale-specific resources.

      An application can specify many res/<qualifiers>/ directories, each with different qualifiers. To create an alternative resource for -a different locale, you use a qualifier that specifies a language or a -language-region combination. (The name of a resource directory must conform -to the naming scheme described in +a different locale, you use a qualifier that specifies a language or a +language-region combination. (The name of a resource directory must conform +to the naming scheme described in Providing Alternative Resources, or else it will not compile.)

      @@ -243,7 +248,7 @@ sets of graphics, each optimized for a different device setup:

      Contains default graphics.
    8. res/drawable-small-land-stylus/
      - Contains graphics optimized for use with a device that expects input from a + Contains graphics optimized for use with a device that expects input from a stylus and has a QVGA low-density screen in landscape orientation.
    9. res/drawable-ja/
      Contains graphics optimized for use with Japanese.
    10. @@ -251,7 +256,7 @@ sets of graphics, each optimized for a different device setup:

      If the application runs on a device that is configured to use Japanese, Android will load graphics from res/drawable-ja/, even if the -device happens to be one that expects input from a stylus and has a QVGA +device happens to be one that expects input from a stylus and has a QVGA low-density screen in landscape orientation.

      Exception: The only qualifiers that take @@ -316,8 +321,8 @@ Checklist document.

      You cannot assume anything about the device on which a user will run your application. The device might have hardware that you were not -anticipating, or it might be set to a locale that you did not plan for or that -you cannot test. Design your application so that it will function normally or fail gracefully no +anticipating, or it might be set to a locale that you did not plan for or that +you cannot test. Design your application so that it will function normally or fail gracefully no matter what device it runs on.

      Important: Make sure that your application @@ -326,14 +331,12 @@ includes a full set of default resources.

      Make sure to include additional modifiers in the folder names) that contain all the images and text that your application will need.

      -

      If an application is missing even one default resource, it will not run on a - device that is set to an unsupported locale. For example, the - res/values/strings.xml default file might lack one string that - the application needs: When the application runs in an unsupported locale and - attempts to load res/values/strings.xml, the user will see an - error message and a Force Close button. An IDE such as Eclipse will not - highlight this kind of error, and you will not see the problem when you - test the application on a device or emulator that is set to a supported locale.

      +

      If an application is missing even one default resource, it will not run on a +device that is set to an unsupported locale. For example, the +res/values/strings.xml default file might lack one string that +the application needs: When the application runs in an unsupported locale and +attempts to load res/values/strings.xml, the user will see an +error message and a Force Close button.

      For more information, see Testing for Default Resources.

      @@ -396,10 +399,10 @@ that Android makes available:

      Testing Localized Applications

      Testing on a Device

      -

      Keep in mind that the device you are testing may be significantly different from - the devices available to consumers in other geographies. The locales available - on your device may differ from those available on other devices. Also, the - resolution and density of the device screen may differ, which could affect +

      Keep in mind that the device you are testing may be significantly different from + the devices available to consumers in other geographies. The locales available + on your device may differ from those available on other devices. Also, the + resolution and density of the device screen may differ, which could affect the display of strings and drawables in your UI.

      To change the locale or language on a device, use the Settings application.

      @@ -419,7 +422,7 @@ the emulator. There are two ways to do this:

      • Use the Custom Locale application, which is accessible from the -Application tab. (After you create a custom locale, switch to it by +Application tab. (After you create a custom locale, switch to it by pressing and holding the locale name.)
      • Change to a custom locale from the adb shell, as described below.
      @@ -443,7 +446,7 @@ command:
      or if you have a device attached, specify that you want the emulator by adding the -e option:
      adb -e shell -
    11. At the adb shell prompt (#), run this command:
      +
    12. At the adb shell prompt (#), run this command:
      setprop persist.sys.locale [BCP-47 language tag];stop;sleep 5;start
      Replace bracketed sections with the appropriate codes from Step 1.
    13. @@ -454,31 +457,28 @@ the -e option:

      setprop persist.sys.locale fr-CA;stop;sleep 5;start

      This will cause the emulator to restart. (It will look like a full reboot, -but it is not.) Once the Home screen appears again, re-launch your application (for -example, click the Run icon in Eclipse), and the application will launch with -the new locale.

      +but it is not.) Once the Home screen appears again, re-launch your application, + and the application launches with the new locale.

      Testing for Default Resources

      -

      Here's how to test whether an application includes every string resource that it needs:

      -
      1. Set the emulator or device to a language that your application does not - support. For example, if the application has French strings in - res/values-fr/ but does not have any Spanish strings in - res/values-es/, then set the emulator's locale to Spanish. - (You can use the Custom Locale application to set the emulator to an - unsupported locale.)
      2. -
      3. Run the application.
      4. -
      5. If the application shows an error message and a Force Close button, it might - be looking for a string that is not available. Make sure that your - res/values/strings.xml file includes a definition for - every string that the application uses.
      6. -
      -

      - -

      If the test is successful, repeat it for other types of - configurations. For example, if the application has a layout file called - res/layout-land/main.xml but does not contain a file called - res/layout-port/main.xml, then set the emulator or device to - portrait orientation and see if the application will run. - - +

      Here's how to test whether an application includes every string +resource that it needs:

      +
      1. Set the emulator or device to a language that your application does not +support. For example, if the application has French strings in +res/values-fr/ but does not have any Spanish strings in +res/values-es/, then set the emulator's locale to Spanish. +(You can use the Custom Locale application to set the emulator to an +unsupported locale.)
      2. +
      3. Run the application.
      4. +
      5. If the application shows an error message and a Force Close button, it might +be looking for a string that is not available. Make sure that your +res/values/strings.xml file includes a definition for +every string that the application uses.
      6. +
      +

      +

      If the test is successful, repeat it for other types of +configurations. For example, if the application has a layout file called +res/layout-land/main.xml but does not contain a file called +res/layout-port/main.xml, then set the emulator or device to +portrait orientation and see if the application will run. diff --git a/docs/html/guide/topics/resources/menu-resource.jd b/docs/html/guide/topics/resources/menu-resource.jd index b2d6eb3445c9b19e3c86f609bd326c01e68c93c2..53ff27615a3e71d28aa037887410e14570f2628c 100644 --- a/docs/html/guide/topics/resources/menu-resource.jd +++ b/docs/html/guide/topics/resources/menu-resource.jd @@ -117,43 +117,47 @@ functionality.

      Introduced in API Level 11.

      android:showAsAction
      -
      Keyword. When and how this item should appear as an action item in the Action -Bar. A menu item can appear as an action item only when the activity includes an {@link -android.app.ActionBar} (introduced in API Level 11). Valid values: +
      Keyword. When and how this item should appear as an action item in the app + bar. A menu item can appear as an action item only when the activity includes an + app bar. Valid values: - + - - + +with other UI in the app bar.
      ValueDescription
      ifRoomOnly place this item in the Action Bar if -there is room for it.
      ifRoomOnly place this item in the + app bar if there is room for it. If there is not room for all + the items marked "ifRoom", the items with the lowest + orderInCategory values are displayed as actions, and + the remaining items are displayed in the overflow menu.
      withTextAlso include the title text (defined by {@code android:title}) with the action item. You can include this value along with one of the others as a flag set, by separating them with a pipe {@code |}.
      neverNever place this item in the Action Bar.
      alwaysAlways place this item in the Action Bar. +
      neverNever place this item in the app bar. Instead, list the item in the app bar's overflow + menu.
      alwaysAlways place this item in the app bar. Avoid using this unless it's critical that the item always appear in the action bar. Setting multiple items to always appear as action items can result in them overlapping -with other UI in the action bar.
      collapseActionViewThe action view associated with this action item (as declared by android:actionLayout or android:actionViewClass) is collapsible.
      Introduced in API Level 14.
      -

      See the Action Bar developer -guide for more information.

      +

      See the Adding the App Bar + training class for more information.

      Introduced in API Level 11.

      android:actionLayout
      Layout resource. A layout to use as the action view. -

      See the Action Bar developer -guide for more information.

      +

      See Action + Views and Action Providers for more information.

      Introduced in API Level 11.

      android:actionViewClass
      Class name. A fully-qualified class name for the {@link android.view.View} to use as the action view. For example, {@code "android.widget.SearchView"} to use {@link android.widget.SearchView} as an action view. -

      See the Action Bar developer -guide for more information.

      +

      See Action + Views and Action Providers for more information.

      Warning: If you obfuscate your code using ProGuard (or a similar tool), be sure to exclude the class you specify in this attribute from renaming, because it can break the @@ -164,8 +168,8 @@ functionality.

      Class name. A fully-qualified class name for the {@link android.view.ActionProvider} to use in place of the action item. For example, {@code "android.widget.ShareActionProvider"} to use {@link android.widget.ShareActionProvider}. -

      See the Action Bar developer -guide for more information.

      +

      See Action + Views and Action Providers for more information.

      Warning: If you obfuscate your code using ProGuard (or a similar tool), be sure to exclude the class you specify in this attribute from renaming, because it can break the @@ -301,4 +305,4 @@ available only on Android 3.0 (API Level 11) and greater.

      - \ No newline at end of file + diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd index 9adf6def4c7f267ddb97f25d7e3e5a7f63d75f39..e6193db31e0159ab148c102ce16ceec788441365 100644 --- a/docs/html/guide/topics/resources/string-resource.jd +++ b/docs/html/guide/topics/resources/string-resource.jd @@ -1,6 +1,7 @@ page.title=String Resources parent.title=Resource Types parent.link=available-resources.html +page.metaDescription=Explains how to use string resources in your UI. @jd:body

      A string resource provides text strings for your application diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd index 1af3e5d7ad61a23caa7eb7bdf28ec709fbc64c7a..389fc1d6802818c2730236a822900b76e868b9a5 100644 --- a/docs/html/guide/topics/search/search-dialog.jd +++ b/docs/html/guide/topics/search/search-dialog.jd @@ -134,8 +134,7 @@ data, and displays the search results.

    14. Or, a {@link android.widget.SearchView} widget

      Using the search widget allows you to put the search box anywhere in your activity. Instead of putting it in your activity layout, you should usually use -{@link android.widget.SearchView} as an -action view in the Action Bar.

      +{@link android.widget.SearchView} as an action view in the app bar.

    15. @@ -493,10 +492,10 @@ the Android system and other apps, you should label your button with the Android available from the Action Bar Icon Pack.

      -

      Note: If your app uses the action bar, then you should not use +

      Note: If your app uses an app bar, then you should not use the search dialog for your search interface. Instead, use the search -widget as a collapsible view in the action bar.

      +widget as a collapsible view in the app bar.

      You can also enable "type-to-search" functionality, which activates the search dialog when the user starts typing on the keyboard—the keystrokes are inserted into the search dialog. You can @@ -680,10 +679,9 @@ android.widget.SearchView} widget as an "action view" in the Action Bar.

      The {@link android.widget.SearchView} widget is available in Android 3.0 and higher. If you're developing your application for Android 3.0 and have decided to use the search widget, we -recommend that you insert the search widget as an action view in the Action Bar, +recommend that you insert the search widget as an action view in the app bar, instead of using the search dialog (and instead of placing the search widget in your activity -layout). For example, figure 2 shows the search widget in the Action Bar.

      +layout). For example, figure 2 shows the search widget in the app bar.

      The search widget provides the same functionality as the search dialog. It starts the appropriate activity when the user executes a search, and it can provide search suggestions and perform voice @@ -707,8 +705,8 @@ android.app.SearchableInfo} object that represents your searchable configuration android.app.SearchManager#getSearchableInfo getSearchableInfo()} on {@link android.app.SearchManager}.

      -

      For example, if you're using a {@link android.widget.SearchView} as an action view in the Action Bar, you should enable the widget +

      For example, if you're using a {@link android.widget.SearchView} as an action view in the +app bar, you should enable the widget during the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} callback:

      @@ -738,9 +736,8 @@ with some callback methods and event listeners. For more information, see the re
       documentation for {@link android.widget.SearchView} and its nested interfaces for the
       appropriate event listeners.

      -

      For more information about action views in the Action Bar, read the Action Bar developer guide (which -includes sample code for adding a search widget as an action view).

      +

      For more information about action views in the Action Bar, see +Action Views and Action Providers.

      Other search widget features

      diff --git a/docs/html/guide/topics/security/normal-permissions.jd b/docs/html/guide/topics/security/normal-permissions.jd new file mode 100644 index 0000000000000000000000000000000000000000..8471dc7fbb672b7641d18f6683f9f897fc8eed51 --- /dev/null +++ b/docs/html/guide/topics/security/normal-permissions.jd @@ -0,0 +1,174 @@ +page.title=Normal Permissions +page.tags=permissions,normal +@jd:body + + +
      +
      + + + +

      Key classes

      +
        +
      1. {@link android.Manifest.permission}
      2. +
      + +

      See Also

      +
        +
      1. Working with System + Permissions
      2. +
      + + + +
      +
      + +

      + Many permissions are designated as {@link + android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}, + which indicates that + there's no great risk to the user's privacy or security in letting apps have + those permissions. For example, users would reasonably want to know whether + an app can read their contact information, so users have to grant this + permission explicitly. By contrast, there's no great risk in allowing an app + to vibrate the device, so that permission is designated as normal. +

      + +

      + If an app declares in its + manifest that it needs a normal permission, the system automatically grants + the app + that permission at install time. The system does not prompt the user + to grant normal + permissions, and users cannot revoke these permissions. +

      + +

      As of API level 23, the following permissions are classified as {@link + android.content.pm.PermissionInfo#PROTECTION_NORMAL PROTECTION_NORMAL}:

      + +
        +
      • {@link android.Manifest.permission#ACCESS_LOCATION_EXTRA_COMMANDS + ACCESS_LOCATION_EXTRA_COMMANDS} +
      • + +
      • {@link android.Manifest.permission#ACCESS_NETWORK_STATE + ACCESS_NETWORK_STATE} +
      • + +
      • {@link android.Manifest.permission#ACCESS_NOTIFICATION_POLICY + ACCESS_NOTIFICATION_POLICY} +
      • + +
      • {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} +
      • + +
      • {@link android.Manifest.permission#BLUETOOTH BLUETOOTH} +
      • + +
      • {@link android.Manifest.permission#BLUETOOTH_ADMIN BLUETOOTH_ADMIN} +
      • + +
      • {@link android.Manifest.permission#BROADCAST_STICKY BROADCAST_STICKY} +
      • + +
      • {@link android.Manifest.permission#CHANGE_NETWORK_STATE + CHANGE_NETWORK_STATE} +
      • + +
      • {@link android.Manifest.permission#CHANGE_WIFI_MULTICAST_STATE + CHANGE_WIFI_MULTICAST_STATE} +
      • + +
      • {@link android.Manifest.permission#CHANGE_WIFI_STATE CHANGE_WIFI_STATE} +
      • + +
      • {@link android.Manifest.permission#DISABLE_KEYGUARD DISABLE_KEYGUARD} +
      • + +
      • {@link android.Manifest.permission#EXPAND_STATUS_BAR EXPAND_STATUS_BAR} +
      • + +
      • {@link android.Manifest.permission#GET_PACKAGE_SIZE GET_PACKAGE_SIZE} +
      • + +
      • {@link android.Manifest.permission#INSTALL_SHORTCUT INSTALL_SHORTCUT} +
      • + +
      • {@link android.Manifest.permission#INTERNET INTERNET} +
      • + +
      • {@link android.Manifest.permission#KILL_BACKGROUND_PROCESSES + KILL_BACKGROUND_PROCESSES} +
      • + +
      • {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS + MODIFY_AUDIO_SETTINGS} +
      • + +
      • {@link android.Manifest.permission#NFC NFC} +
      • + +
      • {@link android.Manifest.permission#READ_SYNC_SETTINGS READ_SYNC_SETTINGS} +
      • + +
      • {@link android.Manifest.permission#READ_SYNC_STATS READ_SYNC_STATS} +
      • + +
      • {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED + RECEIVE_BOOT_COMPLETED} +
      • + +
      • {@link android.Manifest.permission#REORDER_TASKS REORDER_TASKS} +
      • + +
      • {@link android.Manifest.permission#REQUEST_IGNORE_BATTERY_OPTIMIZATIONS + REQUEST_IGNORE_BATTERY_OPTIMIZATIONS} +
      • + +
      • {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES + REQUEST_INSTALL_PACKAGES} +
      • + +
      • {@link android.Manifest.permission#SET_ALARM SET_ALARM} +
      • + +
      • {@link android.Manifest.permission#SET_TIME_ZONE SET_TIME_ZONE} +
      • + +
      • {@link android.Manifest.permission#SET_WALLPAPER SET_WALLPAPER} +
      • + +
      • {@link android.Manifest.permission#SET_WALLPAPER_HINTS + SET_WALLPAPER_HINTS} +
      • + +
      • {@link android.Manifest.permission#TRANSMIT_IR TRANSMIT_IR} +
      • + +
      • {@link android.Manifest.permission#UNINSTALL_SHORTCUT UNINSTALL_SHORTCUT} +
      • + +
      • {@link android.Manifest.permission#USE_FINGERPRINT USE_FINGERPRINT} +
      • + +
      • {@link android.Manifest.permission#VIBRATE VIBRATE} +
      • + +
      • {@link android.Manifest.permission#WAKE_LOCK WAKE_LOCK} +
      • + +
      • {@link android.Manifest.permission#WRITE_SYNC_SETTINGS + WRITE_SYNC_SETTINGS} +
      • +
      diff --git a/docs/html/guide/topics/security/permissions.jd b/docs/html/guide/topics/security/permissions.jd index 6158e40f2f2353d6369c7aebd6fcdc578bfb82f1..f7f70b3d9a349c96e6eaf069606b721f283ad6af 100644 --- a/docs/html/guide/topics/security/permissions.jd +++ b/docs/html/guide/topics/security/permissions.jd @@ -1,4 +1,5 @@ page.title=System Permissions +page.tags=permissions @jd:body
      @@ -10,7 +11,12 @@ page.title=System Permissions
    16. Application Signing
    17. User IDs and File Access
    18. Using Permissions
    19. -
    20. Declaring and Enforcing Permissions +
    21. Normal and Dangerous Permissions +
        +
      1. Permission Groups
      2. +
      +
    22. +
    23. Defining and Enforcing Permissions
      1. ...in AndroidManifest.xml
      2. ...when Sending Broadcasts
      3. @@ -18,8 +24,45 @@ page.title=System Permissions
    24. URI Permissions
    25. + +

      Key classes

      +
        +
      1. {@link android.Manifest.permission}
      2. +
      3. {@link android.Manifest.permission_group}
      4. +
      + +

      See Also

      +
        +
      1. Working with System + Permissions
      2. +
      + + +
      + + +
      +

      Design Patterns

      +

      Permissions

      +
      +
      + + + +
      +

      Video

      +

      Google I/O 2015—Android M Permissions: Best Practices for + Developers

      +

      Android is a privilege-separated operating system, in which each application runs with a distinct system identity (Linux user ID and group @@ -33,7 +76,8 @@ ad hoc access to specific pieces of data.

      This document describes how application developers can use the security features provided by Android. A more general Android Security +href="http://source.android.com/tech/security/index.html" +class="external-link">Android Security Overview is provided in the Android Open Source Project.

      @@ -51,12 +95,11 @@ network access, keeping the device awake, and so on.

      must explicitly share resources and data. They do this by declaring the permissions they need for additional capabilities not provided by the basic sandbox. Applications statically declare the permissions they -require, and the Android system prompts the user for consent at the time the -application is installed.

      +require, and the Android system prompts the user for consent.

      The application sandbox does not depend on the technology used to build an application. In particular the Dalvik VM is not a security boundary, and -any app can run native code (see the Android +any app can run native code (see the Android NDK). All types of applications — Java, native, and hybrid — are sandboxed in the same way and have the same degree of security from each other.

      @@ -72,9 +115,9 @@ signed by a certificate authority; it is perfectly allowable, and typical, for Android applications to use self-signed certificates. The purpose of certificates in Android is to distinguish application authors. This allows the system to grant or deny applications access to signature-level +href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature-level permissions and to grant or deny an application's request to be given +href="{@docRoot}guide/topics/manifest/manifest-element.html#uid">request to be given the same Linux identity as another application.

      @@ -110,15 +153,16 @@ owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it.

      - -

      Using Permissions

      +

      Using Permissions

      A basic Android application has no permissions associated with it by default, meaning it cannot do anything that would adversely impact the user experience or any data on the device. To make use of protected features of the device, -you must include in your AndroidManifest.xml one or more -{@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} -tags declaring the permissions that your application needs.

      +you must include one or more +<uses-permission> +tags in your app +manifest.

      For example, an application that needs to monitor incoming SMS messages would specify:

      @@ -129,14 +173,50 @@ specify:

      ... </manifest>
      -

      At application install time, permissions requested by the application are -granted to it by the package installer, based on checks against the -signatures of the applications declaring those permissions and/or interaction -with the user. No checks with the user -are done while an application is running; the app is either granted a particular -permission when installed, and can use that feature as desired, or the -permission is not granted and any attempt to use the feature fails -without prompting the user.

      + + +

      + If your app lists normal permissions in its manifest (that is, + permissions that don't pose much risk to the user's privacy or the device's + operation), the system automatically grants those permissions. + If your app lists dangerous permissions in its manifest (that is, + permissions that could potentially affect the user's privacy or the device's + normal operation), the system asks the user to explicitly grant those + permissions. The way Android makes the requests depends on the system + version, and the system version targeted by your app: +

      + +
        +
      • If the device is running Android 6.0 (API level 23) or higher, + and the app's targetSdkVersion + is 23 or higher, the app requests permissions from the user at run-time. + The user can revoke the permissions at any time, so the app needs to + check whether it has the permissions every time it runs. + For more information about requesting permissions in your app, see the + Working with System + Permissions training guide. +
      • + +
      • If the device is running Android 5.1 (API level 22) or lower, or + the app's targetSdkVersion + is 22 or lower, the system asks the user to grant the permissions when the + user installs the app. If you add a new permission to an updated version of + the app, the system asks the user to grant that permission when the user + updates the app. Once the user installs the app, the only way they can + revoke the permission is by uninstalling the app. +
      • +

      Often times a permission failure will result in a {@link java.lang.SecurityException} being thrown back to the application. However, @@ -146,12 +226,6 @@ being delivered to each receiver, after the method call has returned, so you will not receive an exception if there are permission failures. In almost all cases, however, a permission failure will be printed to the system log.

      -

      However, in a normal user situation (such as when the app is installed -from Google Play Store), an app cannot be installed if the user does not grant the app -each of the requested permissions. So you generally don't need to worry about runtime failures -caused by missing permissions because the mere fact that the app is installed at all -means that your app has been granted its desired permissions.

      -

      The permissions provided by the Android system can be found at {@link android.Manifest.permission}. Any application may also define and enforce its own permissions, so this is not a comprehensive list of all possible @@ -171,10 +245,9 @@ your broadcast or who can send a broadcast to you.

    26. Binding to or starting a service.
    27. +

      Automatic permission adjustments

      - -
      -

      Caution: Over time, +

      Over time, new restrictions may be added to the platform such that, in order to use certain APIs, your app must request a permission that it previously did not need. Because existing apps assume access to those APIs is freely available, @@ -189,18 +262,283 @@ Android adds the permission.

      added in API level 4 to restrict access to the shared storage space. If your {@code targetSdkVersion} is 3 or lower, this permission is added to your app on newer versions of Android.

      -

      Beware that if this happens to your app, your app listing on Google Play will show these -required permissions even though your app might not actually require them.

      + +

      + Caution: If a permission is automatically added to your app, + your app listing on Google Play lists these additional permissions even + though your app might not actually require them. +

      +

      To avoid this and remove the default permissions you don't need, always update your {@code targetSdkVersion} to be as high as possible. You can see which permissions were added with each release in the {@link android.os.Build.VERSION_CODES} documentation.

      +

      Normal and Dangerous Permissions

      + +

      + System permissions are divided into several protection levels. The two most + important protection levels to know about are normal and + dangerous permissions: +

      + +
        +
      • + Normal permissions cover areas where your app needs to access data + or resources outside the app's sandbox, but where there's very little risk + to the user's privacy or the operation of other apps. For example, + permission to set the time zone is a normal permission. If an app + declares that it needs a normal permission, the system automatically grants + the permission to the app. For a full listing of the current normal + permissions, see Normal permissions. +
      • + +
      • + Dangerous permissions cover areas where the app wants data or + resources that involve the user's private information, or could potentially + affect the user's stored data or the operation of other apps. For example, + the ability to read the user's contacts is a dangerous permission. If an + app declares that it needs a dangerous permission, the user has to + explicitly grant the permission to the app. +
      • +
      + + + +

      Permission groups

      + +

      + All dangerous Android system permissions belong to permission groups. + If the device is running Android 6.0 (API level 23) and the app's targetSdkVersion is 23 or higher, the following system + behavior applies when your app requests a dangerous permission: +

      + +
        +
      • If an app requests a dangerous permission listed in its manifest, and the app + does not currently have any permissions in the permission group, the system + shows a dialog box to the user describing the permission group that the app + wants access to. The dialog box does not describe the specific permission + within that group. For example, if an app requests the {@link + android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission, the + system dialog box just says the app needs access to the device's contacts. If + the user grants approval, the system gives the app just the permission it + requested. +
      • + +
      • If an app requests a dangerous permission listed in its manifest, and the app + already has another dangerous permission in the same permission group, the + system immediately grants the permission without any interaction with the + user. For example, if an app had previously requested and been granted the + {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission, + and it then requests {@link android.Manifest.permission#WRITE_CONTACTS + WRITE_CONTACTS}, the system immediately grants that permission. +
      • +
      + +

      + Any permission can belong to a permission group, including normal permissions + and permissions defined by your app. + However, a permission's group only affects the user experience if the + permission is dangerous. You can ignore the permission group for normal + permissions. +

      + +

      + If the device is running Android 5.1 (API level 22) or lower, or the app's + targetSdkVersion is 22 or lower, the system asks the user + to grant the permissions at install time. Once again, the system just tells + the user what permission groups the app needs, not the individual + permissions. +

      + +

      + Table 1. Dangerous permissions and permission groups.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Permission GroupPermissions
      {@link android.Manifest.permission_group#CALENDAR CALENDAR} +
        +
      • + {@link android.Manifest.permission#READ_CALENDAR READ_CALENDAR} +
      • +
      +
        +
      • + {@link android.Manifest.permission#WRITE_CALENDAR WRITE_CALENDAR} +
      • +
      +
      {@link android.Manifest.permission_group#CAMERA CAMERA} +
        +
      • + {@link android.Manifest.permission#CAMERA CAMERA} +
      • +
      +
      {@link android.Manifest.permission_group#CONTACTS CONTACTS} +
        +
      • + {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} +
      • +
      • + {@link android.Manifest.permission#WRITE_CONTACTS WRITE_CONTACTS} +
      • +
      • + {@link android.Manifest.permission#GET_ACCOUNTS GET_ACCOUNTS} +
      • +
      +
      {@link android.Manifest.permission_group#LOCATION LOCATION} +
        +
      • + {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} +
      • +
      • + {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} +
      • +
      +
      {@link android.Manifest.permission_group#MICROPHONE MICROPHONE} +
        +
      • + {@link android.Manifest.permission#RECORD_AUDIO RECORD_AUDIO} +
      • +
      +
      {@link android.Manifest.permission_group#PHONE PHONE} +
        +
      • + {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} +
      • +
      • + {@link android.Manifest.permission#CALL_PHONE CALL_PHONE} +
      • +
      • + {@link android.Manifest.permission#READ_CALL_LOG READ_CALL_LOG} +
      • +
      • + {@link android.Manifest.permission#WRITE_CALL_LOG WRITE_CALL_LOG} +
      • +
      • + {@link android.Manifest.permission#ADD_VOICEMAIL ADD_VOICEMAIL} +
      • +
      • + {@link android.Manifest.permission#USE_SIP USE_SIP} +
      • +
      • + {@link android.Manifest.permission#PROCESS_OUTGOING_CALLS PROCESS_OUTGOING_CALLS} +
      • +
      +
      {@link android.Manifest.permission_group#SENSORS SENSORS} +
        +
      • + {@link android.Manifest.permission#BODY_SENSORS BODY_SENSORS} +
      • +
      +
      {@link android.Manifest.permission_group#SMS SMS} +
        +
      • + {@link android.Manifest.permission#SEND_SMS SEND_SMS} +
      • +
      • + {@link android.Manifest.permission#RECEIVE_SMS RECEIVE_SMS} +
      • +
      • + {@link android.Manifest.permission#READ_SMS READ_SMS} +
      • +
      • + {@link android.Manifest.permission#RECEIVE_WAP_PUSH RECEIVE_WAP_PUSH} +
      • +
      • + {@link android.Manifest.permission#RECEIVE_MMS RECEIVE_MMS} +
      • +
      +
      + {@link android.Manifest.permission_group#STORAGE STORAGE} + +
        +
      • + {@link android.Manifest.permission#READ_EXTERNAL_STORAGE + READ_EXTERNAL_STORAGE} +
      • +
      • + {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE + WRITE_EXTERNAL_STORAGE} +
      • +
      +
      -

      Declaring and Enforcing Permissions

      +

      Defining and Enforcing Permissions

      To enforce your own permissions, you must first declare them in your AndroidManifest.xml using one or more @@ -439,10 +777,6 @@ android:grantUriPermissions} attribute or {@link android.content.Context#checkUriPermission Context.checkUriPermission()} methods.

      - - - -

      Continue reading about:

      @@ -467,7 +801,7 @@ methods.

      Information about Android works on different types of devices and an introduction to how you can optimize your app for each device or restrict your app's availability to different devices.
      -
      Android Security Overview
      A detailed discussion about the Android platform's security model.
      diff --git a/docs/html/guide/topics/sensors/sensors_motion.jd b/docs/html/guide/topics/sensors/sensors_motion.jd index 393c3c5d827a607ea1ae2c2ff4d5635b287503f8..2a3c4d6a5da26cd0ca9e1af5494ae61c26c5e7a0 100644 --- a/docs/html/guide/topics/sensors/sensors_motion.jd +++ b/docs/html/guide/topics/sensors/sensors_motion.jd @@ -338,7 +338,7 @@ acceleration sensor.

      should be identical to that of the accelerometer.

      Using the Gyroscope

      -

      The gyroscope measures the rate or rotation in rad/s around a device's x, y, +

      The gyroscope measures the rate of rotation in rad/s around a device's x, y, and z axis. The following code shows you how to get an instance of the default gyroscope:

      diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd
      deleted file mode 100644
      index c21671fa5d991b84ea6a76175af06b7532b7f1e8..0000000000000000000000000000000000000000
      --- a/docs/html/guide/topics/ui/actionbar.jd
      +++ /dev/null
      @@ -1,1501 +0,0 @@
      -page.title=Action Bar
      -page.tags=actionbar,menu,tabs
      -
      -@jd:body
      -
      -
      -
      -  
      -

      Design Guide

      -

      Action Bar

      -
      -
      - - - -

      The action bar is a window feature that identifies the user location, and -provides user actions and navigation modes. Using the action bar offers your users a -familiar interface across applications that the system gracefully adapts -for different screen configurations.

      - - -

      Figure 1. An action bar that includes the [1] app icon, -[2] two action items, and [3] action overflow.

      - -

      The action bar provides several key functions:

      - -
        -
      • Provides a dedicated space for giving your app an identity and indicating the user's - location in the app.
      • -
      • Makes important actions prominent and accessible in a predictable way - (such as Search).
      • -
      • Supports consistent navigation and view switching within apps (with tabs or drop-down - lists).
      • -
      - -

      For more information about the action bar's interaction patterns and design guidelines, -see the Action Bar -design guide.

      - -

      The {@link android.app.ActionBar} APIs were first added in Android 3.0 (API level 11) but they -are also available in the Support Library -for compatibility with Android 2.1 (API level 7) and above.

      - -

      This guide focuses on how to use the -support library's action bar, but if your app supports only Android 3.0 or higher, you -should use the {@link android.app.ActionBar} APIs in the framework. Most of the APIs are -the same—but reside in a different package namespace—with a few exceptions to method -names or signatures that are noted in the sections below.

      - - -
      -

      Caution: Be certain you import -the {@code ActionBar} class (and related APIs) from the appropriate package:

      -
        -
      • If supporting API levels lower than 11:
        -{@code import android.support.v7.app.ActionBar}
      • -
      • If supporting only API level 11 and higher:
        -{@code import android.app.ActionBar}
      • -
      -
      - - -

      Note: If you're looking for information about the contextual -action bar for displaying contextual action items, see the Menu guide.

      - - - -

      Adding the Action Bar

      - -

      As mentioned above, this guide focuses on how to use the {@link -android.support.v7.app.ActionBar} APIs in the support library. So before you can add the action -bar, you must set up your project with the appcompat v7 support library by -following the instructions in the Support -Library Setup.

      - -

      Once your project is set up with the support library, here's how to add the action bar:

      -
        -
      1. Create your activity by extending {@link android.support.v7.app.ActionBarActivity}.
      2. -
      3. Use (or extend) one of the {@link android.support.v7.appcompat.R.style#Theme_AppCompat - Theme.AppCompat} themes for your activity. For example: -
        <activity android:theme="@style/Theme.AppCompat.Light" ... >
        -
      4. -
      - -

      Now your activity includes the action bar when running on Android 2.1 (API level 7) or higher. -

      - -
      -

      On API level 11 or higher

      -

      The action bar is included in all activities that use the -{@link android.R.style#Theme_Holo Theme.Holo} theme (or one of its -descendants), which is the default theme when either the {@code targetSdkVersion} or -{@code minSdkVersion} -attribute is set to {@code "11"} or higher. If you don't want the action bar for an -activity, set the activity theme to {@link android.R.style#Theme_Holo_NoActionBar -Theme.Holo.NoActionBar}.

      -
      - - -

      Removing the action bar

      - -

      You can hide the action bar at runtime by calling {@link android.support.v7.app.ActionBar#hide}. -For example:

      - -
      -ActionBar actionBar = {@link android.support.v7.app.ActionBarActivity#getSupportActionBar()};
      -actionBar.hide();
      -
      - -
      -

      On API level 11 or higher

      -

      Get the {@link android.app.ActionBar} with the {@link android.app.Activity#getActionBar} -method.

      -
      - -

      When the action bar hides, the system adjusts your layout to fill the -screen space now available. You can bring the action bar back by calling {@link -android.support.v7.app.ActionBar#show()}.

      - -

      Beware that hiding and removing the action bar causes your activity to re-layout in order to -account for the space consumed by the action bar. If your activity often hides and shows the -action bar, you might want to enable overlay mode. Overlay mode -draws the action bar in front of your activity layout, obscuring the top portion. This -way, your layout remains fixed when the action bar hides and re-appears. To enable overlay mode, -create a custom theme for your activity and set {@link -android.support.v7.appcompat.R.attr#windowActionBarOverlay -windowActionBarOverlay} to {@code true}. For more information, see the section below about Styling the Action Bar.

      - - - - -

      By default, the system uses your application icon in the action bar, as specified by the {@code icon} -attribute in the {@code -} or {@code -} element. However, if you also specify the {@code logo} -attribute, then the action bar uses the logo image instead of the icon.

      - -

      A logo should usually be wider than the icon, but should not include unnecessary text. You -should generally use a logo only when it represents your brand in a traditional format that users -recognize. A good example is the YouTube app's logo—the logo represents the expected user -brand, whereas the app's icon is a modified version that conforms to the square requirement -for the launcher icon.

      - - - - -

      Adding Action Items

      - -
      - -

      Figure 2. Action bar with three action buttons and -the overflow button.

      -
      - -

      The action bar provides users access to the most important action -items relating to the app's current -context. Those that appear directly in the action bar with an icon and/or text are known -as action buttons. Actions that can't fit in the action bar or aren't -important enough are hidden in the action overflow. -The user can reveal a list of the other actions by pressing the overflow button -on the right side (or the device Menu button, if available).

      - -

      When your activity starts, the system populates the action items by calling your activity's -{@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method. Use this -method to inflate a menu resource that defines all the -action items. For example, here's a menu resource defining a couple of menu items:

      - -

      res/menu/main_activity_actions.xml

      -
      -<menu xmlns:android="http://schemas.android.com/apk/res/android" >
      -    <item android:id="@+id/action_search"
      -          android:icon="@drawable/ic_action_search"
      -          android:title="@string/action_search"/>
      -    <item android:id="@+id/action_compose"
      -          android:icon="@drawable/ic_action_compose"
      -          android:title="@string/action_compose" />
      -</menu>
      -
      - -

      Then in your activity's {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} -method, inflate the menu resource into the given {@link android.view.Menu} -to add each item to the action bar:

      - -
      -@Override
      -public boolean onCreateOptionsMenu(Menu menu) {
      -    // Inflate the menu items for use in the action bar
      -    MenuInflater inflater = getMenuInflater();
      -    inflater.inflate(R.menu.main_activity_actions, menu);
      -    return super.onCreateOptionsMenu(menu);
      -}
      -
      - -

      To request that an item appear directly in the action bar -as an action button, include {@code -showAsAction="ifRoom"} in the {@code } tag. For example:

      - -
      -<menu xmlns:android="http://schemas.android.com/apk/res/android"
      -      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
      -    <item android:id="@+id/action_search"
      -          android:icon="@drawable/ic_action_search"
      -          android:title="@string/action_search"
      -          yourapp:showAsAction="ifRoom"  />
      -    ...
      -</menu>
      -
      - -

      If there's not enough room for the item in the action bar, it will appear in the action -overflow.

      - - -
      -

      Using XML attributes from the support library

      -Notice that the {@code showAsAction} attribute above uses a custom namespace defined in the -{@code } tag. This is necessary when using any XML attributes defined by the support -library, because these attributes do not exist in the Android framework on older devices. -So you must use your own namespace as a prefix for all attributes defined by the support library. -

      -
      - -

      If your menu item supplies both a title and an icon—with the {@code title} and -{@code icon} attributes—then the action item shows only the icon by default. If you -want to display the text title, add {@code "withText"} to the {@code showAsAction} -attribute. For example:

      - -
      -<item yourapp:showAsAction="ifRoom|withText" ... />
      -
      - -

      Note: The {@code "withText"} value is a hint to the -action bar that the text title should appear. The action bar will show the title when possible, but -might not if an icon is available and the action bar is constrained for space.

      - -

      You should always define the {@code title} for each item even if you don't declare that -the title appear with the action item, for the following reasons:

      -
        -
      • If there's not enough room in the action bar for the action item, the menu item appears -in the overflow where only the title appears.
      • -
      • Screen readers for sight-impaired users read the menu item's title.
      • -
      • If the action item appears with only the icon, a user can long-press the item to reveal a -tool-tip that displays the action title.
      • -
      - -

      The {@code icon} is optional, but recommended. For icon design recommendations, -see the Iconography design -guide. You can also download a set of standard action bar icons (such as for Search or Discard) -from the Downloads page.

      - -

      You can also use {@code "always"} to declare that an item always appear as an action button. -However, you should not force an item to appear in the action bar this -way. Doing so can create layout problems on devices with a narrow screen. It's best to instead -use {@code "ifRoom"} to request that an item appear in the action bar, but allow the system to move -it into the overflow when there's not enough room. However, it might be necessary to use this value -if the item includes an action view that cannot be collapsed and -must always be visible to provide access to a critical feature.

      - - - - -

      Handling clicks on action items

      - -

      When the user presses an action, the system calls your activity's {@link -android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method. Using the -{@link android.view.MenuItem} passed to this method, you can identify the action by calling {@link -android.view.MenuItem#getItemId()}. This returns the unique ID provided by the {@code } -tag's {@code id} attribute so you can perform the appropriate action. For example:

      - -
      -@Override
      -public boolean onOptionsItemSelected(MenuItem item) {
      -    // Handle presses on the action bar items
      -    switch (item.getItemId()) {
      -        case R.id.action_search:
      -            openSearch();
      -            return true;
      -        case R.id.action_compose:
      -            composeMessage();
      -            return true;
      -        default:
      -            return super.onOptionsItemSelected(item);
      -    }
      -}
      -
      - -

      Note: If you inflate menu items from a fragment, via the {@link -android.app.Fragment} class's {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()} -callback, the system calls {@link -android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} for that -fragment when the user selects one of those items. However, the activity gets a chance to -handle the event first, so the system first calls {@link -android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} on the activity, -before calling the same callback for the fragment. To ensure that any fragments in the -activity also have a chance to handle the callback, always pass the call to the superclass -as the default behavior instead of returning {@code false} when you do not handle the item.

      - - - -
      - -

      Figure 3. Mock-ups showing an action bar with -tabs (left), then with split action bar (middle); and with the app icon and title disabled -(right).

      -

      -
      - -

      Using split action bar

      - -

      Split action bar provides a separate -bar at the bottom of the screen to display all action items when the activity is running on -a narrow screen (such as a portrait-oriented handset).

      - -

      Separating the action items this way -ensures that a reasonable amount of space is available to display all your action -items on a narrow screen, while leaving room for navigation and title elements at the top.

      - -

      To enable split action bar when using the support library, you must do two things:

      -
        -
      1. Add {@code uiOptions="splitActionBarWhenNarrow"} to each -{@code } -element or to the -{@code } -element. This attribute is understood only by API level 14 and higher (it is ignored -by older versions).
      2. -
      3. To support older versions, add a {@code } - element as a child of each - {@code } - element that declares the same value for {@code "android.support.UI_OPTIONS"}.
      4. -
      - -

      For example:

      - -
      -<manifest ...>
      -    <activity uiOptions="splitActionBarWhenNarrow" ... >
      -        <meta-data android:name="android.support.UI_OPTIONS"
      -                   android:value="splitActionBarWhenNarrow" />
      -    </activity>
      -</manifest>
      -
      - - -

      Using split action bar also allows navigation tabs to collapse into the -main action bar if you remove the icon and title (as shown on the right in figure 3). -To create this effect, disable the action bar -icon and title with {@link android.support.v7.app.ActionBar#setDisplayShowHomeEnabled -setDisplayShowHomeEnabled(false)} and {@link -android.support.v7.app.ActionBar#setDisplayShowTitleEnabled setDisplayShowTitleEnabled(false)}.

      - - - -

      Navigating Up with the App Icon

      - - -
      -

      Design Guide

      -

      Navigation with Back and Up

      -
      -
      - -
      - -

      Figure 4. The Up button in Gmail.

      -
      - -

      Enabling the app icon as an Up button allows the user to navigate your app based -on the hierarchical relationships between screens. For instance, if screen A displays a list of -items, and selecting an item leads to screen B, then -screen B should include the Up button, which returns to screen A.

      - -

      Note: Up navigation is distinct from the back navigation provided -by the system Back button. The Back button is used to navigate in reverse -chronological order through the history of screens the user has recently worked with. It is -generally based on the temporal relationships between screens, rather than the app's hierarchy -structure (which is the basis for up navigation).

      - -

      To enable the app icon as an Up button, call {@link -android.support.v7.app.ActionBar#setDisplayHomeAsUpEnabled setDisplayHomeAsUpEnabled()}. -For example:

      - -
      -@Override
      -protected void onCreate(Bundle savedInstanceState) {
      -    super.onCreate(savedInstanceState);
      -    setContentView(R.layout.activity_details);
      -
      -    ActionBar actionBar = getSupportActionBar();
      -    actionBar.setDisplayHomeAsUpEnabled(true);
      -    ...
      -}
      -
      - -

      Now the icon in the action bar appears with the Up caret (as shown in figure 4). -However, it won't do anything by default. To specify the activity to open when the -user presses Up button, you have two options:

      - -
        -
      • Specify the parent activity in the manifest file. -

        This is the best option when the parent activity is always the same. By -declaring in the manifest which activity is the parent, the action bar automatically performs the -correct action when the user presses the Up button.

        - -

        Beginning in Android 4.1 (API level 16), you can declare the parent with the {@code parentActivityName} -attribute in the {@code -} element.

        -

        To support older devices with the support library, also -include a {@code -} element that specifies -the parent activity as the value for {@code android.support.PARENT_ACTIVITY}. For example:

        -
        -<application ... >
        -    ...
        -    <!-- The main/home activity (has no parent activity) -->
        -    <activity
        -        android:name="com.example.myfirstapp.MainActivity" ...>
        -        ...
        -    </activity>
        -    <!-- A child of the main activity -->
        -    <activity
        -        android:name="com.example.myfirstapp.DisplayMessageActivity"
        -        android:label="@string/title_activity_display_message"
        -        android:parentActivityName="com.example.myfirstapp.MainActivity" >
        -        <!-- Parent activity meta-data to support API level 7+ -->
        -        <meta-data
        -            android:name="android.support.PARENT_ACTIVITY"
        -            android:value="com.example.myfirstapp.MainActivity" />
        -    </activity>
        -</application>
        -
        - -

        Once the parent activity is specified in the manifest like this and you enable the Up - button with {@link -android.support.v7.app.ActionBar#setDisplayHomeAsUpEnabled setDisplayHomeAsUpEnabled()}, your work -is done and the action bar properly navigates up.

        -
      • - - -
      • Or, override {@link -android.support.v7.app.ActionBarActivity#getSupportParentActivityIntent()} and {@link -android.support.v7.app.ActionBarActivity#onCreateSupportNavigateUpTaskStack -onCreateSupportNavigateUpTaskStack()} in your activity.
      • - -

        This is appropriate when the parent activity may be different depending - on how the user arrived at the current screen. That is, if there are many paths that the user - could have taken to reach the current screen, the Up button should navigate - backward along the path the user actually followed to get there.

        - -

        The system calls {@link -android.support.v7.app.ActionBarActivity#getSupportParentActivityIntent()} when the user presses -the Up button while navigating your app (within your app's own task). If the activity that -should open upon up navigation differs depending on how the user arrived at the current location, -then you should override this method to return the {@link -android.content.Intent} that starts the appropriate parent activity.

        - -

        The system calls {@link -android.support.v7.app.ActionBarActivity#onCreateSupportNavigateUpTaskStack -onCreateSupportNavigateUpTaskStack()} for your activity when the user presses the Up -button while your activity is running in a task that does not belong to your app. Thus, -you must use the {@link android.support.v4.app.TaskStackBuilder} passed to this method to construct -the appropriate back stack that should be synthesized when the user navigates up.

        - -

        Even if you override {@link -android.support.v7.app.ActionBarActivity#getSupportParentActivityIntent()} to specify up navigation -as the user navigates your app, you can avoid the need to implement {@link -android.support.v7.app.ActionBarActivity#onCreateSupportNavigateUpTaskStack -onCreateSupportNavigateUpTaskStack()} by declaring "default" parent activities in the manifest file -as shown above. Then the default implementation of {@link -android.support.v7.app.ActionBarActivity#onCreateSupportNavigateUpTaskStack -onCreateSupportNavigateUpTaskStack()} will synthesize a back stack based on the parent activities -declared in the manifest.

        - - -
      - -

      Note: -If you've built your app hierarchy using a series of fragments instead of multiple -activities, then neither of the above options will work. Instead, to navigate up through your -fragments, override {@link android.support.v7.app.ActionBarActivity#onSupportNavigateUp()} -to perform the appropriate fragment transaction—usually by popping -the current fragment from the back stack by calling {@link -android.support.v4.app.FragmentManager#popBackStack()}.

      - -

      For more information about implementing Up navigation, read -Providing Up Navigation.

      - - - -

      Adding an Action View

      - -
      - -

      Figure 5. An action bar with a collapsible -{@link android.support.v7.widget.SearchView}.

      -
      - - -

      An action view is a widget that appears in the action bar as a substitute for an action -button. An action view provides fast access to rich actions without changing activities or -fragments, and without replacing the action bar. For example, if you have an action for Search, you -can add an action view to -embeds a {@link android.support.v7.widget.SearchView} widget in the action bar, as shown in figure -5.

      - -

      To declare an action view, use either the {@code -actionLayout} or {@code actionViewClass} attribute to specify either a layout -resource or widget class to use, respectively. For example, here's how to add -the {@link android.support.v7.widget.SearchView} widget:

      - -
      -<?xml version="1.0" encoding="utf-8"?>
      -<menu xmlns:android="http://schemas.android.com/apk/res/android"
      -      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
      -    <item android:id="@+id/action_search"
      -          android:title="@string/action_search"
      -          android:icon="@drawable/ic_action_search"
      -          yourapp:showAsAction="ifRoom|collapseActionView"
      -          yourapp:actionViewClass="android.support.v7.widget.SearchView" />
      -</menu>
      -
      - -

      Notice that the {@code showAsAction} attribute also includes the {@code "collapseActionView"} -value. This is optional and declares that the action view should be collapsed into a -button. (This behavior is explained further in the following section about -Handling collapsible action views.)

      - -

      If you need to configure the action view (such as to add event listeners), you can do so during -the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} callback. You can -acquire the action view object by calling the static method {@link -android.support.v4.view.MenuItemCompat#getActionView MenuItemCompat.getActionView()} and passing it -the corresponding {@link android.view.MenuItem}. For example, the search widget from the above -sample is acquired like this:

      - - -
      -@Override
      -public boolean onCreateOptionsMenu(Menu menu) {
      -    getMenuInflater().inflate(R.menu.main_activity_actions, menu);
      -    MenuItem searchItem = menu.findItem(R.id.action_search);
      -    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
      -    // Configure the search info and add any event listeners
      -    ...
      -    return super.onCreateOptionsMenu(menu);
      -}
      -
      - -
      -

      On API level 11 or higher

      -

      Get the action view by calling {@link android.view.MenuItem#getActionView} on the -corresponding {@link android.view.MenuItem}:

      -
      menu.findItem(R.id.action_search).getActionView()
      -
      - -

      For more information about using the search widget, see Creating a Search Interface.

      - - - -

      Handling collapsible action views

      - -

      To preserve the action bar space, you can collapse your action view into an action button. -When collapsed, the system might place the action -into the action overflow, but the -action view still appears in the action bar when the user selects it. You can make your action -view collapsible by adding {@code "collapseActionView"} to the {@code showAsAction} -attribute, as shown in the XML above.

      - -

      Because the system expands the action view when the user selects the action, you -do not need to respond to the item in the {@link -android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} callback. The system still calls -{@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()}, but if -you return {@code true} (indicating you've handled the event instead), then the -action view will not expand.

      - -

      The system also collapses your action view when the user presses the Up button -or Back button.

      - -

      If you need to update your activity based on the visibility of your action view, you can receive -callbacks when the action is expanded and collapsed by defining an {@link -android.support.v4.view.MenuItemCompat.OnActionExpandListener OnActionExpandListener} and -passing it to {@link android.support.v4.view.MenuItemCompat#setOnActionExpandListener -setOnActionExpandListener()}. For example:

      - - -
      -@Override
      -public boolean onCreateOptionsMenu(Menu menu) {
      -    getMenuInflater().inflate(R.menu.options, menu);
      -    MenuItem menuItem = menu.findItem(R.id.actionItem);
      -    ...
      -
      -    // When using the support library, the setOnActionExpandListener() method is
      -    // static and accepts the MenuItem object as an argument
      -    MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
      -        @Override
      -        public boolean onMenuItemActionCollapse(MenuItem item) {
      -            // Do something when collapsed
      -            return true;  // Return true to collapse action view
      -        }
      -
      -        @Override
      -        public boolean onMenuItemActionExpand(MenuItem item) {
      -            // Do something when expanded
      -            return true;  // Return true to expand action view
      -        }
      -    });
      -}
      -
      - - - - -

      Adding an Action Provider

      - -
      - -

      Figure 6. An action bar with - {@link android.widget.ShareActionProvider} expanded to show share targets.

      -
      - -

      Similar to an action view, an action provider -replaces an action button with a customized layout. However, -unlike an action view, an action provider takes control of all the action's behaviors -and an action provider can display a submenu when pressed.

      - -

      To declare an action provider, supply the {@code actionViewClass} attribute in the -menu {@code } tag with a fully-qualified class name for an -{@link android.support.v4.view.ActionProvider}.

      - -

      You can build your own action provider by extending the {@link -android.support.v4.view.ActionProvider} class, but Android provides some pre-built action providers -such as {@link android.support.v7.widget.ShareActionProvider}, which facilitates a "share" action -by showing a list of possible apps for sharing directly in the action bar (as shown in figure -6).

      - -

      Because each {@link android.support.v4.view.ActionProvider} class defines its own action -behaviors, you don't need to listen for the action in the {@link -android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method. If necessary though, -you can still listen for the click event in the {@link android.app.Activity#onOptionsItemSelected -onOptionsItemSelected()} method in case you need to simultaneously perform another action. But be -sure to return {@code false} so that the the action provider still receives the {@link -android.support.v4.view.ActionProvider#onPerformDefaultAction()} callback to perform its intended -action.

      - - -

      However, if the action provider provides a submenu of actions, then your -activity does not receive a call to {@link android.app.Activity#onOptionsItemSelected -onOptionsItemSelected()} when the user opens the list or selects one of the submenu items.

      - - - -

      Using the ShareActionProvider

      - -

      To add a "share" action with {@link android.support.v7.widget.ShareActionProvider}, -define the {@code actionProviderClass} for an {@code } tag with -the {@link android.support.v7.widget.ShareActionProvider} class. For example:

      - -
      -<?xml version="1.0" encoding="utf-8"?>
      -<menu xmlns:android="http://schemas.android.com/apk/res/android"
      -      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
      -    <item android:id="@+id/action_share"
      -          android:title="@string/share"
      -          yourapp:showAsAction="ifRoom"
      -          yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
      -          />
      -    ...
      -</menu>
      -
      - -

      Now the action provider takes control of the action item and handles both -its appearance and behavior. But you must -still provide a title for the item to be used when it appears in the action overflow.

      - -

      The only thing left to do is define -the {@link android.content.Intent} you want to use for sharing. To do so, edit -your {@link -android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method to call {@link -android.support.v4.view.MenuItemCompat#getActionProvider MenuItemCompat.getActionProvider()} -and pass it the {@link android.view.MenuItem} holding the action provider. Then call {@link -android.support.v7.widget.ShareActionProvider#setShareIntent setShareIntent()} on the -returned {@link android.support.v7.widget.ShareActionProvider} and pass it an -{@link android.content.Intent#ACTION_SEND} intent with the appropriate content attached.

      - -

      You should call {@link -android.support.v7.widget.ShareActionProvider#setShareIntent setShareIntent()} once during {@link -android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} to initialize the share action, -but because the user context might change, you must update the intent any time the shareable -content changes by again calling {@link -android.support.v7.widget.ShareActionProvider#setShareIntent setShareIntent()}.

      - -

      For example:

      - -
      -private ShareActionProvider mShareActionProvider;
      -
      -@Override
      -public boolean onCreateOptionsMenu(Menu menu) {
      -    getMenuInflater().inflate(R.menu.main_activity_actions, menu);
      -
      -    // Set up ShareActionProvider's default share intent
      -    MenuItem shareItem = menu.findItem(R.id.action_share);
      -    mShareActionProvider = (ShareActionProvider)
      -            MenuItemCompat.getActionProvider(shareItem);
      -    mShareActionProvider.setShareIntent(getDefaultIntent());
      -
      -    return super.onCreateOptionsMenu(menu);
      -}
      -
      -/** Defines a default (dummy) share intent to initialize the action provider.
      -  * However, as soon as the actual content to be used in the intent
      -  * is known or changes, you must update the share intent by again calling
      -  * mShareActionProvider.{@link android.support.v7.widget.ShareActionProvider#setShareIntent setShareIntent()}
      -  */
      -private Intent getDefaultIntent() {
      -    Intent intent = new Intent(Intent.ACTION_SEND);
      -    intent.setType("image/*");
      -    return intent;
      -}
      -
      - -

      The {@link android.support.v7.widget.ShareActionProvider} now handles all user interaction with -the item and you do not need to handle click events from the {@link -android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} callback method.

      - - -

      By default, the {@link android.support.v7.widget.ShareActionProvider} retains a ranking for each -share target based on how often the user selects each one. The share targets used more frequently -appear at the top of the drop-down list and the target used most often appears directly in the -action bar as the default share target. By default, the ranking information is saved in a private -file with a name specified by {@link -android.support.v7.widget.ShareActionProvider#DEFAULT_SHARE_HISTORY_FILE_NAME}. If you use the -{@link android.support.v7.widget.ShareActionProvider} or an extension of it for only one type of -action, then you should continue to use this default history file and there's nothing you need to -do. However, if you use {@link android.support.v7.widget.ShareActionProvider} or an extension of it -for multiple actions with semantically different meanings, then each {@link -android.support.v7.widget.ShareActionProvider} should specify its own history file in order to -maintain its own history. To specify a different history file for the {@link -android.support.v7.widget.ShareActionProvider}, call {@link -android.support.v7.widget.ShareActionProvider#setShareHistoryFileName setShareHistoryFileName()} -and provide an XML file name (for example, {@code "custom_share_history.xml"}).

      - - -

      Note: Although the {@link -android.support.v7.widget.ShareActionProvider} ranks share targets based on frequency of use, the -behavior is extensible and extensions of {@link android.support.v7.widget.ShareActionProvider} can -perform different behaviors and ranking based on the history file (if appropriate).

      - - - - -

      Creating a custom action provider

      - -

      Creating your own action provider allows you to re-use and manage dynamic action item -behaviors in a self-contained module, rather than handle action item transformations and -behaviors in your fragment or activity -code. As shown in the previous section, Android already provides an implementation of {@link -android.support.v4.view.ActionProvider} for share actions: the {@link -android.support.v7.widget.ShareActionProvider}.

      - -

      To create your own action provider for a different action, simply extend the -{@link android.support.v4.view.ActionProvider} class and implement -its callback methods as appropriate. Most importantly, you should implement the following:

      - -
      -
      {@link android.support.v4.view.ActionProvider#ActionProvider ActionProvider()}
      -
      This constructor passes you the application {@link android.content.Context}, which you -should save in a member field to use in the other callback methods.
      - -
      {@link android.support.v4.view.ActionProvider#onCreateActionView(MenuItem)}
      -
      This is where you define the action view for the item. Use the {@link -android.content.Context} acquired from the constructor to instantiate a {@link -android.view.LayoutInflater} and inflate your action view layout from an XML resource, then hook -up event listeners. For example: -
      -public View onCreateActionView(MenuItem forItem) {
      -    // Inflate the action view to be shown on the action bar.
      -    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
      -    View view = layoutInflater.inflate(R.layout.action_provider, null);
      -    ImageButton button = (ImageButton) view.findViewById(R.id.button);
      -    button.setOnClickListener(new View.OnClickListener() {
      -        @Override
      -        public void onClick(View v) {
      -            // Do something...
      -        }
      -    });
      -    return view;
      -}
      -
      -
      - -
      {@link android.support.v4.view.ActionProvider#onPerformDefaultAction()}
      -
      The system calls this when the menu item is selected from the action overflow and the -action provider should perform a default action for the menu item. -

      However, if your action provider provides a submenu, through the {@link -android.support.v4.view.ActionProvider#onPrepareSubMenu onPrepareSubMenu()} callback, then the -submenu appears even when the action provider is placed in the action overflow. Thus, {@link -android.support.v4.view.ActionProvider#onPerformDefaultAction()} is never called when there is a -submenu.

      - -

      Note: An activity or a fragment that implements {@link -android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} can override the action -provider's default behavior (unless it uses a submenu) by handling the item-selected event (and -returning true), in which case, the system does not call {@link -android.support.v4.view.ActionProvider#onPerformDefaultAction()}.

      - -
      -
      - -

      For an example extension of {@link android.view.ActionProvider}, see ActionBarSettingsActionProviderActivity.

      - - - - -

      Adding Navigation Tabs

      - - -

      Figure 7. Action bar tabs on a wide screen.

      - - -
      -

      Design Guide

      -

      Tabs

      -
      -
      - - -
      -

      Also read

      -

      Creating Swipe Views with Tabs

      -
      -
      - - -
      - -

      Figure 8. Tabs on a narrow screen.

      -
      - -

      Tabs in the action bar make it easy for users to explore and switch between different views in -your app. The tabs provided by the {@link android.support.v7.app.ActionBar} are ideal because they -adapt to different screen sizes. For example, when the screen is wide enough the tabs appear in the -action bar alongside the action buttons (such as when on a tablet, shown in figure 7), while when -on a narrow screen they appear in a separate bar (known as the "stacked action bar", shown in -figure 8). In some cases, the Android system will instead show your tab items as a drop-down list -to ensure the best fit in the action bar.

      - -

      To get started, your layout must include a {@link android.view.ViewGroup} in which you place -each {@link android.app.Fragment} associated with a tab. Be sure the {@link android.view.ViewGroup} -has a resource ID so you can reference it from your code and swap the tabs within it. -

      - -

      Once you determine where the fragments appear in the layout, the basic procedure to add tabs -is:

      -
        -
      1. Implement the {@link android.support.v7.app.ActionBar.TabListener} interface. This interface - provides callbacks for tab events, such as when the user presses one so you can swap the - tabs.
      2. -
      3. For each tab you want to add, instantiate an {@link android.support.v7.app.ActionBar.Tab} - and set the {@link android.support.v7.app.ActionBar.TabListener} by calling {@link - android.support.v7.app.ActionBar.Tab#setTabListener setTabListener()}. Also set the tab's title - and with {@link android.app.ActionBar.Tab#setText setText()} (and optionally, an icon with - {@link android.app.ActionBar.Tab#setIcon setIcon()}).
      4. -
      5. Then add each tab to the action bar by calling {@link android.support.v7.app.ActionBar#addTab - addTab()}.
      6. -
      - -

      Notice that the {@link android.support.v7.app.ActionBar.TabListener} -callback methods don't specify which fragment is associated with the tab, but merely which -{@link android.support.v7.app.ActionBar.Tab} was selected. -You must define your own association -between each {@link android.app.ActionBar.Tab} and the appropriate {@link android.app.Fragment} that -it represents. There are several ways you -can define the association, depending on your design.

      - -

      For example, here's how you might implement the {@link android.app.ActionBar.TabListener} -such that each tab uses its own instance of the listener:

      -
      -public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
      -    private Fragment mFragment;
      -    private final Activity mActivity;
      -    private final String mTag;
      -    private final Class<T> mClass;
      -
      -    /** Constructor used each time a new tab is created.
      -      * @param activity  The host Activity, used to instantiate the fragment
      -      * @param tag  The identifier tag for the fragment
      -      * @param clz  The fragment's Class, used to instantiate the fragment
      -      */
      -    public TabListener(Activity activity, String tag, Class<T> clz) {
      -        mActivity = activity;
      -        mTag = tag;
      -        mClass = clz;
      -    }
      -
      -    /* The following are each of the {@link android.app.ActionBar.TabListener} callbacks */
      -
      -    public void onTabSelected(Tab tab, FragmentTransaction ft) {
      -        // Check if the fragment is already initialized
      -        if (mFragment == null) {
      -            // If not, instantiate and add it to the activity
      -            mFragment = Fragment.instantiate(mActivity, mClass.getName());
      -            ft.add(android.R.id.content, mFragment, mTag);
      -        } else {
      -            // If it exists, simply attach it in order to show it
      -            ft.attach(mFragment);
      -        }
      -    }
      -
      -    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
      -        if (mFragment != null) {
      -            // Detach the fragment, because another one is being attached
      -            ft.detach(mFragment);
      -        }
      -    }
      -
      -    public void onTabReselected(Tab tab, FragmentTransaction ft) {
      -        // User selected the already selected tab. Usually do nothing.
      -    }
      -}
      -
      - -

      Caution: You must not call {@link -android.app.FragmentTransaction#commit} for the fragment transaction in each of these -callbacks—the system calls it for you and it may throw an exception if you call it yourself. -You also cannot add these fragment transactions to the back stack.

      - -

      In this example, the listener simply attaches ({@link android.app.FragmentTransaction#attach -attach()}) a fragment to the activity layout—or if not instantiated, creates the fragment and -adds ({@link android.app.FragmentTransaction#add add()}) it to the layout (as a child of the {@code -android.R.id.content} view group)—when the respective tab is selected, and detaches ({@link -android.app.FragmentTransaction#detach detach()}) it when the tab is unselected.

      - -

      All that remains is to create each {@link android.app.ActionBar.Tab} and add it to the {@link -android.app.ActionBar}. Additionally, you must call {@link -android.app.ActionBar#setNavigationMode(int) setNavigationMode(NAVIGATION_MODE_TABS)} to make the -tabs visible.

      - -

      For example, the following code adds two tabs using the listener defined above:

      - -
      -@Override
      -protected void onCreate(Bundle savedInstanceState) {
      -    super.onCreate(savedInstanceState);
      -    // Notice that setContentView() is not used, because we use the root
      -    // android.R.id.content as the container for each fragment
      -
      -    // setup action bar for tabs
      -    ActionBar actionBar = getSupportActionBar();
      -    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
      -    actionBar.setDisplayShowTitleEnabled(false);
      -
      -    Tab tab = actionBar.newTab()
      -                       .setText(R.string.artist)
      -                       .setTabListener(new TabListener<ArtistFragment>(
      -                               this, "artist", ArtistFragment.class));
      -    actionBar.addTab(tab);
      -
      -    tab = actionBar.newTab()
      -                   .setText(R.string.album)
      -                   .setTabListener(new TabListener<AlbumFragment>(
      -                           this, "album", AlbumFragment.class));
      -    actionBar.addTab(tab);
      -}
      -
      - - -

      If your activity stops, you should retain the currently selected tab with the saved instance state so you -can open the appropriate tab when the user returns. When it's time to save the state, you can query -the currently selected tab with {@link -android.support.v7.app.ActionBar#getSelectedNavigationIndex()}. This returns the index position of -the selected tab.

      - - -

      Caution: It's important that you save the state of each fragment -so when users switch fragments with the tabs and then return to a previous -fragment, it looks the way it did when they left. Some of the state is saved by default, but you -may need to manually save state for customized views. For information about saving the state of your -fragment, see the Fragments -API guide.

      - -

      Note: The above implementation for {@link -android.support.v7.app.ActionBar.TabListener} is one of several possible techniques. Another popular -option is to use {@link android.support.v4.view.ViewPager} to manage the fragments so users -can also use a swipe gesture to switch tabs. In this case, you simply tell the -{@link android.support.v4.view.ViewPager} the current tab position in the -{@link android.support.v7.app.ActionBar.TabListener#onTabSelected onTabSelected()} callback. -For more information, read -Creating Swipe Views with Tabs.

      - - - - - - - -
      - -

      Figure 9. A drop-down navigation list in the -action bar.

      -
      - -

      As another mode of navigation (or filtering) for your activity, the action bar offers a built -in drop-down list (also known as a "spinner"). For example, the drop-down list can offer different -modes by which content in the activity is sorted.

      - -

      Using the drop-down list is useful when changing the content is important but not necessarily a -frequent occurrence. In cases where switching the content is more frequent, -you should use navigation tabs instead.

      - - -

      The basic procedure to enable drop-down navigation is:

      - -
        -
      1. Create a {@link android.widget.SpinnerAdapter} that provides the -list of selectable items for the drop-down and the layout to use when drawing each item in the -list.
      2. -
      3. Implement {@link android.support.v7.app.ActionBar.OnNavigationListener} to define the - behavior that occurs when the user selects an item from the list.
      4. -
      5. During your activity's {@link android.app.Activity#onCreate -onCreate()} method, enable the action bar's drop-down list by calling {@link -android.support.v7.app.ActionBar#setNavigationMode setNavigationMode(NAVIGATION_MODE_LIST)}. -
      6. -
      7. Set the callback for the drop-down list with {@link -android.support.v7.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}. -For example: -
        -actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);
        -
        -

        This method takes your {@link android.widget.SpinnerAdapter} and {@link -android.support.v7.app.ActionBar.OnNavigationListener}.

        -
      8. -
      - -

      This procedure is relatively short, but implementing the {@link android.widget.SpinnerAdapter} -and {@link android.app.ActionBar.OnNavigationListener} is where most of the work is done. There are -many ways you can implement these to define the functionality for your drop-down navigation and -implementing various types of {@link android.widget.SpinnerAdapter} is beyond the scope of this -document (you should refer to the {@link android.widget.SpinnerAdapter} class reference for more -information). However, below is an example for a {@link android.widget.SpinnerAdapter} and {@link -android.app.ActionBar.OnNavigationListener} to get you started (click the title to reveal the -sample).

      - - - -
      - -

      - - Example SpinnerAdapter and OnNavigationListener -

      - -
      - -

      {@link android.widget.SpinnerAdapter} is an adapter that provides data for a spinner widget, -such as the drop-down list in the action bar. {@link android.widget.SpinnerAdapter} is an interface -that you can implement, but Android includes some useful implementations that you can extend, such -as {@link android.widget.ArrayAdapter} and {@link -android.widget.SimpleCursorAdapter}. For example, here's an easy way to create a {@link -android.widget.SpinnerAdapter} by using {@link android.widget.ArrayAdapter} implementation, which -uses a string array as the data source:

      - -
      -SpinnerAdapter mSpinnerAdapter = ArrayAdapter.createFromResource(this,
      -        R.array.action_list, android.R.layout.simple_spinner_dropdown_item);
      -
      - -

      The {@link android.widget.ArrayAdapter#createFromResource createFromResource()} method takes -three parameters: the application {@link android.content.Context}, the resource ID for the string -array, and the layout to use for each list item.

      - -

      A string array -defined in a resource looks like this:

      - -
      -<?xml version="1.0" encoding="utf-8"?>
      -<resources>
      -    <string-array name="action_list">
      -        <item>Mercury</item>
      -        <item>Venus</item>
      -        <item>Earth</item>
      -    </string-array>
      -</resources>
      -
      - -

      The {@link android.widget.ArrayAdapter} returned by {@link -android.widget.ArrayAdapter#createFromResource createFromResource()} is complete and ready for you -to pass it to {@link android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()} -(in step 4 from above). Before you do, though, you need to create the {@link -android.app.ActionBar.OnNavigationListener OnNavigationListener}.

      - - -

      Your implementation of {@link android.app.ActionBar.OnNavigationListener} is where you handle -fragment changes or other modifications to your activity when the user selects an item from the -drop-down list. There's only one callback method to implement in the listener: {@link -android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()}.

      - -

      The {@link -android.app.ActionBar.OnNavigationListener#onNavigationItemSelected onNavigationItemSelected()} -method receives the position of the item in the list and a unique item ID provided by the {@link -android.widget.SpinnerAdapter}.

      - -

      Here's an example that instantiates an anonymous implementation of {@link -android.app.ActionBar.OnNavigationListener OnNavigationListener}, which inserts a {@link -android.app.Fragment} into the -layout container identified by {@code R.id.fragment_container}:

      - -
      -mOnNavigationListener = new OnNavigationListener() {
      -  // Get the same strings provided for the drop-down's ArrayAdapter
      -  String[] strings = getResources().getStringArray(R.array.action_list);
      -
      -  @Override
      -  public boolean onNavigationItemSelected(int position, long itemId) {
      -    // Create new fragment from our own Fragment class
      -    ListContentFragment newFragment = new ListContentFragment();
      -    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
      -
      -    // Replace whatever is in the fragment container with this fragment
      -    // and give the fragment a tag name equal to the string at the position
      -    // selected
      -    ft.replace(R.id.fragment_container, newFragment, strings[position]);
      -
      -    // Apply changes
      -    ft.commit();
      -    return true;
      -  }
      -};
      -
      - -

      This instance of {@link android.app.ActionBar.OnNavigationListener OnNavigationListener} is -complete and you can now call {@link android.app.ActionBar#setListNavigationCallbacks -setListNavigationCallbacks()} (in step 4), passing the {@link android.widget.ArrayAdapter} and this -{@link android.app.ActionBar.OnNavigationListener OnNavigationListener}.

      - -

      In this example, when the user selects an item from the drop-down list, a fragment is added to -the layout (replacing the current fragment in the {@code R.id.fragment_container} view). The -fragment added is given a tag that uniquely identifies it, which is the same string used to -identify the fragment in the drop-down list.

      - -

      Here's a look at the {@code ListContentFragment} class that defines each fragment in this -example:

      - -
      -public class ListContentFragment extends Fragment {
      -    private String mText;
      -
      -    @Override
      -    public void onAttach(Activity activity) {
      -      // This is the first callback received; here we can set the text for
      -      // the fragment as defined by the tag specified during the fragment
      -      // transaction
      -      super.onAttach(activity);
      -      mText = getTag();
      -    }
      -
      -    @Override
      -    public View onCreateView(LayoutInflater inflater, ViewGroup container,
      -            Bundle savedInstanceState) {
      -        // This is called to define the layout for the fragment;
      -        // we just create a TextView and set its text to be the fragment tag
      -        TextView text = new TextView(getActivity());
      -        text.setText(mText);
      -        return text;
      -    }
      -}
      -
      - -
      - -
      - - - - - - - -

      Styling the Action Bar

      - -

      If you want to implement a visual design that represents your app's brand, the action bar allows -you to customize each detail of its appearance, including the action bar color, text colors, button -styles, and more. To do so, you need to use Android's style and theme framework to restyle the action bar -using special style properties.

      - -

      Caution: For all background drawables you provide, be sure to -use Nine-Patch drawables -to allow stretching. The nine-patch image should be smaller than 40dp tall and 30dp -wide.

      - - - -

      General appearance

      - -
      -
      {@link android.R.attr#actionBarStyle - actionBarStyle}
      -
      Specifies a style resource that defines various style properties - for the action bar. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionBar - Widget.AppCompat.ActionBar}, which is what you should use as the parent style.

      -

      Supported styles include:

      -
      -
      {@link android.R.attr#background}
      -
      Defines a drawable resource for the action bar background.
      -
      {@link android.R.attr#backgroundStacked}
      -
      Defines a drawable resource for the stacked action bar - (the tabs).
      -
      {@link android.R.attr#backgroundSplit}
      -
      Defines a drawable resource for the split action bar.
      -
      {@link android.R.attr#actionButtonStyle}
      -
      Defines a style resource for action buttons. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionButton - Widget.AppCompat.ActionButton}, which is what you should use as the parent style.

      -
      -
      {@link android.R.attr#actionOverflowButtonStyle}
      -
      Defines a style resource for overflow action items. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionButton_Overflow - Widget.AppCompat.ActionButton.Overflow}, which is what you should use as the parent style.

      -
      -
      {@link android.R.attr#displayOptions}
      -
      Defines one or more action bar display options, such as whether to use the app logo, - show the activity title, or enable the Up action. See {@link - android.R.attr#displayOptions} for all possible values. -
      {@link android.R.attr#divider}
      -
      Defines a drawable resource for the divider between action items.
      -
      {@link android.R.attr#titleTextStyle}
      -
      Defines a style resource for the action bar title. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#TextAppearance_AppCompat_Widget_ActionBar_Title - TextAppearance.AppCompat.Widget.ActionBar.Title}, which is what you should use as the parent - style.

      -
      -
      - -
      {@link android.R.attr#windowActionBarOverlay - windowActionBarOverlay}
      -
      Declares whether the action bar should overlay the activity layout rather than offset the -activity's layout position (for example, the Gallery app uses overlay mode). This is -{@code false} by default. -

      Normally, the action bar requires its own space on the screen and your activity layout fills in -what's left over. When the action bar is in overlay mode, your activity layout uses all the -available space and the system draws the action bar on top. Overlay mode can be useful if you want -your content to keep a fixed size and position when the action bar is hidden and shown. You might -also like to use it purely as a visual effect, because you can use a semi-transparent background -for the action bar so the user can still see some of your activity layout behind the action -bar.

      -

      Note: The {@link android.R.style#Theme_Holo Holo} theme families -draw the action bar with a semi-transparent background by default. However, you can modify it with -your own styles and the {@link android.R.style#Theme_DeviceDefault DeviceDefault} theme on -different devices might use an opaque background by default.

      -

      When overlay mode is enabled, your activity layout has no awareness of the action bar lying on -top of it. So, you must be careful not to place any important information or UI components in the -area overlaid by the action bar. If appropriate, you can refer to the platform's value for {@link -android.R.attr#actionBarSize} to determine the height of the action bar, by referencing it -in your XML layout. For example:

      -
      -<SomeView
      -    ...
      -    android:layout_marginTop="?android:attr/actionBarSize" />
      -
      -

      You can also retrieve the action bar height at runtime with {@link -android.app.ActionBar#getHeight()}. This reflects the height of the action bar at the time it's -called, which might not include the stacked action bar (due to navigation tabs) if called during -early activity lifecycle methods. To see how you can determine the total height at runtime, -including the stacked action bar, see the -{@code TitlesFragment} class in the Honeycomb Gallery sample app.

      - -
      - -
      - - -

      Action items

      - -
      -
      {@link android.R.attr#actionButtonStyle - actionButtonStyle}
      -
      Defines a style resource for the action item buttons. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionButton - Widget.AppCompat.ActionButton}, which is what you should use as the parent style.

      - -
      {@link android.R.attr#actionBarItemBackground - actionBarItemBackground}
      -
      Defines a drawable resource for each action item's background. - This should be a state-list drawable to indicate different selected states.
      - -
      {@link android.R.attr#itemBackground - itemBackground}
      -
      Defines a drawable resource for each action overflow item's background. - This should be a state-list drawable to indicate different selected states.
      - -
      {@link android.R.attr#actionBarDivider - actionBarDivider}
      -
      Defines a drawable resource for the divider between action items.
      - -
      {@link android.R.attr#actionMenuTextColor - actionMenuTextColor}
      -
      Defines a color for text that appears in an action item.
      - -
      {@link android.R.attr#actionMenuTextAppearance - actionMenuTextAppearance}
      -
      Defines a style resource for text that appears in an action item.
      - -
      {@link android.R.attr#actionBarWidgetTheme - actionBarWidgetTheme}
      -
      Defines a theme resource for widgets that are inflated into the action bar as action views.
      -
      - - - - -
      -
      {@link android.R.attr#actionBarTabStyle - actionBarTabStyle}
      -
      Defines a style resource for tabs in the action bar. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionBar_TabView - Widget.AppCompat.ActionBar.TabView}, which is what you should use as the parent style.

      - -
      {@link android.R.attr#actionBarTabBarStyle - actionBarTabBarStyle}
      -
      Defines a style resource for the thin bar that appears below the navigation tabs. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionBar_TabBar - Widget.AppCompat.ActionBar.TabBar}, which is what you should use as the parent style.

      - -
      {@link android.R.attr#actionBarTabTextStyle - actionBarTabTextStyle}
      -
      Defines a style resource for text in the navigation tabs. -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_ActionBar_TabText - Widget.AppCompat.ActionBar.TabText}, which is what you should use as the parent style.

      -
      - - - - -
      -
      {@link android.R.attr#actionDropDownStyle - actionDropDownStyle}
      -
      Defines a style for the drop-down navigation (such as the background and text styles). -

      The default for this style for this - is {@link android.support.v7.appcompat.R.style#Widget_AppCompat_Spinner_DropDown_ActionBar - Widget.AppCompat.Spinner.DropDown.ActionBar}, which is what you should use as the parent - style.

      -
      - - -

      Example theme

      - -

      Here's an example that defines a custom theme for an activity, {@code CustomActivityTheme}, -that includes several styles to customize the action bar.

      - -

      Notice that there are two versions for each action bar style property. The first one -includes the {@code android:} prefix on the property name to support API levels 11 and higher -that include these properties in the framework. The second version does not -include the {@code android:} prefix and is for older versions of the platform, on which -the system uses the style property from the support library. The effect for each is the same.

      - -
      -<?xml version="1.0" encoding="utf-8"?>
      -<resources>
      -    <!-- the theme applied to the application or activity -->
      -    <style name="CustomActionBarTheme"
      -           parent="@style/Theme.AppCompat.Light">
      -        <item name="android:actionBarStyle">@style/MyActionBar</item>
      -        <item name="android:actionBarTabTextStyle">@style/TabTextStyle</item>
      -        <item name="android:actionMenuTextColor">@color/actionbar_text</item>
      -
      -        <!-- Support library compatibility -->
      -        <item name="actionBarStyle">@style/MyActionBar</item>
      -        <item name="actionBarTabTextStyle">@style/TabTextStyle</item>
      -        <item name="actionMenuTextColor">@color/actionbar_text</item>
      -    </style>
      -
      -    <!-- general styles for the action bar -->
      -    <style name="MyActionBar"
      -           parent="@style/Widget.AppCompat.ActionBar">
      -        <item name="android:titleTextStyle">@style/TitleTextStyle</item>
      -        <item name="android:background">@drawable/actionbar_background</item>
      -        <item name="android:backgroundStacked">@drawable/actionbar_background</item>
      -        <item name="android:backgroundSplit">@drawable/actionbar_background</item>
      -
      -        <!-- Support library compatibility -->
      -        <item name="titleTextStyle">@style/TitleTextStyle</item>
      -        <item name="background">@drawable/actionbar_background</item>
      -        <item name="backgroundStacked">@drawable/actionbar_background</item>
      -        <item name="backgroundSplit">@drawable/actionbar_background</item>
      -    </style>
      -
      -    <!-- action bar title text -->
      -    <style name="TitleTextStyle"
      -           parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
      -        <item name="android:textColor">@color/actionbar_text</item>
      -    </style>
      -
      -    <!-- action bar tab text -->
      -    <style name="TabTextStyle"
      -           parent="@style/Widget.AppCompat.ActionBar.TabText">
      -        <item name="android:textColor">@color/actionbar_text</item>
      -    </style>
      -</resources>
      -
      -
      - -

      In your manifest file, you can apply the theme to your entire app:

      - -
      -<application android:theme="@style/CustomActionBarTheme" ... />
      -
      - -

      Or to individual activities:

      - -
      -<activity android:theme="@style/CustomActionBarTheme" ... />
      -
      - - -

      Caution: Be certain that each theme and style declares a parent -theme in the {@code +

      +
      +
      +
      + +
      +
      +

      Android Studio
      +The Official IDE for Android

      + +

      Android Studio provides the fastest tools for +building apps on every type of Android device.

      + +

      World-class code editing, debugging, +performance tooling, a flexible build system, and an instant build/deploy +system all allow you to focus on building unique and high quality apps.

      + +

      + Download Android Studio 2.0
      +
      +

      - - - -
      - - - +
      +
      +
      + + + + + +
      +
      +
      +
      +
      + +
      +
      +

      Download Android Studio

      +
      +
      +
      +
      +
      +
      +
      +

      Before downloading, + you must agree to the following terms + and conditions.

      +

      Terms and Conditions

      This is the Android Software Development Kit License Agreement

      1. Introduction

      -1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK. +1.1 The Android Software Development Kit (referred to in the License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK. 1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time. -1.3 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States. +1.3 A "compatible implementation" means any Android device that (i) complies with the Android Compatibility Definition document, which can be found at the Android compatibility website (http://source.android.com/compatibility) and which may be updated from time to time; and (ii) successfully passes the Android Compatibility Test Suite (CTS). + +1.4 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.

      2. Accepting this License Agreement

      -2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement. +2.1 In order to use the SDK, you must first agree to the License Agreement. You may not use the SDK if you do not accept the License Agreement. -2.2 By clicking to accept, you hereby agree to the terms of this License Agreement. +2.2 By clicking to accept, you hereby agree to the terms of the License Agreement. -2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries including the country in which you are resident or from which you use the SDK. +2.3 You may not use the SDK and may not accept the License Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries, including the country in which you are resident or from which you use the SDK. -2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity. +2.4 If you are agreeing to be bound by the License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the SDK on behalf of your employer or other entity.

      3. SDK License from Google

      -3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable and non-exclusive license to use the SDK solely to develop applications to run on the Android platform. +3.1 Subject to the terms of the License Agreement, Google grants you a limited, worldwide, royalty-free, non-assignable, non-exclusive, and non-sublicensable license to use the SDK solely to develop applications for compatible implementations of Android. -3.2 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you. +3.2 You may not use this SDK to develop applications for other platforms (including non-compatible implementations of Android) or to develop another SDK. You are of course free to develop applications for other platforms, including non-compatible implementations of Android, provided that this SDK is not used for that purpose. -3.3 You may not use the SDK for any purpose not expressly permitted by this License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK. +3.3 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you. -3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the SDK. +3.4 You may not use the SDK for any purpose not expressly permitted by the License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK; or (b) load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK. -3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement. +3.5 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement. 3.6 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you. -3.7 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. +3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features. 3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.

      4. Use of the SDK by You

      -4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications. +4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications. -4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). +4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) the License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). 4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so. @@ -155,7 +129,7 @@ This is the Android Software Development Kit License Agreement 4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so. -4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach. +4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.

      5. Your Developer Credentials

      @@ -173,7 +147,7 @@ This is the Android Software Development Kit License Agreement 7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners. -7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties. +7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, the License Agreement does not affect your legal relationship with these third parties.

      8. Using Android APIs

      @@ -185,17 +159,17 @@ This is the Android Software Development Kit License Agreement

      9. Terminating this License Agreement

      -9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below. +9.1 The License Agreement will continue to apply until terminated by either you or Google as set out below. -9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials. +9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials. -9.3 Google may at any time, terminate this License Agreement with you if: -(A) you have breached any provision of this License Agreement; or +9.3 Google may at any time, terminate the License Agreement with you if: +(A) you have breached any provision of the License Agreement; or (B) Google is required to do so by law; or (C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or (D) Google decides to no longer provide the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable. -9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely. +9.4 When the License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst the License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely.

      10. DISCLAIMER OF WARRANTIES

      @@ -211,7 +185,7 @@ This is the Android Software Development Kit License Agreement

      12. Indemnification

      -12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement. +12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with the License Agreement.

      13. Changes to the License Agreement

      @@ -219,25 +193,22 @@ This is the Android Software Development Kit License Agreement

      14. General Legal Terms

      -14.1 This License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK. +14.1 The License Agreement constitutes the whole legal agreement between you and Google and governs your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the SDK. -14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. +14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google. -14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable. +14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable. -14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement. +14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement. 14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE. -14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party. - -14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. - - -December 8, 2014 -
      +14.6 The rights granted in the License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under the License Agreement without the prior written approval of the other party. +14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction. +November 20, 2015 +
      @@ -245,243 +216,314 @@ This is the Android Software Development Kit License Agreement

      You're just a few steps away from building apps for Android!

      In a moment, you'll be redirected to Installing the Android SDK.

      -
      - -

      -

      +

      +
      + + +
      + + + + +
      +
      +
      + + + - - - - - - - -
      - -
       
      - - - -
      - -

      Android Studio

      - -

      The official Android IDE

      - -
        -
      • Android Studio IDE
      • -
      • Android SDK tools
      • -
      • Android 5.0 (Lollipop) Platform
      • -
      • Android 5.0 emulator system image with Google APIs
      • -
      - -Download Android Studio
      - - -

      -To get Android Studio or stand-alone SDK tools, visit developer.android.com/sdk/ -

      + + + + +
      +
      + +
      +
      +
      + +
      +
      +
      +

      Instant Run

      +

      Push code and resource +changes to your app running on a device or emulator and see the +changes instantly come to life.

      +

      Instant Run dramatically speeds up your edit, +build, and run cycles, keeping you "in the flow."

      + + + Learn more +
      +
      - - - - - -
      -

      Intelligent code editor

      - -
      - -
      - -
      -

      At the core of Android Studio is an intelligent code editor capable of advanced - code completion, refactoring, and code analysis.

      -

      The powerful code editor helps you be a more productive Android app developer.

      +
      +
      +
      + +
      +
      +
      +

      Intelligent code editor

      +

      Write better code, work faster, and be more productive with an intelligent code editor that helps you each step of the way.

      +

      Android Studio is built on IntelliJ and is capable of advanced code completion, refactoring, and code analysis.

      +
      +
      - - - - -

      Code templates and GitHub integration

      - -
      - -
      - -
      -

      New project wizards make it easier than ever to start a new project.

      - -

      Start projects using template code for patterns such as navigation drawer and view pagers, - and even import Google code samples from GitHub.

      +
      +
      +
      + +
      +
      +
      +

      Fast and feature-rich emulator

      +

      Install and run your apps faster than with a physical device and test your app on virtually any Android device configuration: Android phones, Android tablets, Android Wear, and Android TV devices.

      +

      The new Android Emulator 2.0 is faster than ever and allows you to dynamically resize the emulator and access a suite of sensor controls.

      + + + Learn more +
      +
      - - - -

      Multi-screen app development

      - -
      - -
      - -
      -

      Build apps for Android phones, tablets, Android Wear, - Android TV, Android Auto and Google Glass.

      -

      With the new Android Project View and module support in Android Studio, it's easier - to manage app projects and resources. +

      +
      +
      + +
      +
      +
      +

      Robust and flexible build system

      +

      Easily configure your project to include code libraries and generate multiple build variants from a single project.

      +

      With Gradle, Android Studio offers high-performance build automation, robust dependency management, and customizable build configurations.

      + + + Learn more +
      +
      - - - -

      Virtual devices for all shapes and sizes

      - -
      - -
      - -
      -

      Android Studio comes pre-configured with an optimized emulator image.

      -

      The updated and streamlined Virtual Device Manager provides - pre-defined device profiles for common Android devices.

      +
      + +
      +
      +
      + +
      +
      +
      +

      Develop for all Android devices

      +

      Target multiple form factors with a single +project to easily share code among your different versions of your app.

      +

      Android Studio provides a unified environment +to develop apps for Android phones, tablets, Android Wear, Android TV, and +Android Auto.

      + + + + Learn more +
      +
      - - - -

      -Android builds evolved, with Gradle

      - -
      - -
      - -
      -

      Create multiple APKs for your Android app with different features using the same project.

      -

      Manage app dependencies with Maven.

      -

      Build APKs from Android Studio or the command line.

      +
      +
      +
      + +
      +
      +
      +

      Code templates and GitHub integration

      +

      Start projects with code templates for patterns such as navigation drawer and view pagers, or import Google code samples from GitHub.

      +

      Android Studio's project wizards make it easier than ever to add code in a new project.

      +
      +
      +
      + + -

      More about Android Studio

      -
      - -Download Android Studio - -
        -
      • Built on IntelliJ IDEA Community Edition, the popular Java IDE by JetBrains.
      • -
      • Flexible Gradle-based build system.
      • -
      • Build variants and multiple APK generation.
      • -
      • Expanded template support for Google Services and various device types.
      • -
      • Rich layout editor with support for theme editing.
      • -
      • Lint tools to catch performance, usability, version compatibility, and other problems.
      • -
      • ProGuard and app-signing capabilities.
      • -
      • Built-in support for Google Cloud Platform, making it easy to integrate Google Cloud - Messaging and App Engine.
      • -
      - -

      -For more details about features available in Android Studio, -read the overview at Android Studio.

      +
      -

      If you have been using Eclipse with ADT, be aware that Android Studio is now the official IDE -for Android, so you should migrate to Android Studio to receive all the -latest IDE updates. For help moving projects, -see Migrating to Android -Studio.

      +
      +

      Latest News

      +
      +
      +
      +
      +

      Resources

      +
      +
      +
      +
      +

      Videos

      +
      +
      +
      +
      +
      -

      System Requirements

      +

      System Requirements

      -

      Windows

      +
      +
      +

      Windows

        -
      • Microsoft® Windows® 8/7/Vista/2003 (32 or 64-bit)
      • -
      • 2 GB RAM minimum, 4 GB RAM recommended
      • -
      • 400 MB hard disk space
      • -
      • At least 1 GB for Android SDK, emulator system images, and caches
      • +
      • Microsoft® Windows® 7/8/10 (32 or 64-bit)
      • +
      • 2 GB RAM minimum, 8 GB RAM recommended
      • +
      • 2 GB of available disk space minimum (500 MB for IDE + 1.5 GB for +Android SDK and emulator system image). 4 GB Recommended.
      • 1280 x 800 minimum screen resolution
      • -
      • Java Development Kit (JDK) 7
      • -
      • Optional for accelerated emulator: Intel® processor with support for Intel® VT-x, Intel® EM64T -(Intel® 64), and Execute Disable (XD) Bit functionality
      • +
      • Java Development Kit (JDK) 8
      • +
      • Optional for accelerated emulator: Intel® processor with support for Intel® +VT-x, Intel® EM64T (Intel® 64), and Execute Disable (XD) Bit functionality
      +
      - -

      Mac OS X

      - +
      +

      Mac

        -
      • Mac® OS X® 10.8.5 or higher, up to 10.9 (Mavericks)
      • -
      • 2 GB RAM minimum, 4 GB RAM recommended
      • -
      • 400 MB hard disk space
      • -
      • At least 1 GB for Android SDK, emulator system images, and caches
      • +
      • Mac® OS X® 10.8.5 or higher, up to 10.11.4 (El Capitan)
      • +
      • 2 GB RAM minimum, 8 GB RAM recommended
      • +
      • 2 GB of available disk space minimum (500 MB for IDE + 1.5 GB for +Android SDK and emulator system image). 4 GB Recommended.
      • 1280 x 800 minimum screen resolution
      • -
      • Java Runtime Environment (JRE) 6
      • -
      • Java Development Kit (JDK) 7
      • -
      • Optional for accelerated emulator: Intel® processor with support for Intel® VT-x, Intel® EM64T -(Intel® 64), and Execute Disable (XD) Bit functionality
      • +
      • Java Development Kit (JDK) 8
      +
      -

      On Mac OS, run Android Studio with Java Runtime Environment (JRE) 6 for optimized font -rendering. You can then configure your project to use Java Development Kit (JDK) 6 or JDK 7.

      - - - -

      Linux

      - +
      +

      Linux

        -
      • GNOME or KDE desktop
      • -
      • GNU C Library (glibc) 2.15 or later
      • -
      • 2 GB RAM minimum, 4 GB RAM recommended
      • -
      • 400 MB hard disk space
      • -
      • At least 1 GB for Android SDK, emulator system images, and caches
      • +
      • GNOME or KDE desktop +

        Tested on Ubuntu® 12.04, Precise Pangolin (64-bit distribution +capable of running 32-bit applications)

      • +
      • GNU C Library (glibc) 2.11 or later
      • +
      • 2 GB RAM minimum, 8 GB RAM recommended
      • +
      • 2 GB of available disk space minimum (500 MB for IDE + 1.5 GB for +Android SDK and emulator system image). 4 GB Recommended.
      • 1280 x 800 minimum screen resolution
      • -
      • Oracle® Java Development Kit (JDK) 7
      • +
      • Java Development Kit (JDK) 8
      -

      Tested on Ubuntu® 14.04, Trusty Tahr (64-bit distribution capable of running -32-bit applications).

      - - +
      +
      +
      -

      Other Download Options

      +
      - diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd deleted file mode 100644 index 88619bd4084c6c45d079101d8b3f95566a1f8a1d..0000000000000000000000000000000000000000 --- a/docs/html/sdk/installing/adding-packages.jd +++ /dev/null @@ -1,229 +0,0 @@ -page.title=Adding SDK Packages - -page.tags=sdk manager -helpoutsWidget=true - -@jd:body - - - - -

      -By default, the Android SDK does not include everything you need to start developing. -The SDK separates tools, platforms, and other components into packages you can -download as needed using the -Android SDK Manager. -So before you can start, there are a few packages you should add to your Android SDK.

      - -

      To start adding packages, launch the Android SDK Manager in one of the following ways:

      -
        -
      • In Android Studio, click SDK Manager - in the toolbar.
      • -
      • If you're not using Android Studio: -
          -
        • Windows: Double-click the SDK Manager.exe file at the root of the Android - SDK directory.
        • -
        • Mac/Linux: Open a terminal and navigate to the tools/ directory in the - location where the Android SDK was installed, then execute android sdk.
        • -
        -
      • -
      - -

      When you open the SDK Manager for the first time, several packages are selected by -default. Leave these selected, but be sure you have everything you need -to get started by following these steps:

      - - -
        -
      1. -

        Get the latest SDK tools

        - - - -

        As a minimum when setting up the Android SDK, - you should download the latest tools and Android platform:

        -
          -
        1. Open the Tools directory and select: -
            -
          • Android SDK Tools
          • -
          • Android SDK Platform-tools
          • -
          • Android SDK Build-tools (highest version)
          • -
          -
        2. -
        3. Open the first Android X.X folder (the latest version) and select: -
            -
          • SDK Platform
          • -
          • A system image for the emulator, such as
            - ARM EABI v7a System Image
          • -
          -
        4. -
        -
      2. - -
      3. -

        Get the support library for additional APIs

        - - - -

        The Android Support Library - provides an extended set of APIs that are compatible with most versions of Android.

        - -

        Open the Extras directory and select:

        -
          -
        • Android Support Repository
        • -
        • Android Support Library
        • -
        - -

         

        -

         

        - -
      4. - - -
      5. -

        Get Google Play services for even more APIs

        - - - -

        To develop with Google APIs, you need the Google Play services package:

        -

        Open the Extras directory and select:

        -
          -
        • Google Repository
        • -
        • Google Play services
        • -
        - -

        Note: Google Play services APIs are not available on all - Android-powered devices, but are available on all devices with Google Play Store. To use these - APIs in the Android emulator, you must also install the the Google APIs - system image from the latest Android X.X directory in the SDK Manager.

        -
      6. - - -
      7. -

        Install the packages

        -

        Once you've selected all the desired packages, continue to install:

        -
          -
        1. Click Install X packages.
        2. -
        3. In the next window, double-click each package name on the left - to accept the license agreement for each.
        4. -
        5. Click Install.
        6. -
        -

        The download progress is shown at the bottom of the SDK Manager window. - Do not exit the SDK Manager or it will cancel the download.

        -
      8. - -
      9. -

        Build something!

        - -

        With the above packages now in your Android SDK, you're ready to build apps -for Android. As new tools and other APIs become available, simply launch the SDK Manager - to download the new packages for your SDK.

        - -

        Here are a few options for how you should proceed:

        - -
        -
        -

        Get started

        -

        If you're new to Android development, learn the basics of Android apps by following -the guide to Building Your First App.

        - -
        -
        -

        Build for wearables

        -

        If you're ready to start building apps for Android wearables, see the guide to -Building Apps for Android Wear.

        - -
        -
        -

        Use Google APIs

        -

        To start using Google APIs, such as Maps or -Play Game services, see the guide to -Setting Up Google Play -Services.

        - -
        -
        - - -
      10. - -
      - - diff --git a/docs/html/sdk/installing/create-project.jd b/docs/html/sdk/installing/create-project.jd index 68fd57250d171ee37db38638b1f10a7da7cc3550..a4de85ceb7e2b1f152c010b0c20ab0983fcf6744 100644 --- a/docs/html/sdk/installing/create-project.jd +++ b/docs/html/sdk/installing/create-project.jd @@ -1,4 +1,4 @@ -page.title=Managing Projects from Android Studio +page.title=Managing Projects with Android Studio @jd:body @@ -8,13 +8,6 @@ page.title=Managing Projects from Android Studio
      1. Creating an Android Project
      2. -
          -
        1. Create a New Project
        2. -
        3. Select Form Factors and API Level
        4. -
        5. Add an Activity
        6. -
        7. Configure Your App
        8. -
        9. Develop Your App
        10. -
      3. Creating an Android Module
      4. @@ -190,8 +183,8 @@ with complete source files for each of them as shown in figure 6.

      5. Select a Theme. This setting specifies which standard Android visual style is applied to your application. Select activity template. For more information about Android code templates, see - Using Code TemplatesLeave the - Create activity option checked so you can start your + Using Code Templates. Leave the + Create activity option checked so you can start your application with some essential components.
      6. Click the check box for the required Support Libraries then click Next.
      7. @@ -359,10 +352,6 @@ Android project view:

        per resource type. - - -

        Use the Android Project View

        -

        The Android project view is enabled by default and shows all the build files at the top level of the project hierarchy under Gradle Scripts. The project module appears as a folder at the top level of the project hierarchy and contains these three elements @@ -386,12 +375,3 @@ from this representation and maintains the traditional project structure.

           

        Figure 10: Android and Traditional project view

        - - - - - - - - - diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd index 0375a2f37a9986ad7e106ab5675ac6f4338ce442..f776c9a2e27eccd0591bbfbdba2a8e9952195ea1 100644 --- a/docs/html/sdk/installing/index.jd +++ b/docs/html/sdk/installing/index.jd @@ -1,379 +1,199 @@ -page.title=Installing the Android SDK +page.title=Install Android Studio excludeFromSuggestions=true page.tags=sdk tools -helpoutsWidget=true @jd:body - - - - - -
      +

      That's it! +The following video shows each step of the recommended setup procedure.

      + +

      As new tools and other APIs become available, Android Studio will tell you +with a pop-up, or you can check yourself by clicking Help > +Check for Update.

      - +

      Note: To support 32-bit apps + on a 64-bit machine, you will need to install the ia32-libs, + lib32ncurses5-dev, and lib32stdc++6 packages.

      +
      -
      + Acer + + http://www.acer.com/worldwide/support/mobile.html +
      + alcatel one touch + + http://www.alcatelonetouch.com/global-en/support/ +
      + Asus + + http://support.asus.com/download/ +
      + Blackberry + + + https://swdownloads.blackberry.com/Downloads/entry.do?code=4EE0932F46276313B51570F46266A608 +
      + Dell + + + http://support.dell.com/support/downloads/index.aspx?c=us&cs=19&l=en&s=dhs&~ck=anavml +
      + Fujitsu + + http://www.fmworld.net/product/phone/sp/android/develop/ +
      + Hisense + + + http://app.hismarttv.com/dss/resourcecontent.do?method=viewResourceDetail&resourceId=16&type=5 +
      + HTC + + http://www.htc.com
      + Click on the support tab to select your products/device. Different regions will have + different links. +
      + Huawei + + http://consumer.huawei.com/en/support/index.htm +
      + Intel + + http://www.intel.com/software/android +
      + Kyocera + + http://www.kyocera-wireless.com/support/phone_drivers.htm +
      + Lenovo + + http://support.lenovo.com/us/en/GlobalProductSelector +
      + LGE + + http://www.lg.com/us/support/software-firmware +
      + Motorola + + https://motorola-global-portal.custhelp.com/app/answers/detail/a_id/88481/ +
      + MTK + + http://online.mediatek.com/Public%20Documents/MTK_Android_USB_Driver.zip + (ZIP download) +
      + Oppo + + http://www.oppo.com/index.php?q=software/view&sw_id=631 +
      + Pegatron + + http://www.pegatroncorp.com/download/New_Duke_PC_Driver_0705.zip + (ZIP download) +
      + Samsung + + http://www.samsung.com/us/support/downloads +
      + Sharp + + http://k-tai.sharp.co.jp/support/ +
      + Sony Mobile Communications + + http://developer.sonymobile.com/downloads/drivers/ +
      + Toshiba + + http://support.toshiba.com/sscontent?docId=4001814 +
      + Xiaomi + + http://www.xiaomi.com/c/driver/index.html +
      + ZTE + + http://support.zte.com.cn/support/news/NewsDetail.aspx?newsId=1000442 +
      \ No newline at end of file diff --git a/docs/html/tools/help/adb.jd b/docs/html/tools/help/adb.jd old mode 100644 new mode 100755 index bcd949adc3707e52a9e392e02e8be306a7bd1561..0340efeec2ea09e547222d593b496cb05b79d7ed --- a/docs/html/tools/help/adb.jd +++ b/docs/html/tools/help/adb.jd @@ -29,7 +29,7 @@ three components:

      • A client, which runs on your development machine. You can invoke a client from a shell -by issuing an adb command. Other Android tools such as the ADT plugin and DDMS also create +by issuing an adb command. Other Android tools such as DDMS also create adb clients.
      • A server, which runs as a background process on your development machine. The server manages communication between the client and the adb daemon running on an emulator or device.
      • @@ -361,7 +361,7 @@ the hardware device.

        For more information about how to create an .apk file that you can install on an emulator/device instance, see Building and Running

        -

        Note that, if you are using the Eclipse IDE and have the ADT plugin installed, you do not need to use adb (or aapt) directly to install your application on the emulator/device. Instead, the ADT plugin handles the packaging and installation of the application for you.

        +

        Note that, if you are using Android Studio, you do not need to use adb (or aapt) directly to install your application on the emulator/device. Instead, Android Studio handles the packaging and installation of the application for you.

        diff --git a/docs/html/tools/help/adt.jd b/docs/html/tools/help/adt.jd index 0fac62d332e7d8ffbff0581b3f929912ab37bbb0..7d29d02ce5b431d3837f2f48ccc4d8e9c1b8ec41 100644 --- a/docs/html/tools/help/adt.jd +++ b/docs/html/tools/help/adt.jd @@ -2,540 +2,23 @@ page.title=Android Developer Tools page.tags=adt @jd:body - -

        - Important: Support for the Android Developer Tools (ADT) in Eclipse is ending, + Important: Support for the Android Developer Tools (ADT) in Eclipse has ended, per our announcement. You should migrate your app development projects to Android Studio as soon as possible. For more information on transitioning to Android Studio, see - Migrating to Android Studio. -

        - -

        Android Developer Tools (ADT) is a plugin for Eclipse that provides a suite of - tools that are integrated with the Eclipse IDE. It offers you access to many features that help - you develop Android applications. ADT - provides GUI access to many of the command line SDK tools as well as a UI design tool for rapid - prototyping, designing, and building of your application's user interface.

        - -

        If you still wish to use the ADT plugin for Eclipse, see -Installing Eclipse Plugin. + Migrating from Eclipse ADT.

        +

        Formerly the official IDE solution for Android development, Android Developer Tools (ADT) + is a plugin for Eclipse that provides GUI-based access to many of the command-line SDK tools, + along with a UI design tool for rapid prototyping, designing, and building of your app's + user interface.

        -

        SDK Tools Integration

        - - - -

        Many of the tools that you can start or run from the command line are integrated into ADT. - They include:

        - -
          -
        • Traceview: - Allows you to profile your program's execution - (Window > Open Perspective > Traceview).
        • - -
        • android: Provides access to - the Android SDK Manager and AVD Manager. Other android features such as creating or - updating projects (application and library) are integrated throughout the Eclipse IDE.
        • - -
        • Hierarchy - Viewer: Allows you to visualize your application's view hierarchy to find inefficiencies - (Window > Open Perspective > Hierarchy Viewer).
        • - -
        • Pixel - Perfect: Allows you to closely examine your UI to help with designing and building. - (Window > Open Perspective > Pixel Perfect).
        • - -
        • DDMS: Provides - debugging features including: screen capturing, thread and heap information, and logcat - (Window > Open Perspective > DDMS).
        • - -
        • adb: Provides access to - a device from your development system. Some features of - adb are integrated into ADT such as project installation (Eclipse run menu), - file transfer, device enumeration, and logcat (DDMS). You must access the more advanced - features of adb, such as shell commands, from the command line.
        • - -
        • ProGuard: Allows code obfuscation, - shrinking, and optimization. ADT integrates ProGuard as part of the build, if you enable it.
        • -
        - -

        Code Editors

        - -

        In addition to Eclipse's standard editor features, ADT provides custom XML editors to help - you create and edit Android manifests, resources, menus, and layouts in a form-based or graphical - mode. Double-clicking on an XML file in Eclipse's package explorer opens the - appropriate XML editor. - -

        - -

        Note: You can edit Android-specific XML files (such as a layout -or manifest) in both a graphical mode and also an XML markup mode. You can switch between -these modes with the pair of tabs at the bottom of each custom XML editor.

        - -

        In addition, some special file types that don't have custom editors, such as drawables, animations, - and color files offer editing enhancements such as XML tag completion.

        - -

        ADT provides the following custom, form-based XML editors:

        - -
        - -
        Graphical Layout Editor
        - -
        Edit and design your XML layout files with a drag and drop interface. The layout editor - renders your interface as well, offering you a preview as you design your layouts. This editor - is invoked when you open an XML file with a view declared (usually declared in - res/layout. For more information, see Graphical Layout - Editor.
        - -
        Android Manifest Editor
        - -
        Edit Android manifests with a simple graphical interface. This editor is invoked - when you open an AndroidManifest.xml file.
        - -
        Menu Editor
        - -
        Edit menu groups and items with a simple graphical interface. This editor is - invoked when you open an XML file with a <menu> declared (usually located in - the res/menu folder).
        - -
        Resources Editor
        - -
        Edit resources with a simple graphical interface. This editor is invoked when - you open an XML file with a <resources> tag declared.
        - -
        XML Resources Editor
        - -
        Edit XML resources with a simple graphical interface. This editor is invoked - when you open an XML file.
        -
        - - -

        Resource linking enhancements

        -

        In addition to the normal code editing features of Eclipse, ADT provides enhancements to the Android - development experience that allow you to quickly jump to declarations of various types of resources such - as strings or layout files. You can access these enhancements by holding down the control key and - clicking on the following items: - -

          - -
        • A resource identifier, such as R.id.button1, jumps - to the XML definition of the view.
        • - -
        • A declaration in the R.java file, such as public - static final int Button01=0x7f050000", jumps to the corresponding XML definition.
        • - -
        • An activity or service definition in your manifest, such as - <activity android:name=".TestActivity">, jumps to the corresponding Java class. You can - jump from an activity definition (or service definition) into the corresponding Java class.
        • - -
        • You can jump to any value definition (e.g. @string:foo), regardless of -which XML file - "foo" is defined in.
        • - -
        • Any file-based declaration, such as @layout/bar, opens the file.
        • - -
        • Non-XML resources, such as @drawable/icon, launches - Eclipse's default application for the given file type, which in this case is an - image.
        • - -
        • @android namespace resources opens the resources found in - the SDK install area.
        • - -
        • Custom views in XML layouts, such as <foo.bar.MyView></foo.bar.MyView>, - or <view class="foo.bar.MyView">) jump to the corresponding custom view classes.
        • - -
        • An XML attribute such as @android:string/ok or android.R.string.id in Java code - opens the file that declares the strings. The XML tab opens when doing this, not - the form-based editor.
        • - -
        - -

        Graphical Layout Editor

        - -

        ADT provides many features to allow you to design and build your application's user interface. - Many of these features are in the graphical layout editor, which you can access by opening one of - your application's XML layout files in Eclipse. -

        - -

        The graphical layout editor is the main screen that you use to visually design and build your - UI. It is split up into the following parts:

        - -
        -
        Canvas
        - -
        In the middle of the editor is the canvas. It provides the rendered view of your - layout and supports dragging and dropping of UI widgets - directly from the palette. You can select the platform version used to render the items in - the canvas. Each platform version has its own look and feel, which might be the similar to or - radically different from another platform version. The canvas renders the appropriate look - and feel for the currently selected platform version. - This platform version does not need to be the same as the version that your - application targets. - -

        The canvas also provides - context-sensitive actions in the layout actions bar, such as adjusting layout margins and -orientation. - The layout actions bar displays available actions depending on the selected UI element in the - canvas.

        -
        - -
        Outline
        - -
        On the right side of the editor is the outline view. It displays a hierarchical - view of your layout where you can do things such as reorder of views. The outline - view exposes similar functionality as the canvas but displays your layout in an ordered - list instead of a rendered preview.
        - -
        Palette
        - -
        On the left side of the editor is the palette. It provides a set of widgets that - you can drag onto the canvas. The palette shows rendered previews of the - widgets for easy lookup of desired UI widgets.
        - -
        Configuration Chooser
        - -
        At the top of the editor is the configuration chooser. - It provides options to change a layout's rendering mode or screen type.
        -
        - - graphical layout editor screenshot - -

        Figure 1. Graphical layout editor

        - -

        Canvas and outline view

        - - - -

        The canvas is the area where you can drag and drop UI widgets from the palette to design your - layout. The canvas offers a rendered preview of your layout depending on factors such as the - selected platform version, screen orientation, and currently selected theme that you specify in - the configuration chooser. You can also drag and drop - items into the outline view, which displays your layout in a hierarchical list. The outline view - exposes much of the same functionality as the canvas but offers another method of organization - that is beneficial for ordering and quickly selecting items. When you right-click a specific item - in the canvas or outline view, you can access a context-sensitive menu that lets you modify the - following attributes of the layout or view:

        - -
        -
        View and layout properties
        - -
        - When you right-click a view or layout in the canvas or outline view, it brings up a - context-sensitive menu that lets you set things such as: - -
          -
        • ID of the view or layout
        • - -
        • Text of the view
        • - -
        • Layout width
        • - -
        • Layout height
        • - -
        • Properties such as alpha or clickable
        • -
        -
        - -
        Animation preview and creation
        - -
        - If your layout or view is animated, you can preview the animation directly in the canvas - (when you select Android 3.0 or later as the platform version in the configuration chooser). - Right-click an item in the canvas and select Play Animation. If - animation is not associated with item, an option is available in the menu to create one. - -

        View the segment on the animation features for more - information.

        -
        - -
        Extract as Include
        - -
        You can extract parts of a current layout into its own layout file, - which you can then include in any layout with a single line of XML. See Layout Refactoring Support for more information.
        -
        - -

        Other canvas features

        - -

        The canvas has additional features not available in the outline view:

        - -
          - -
        • Edit views with the layout actions bar: The context-sensitive layout actions bar allows you to - edit how a view is laid out in your UI. The available actions depend on the currently - selected view and its parent layout. Some common actions include - toggling the fill mode of the view and specifying margins. For instance, if you select a - {@link android.widget.Button} - in a {@link android.widget.LinearLayout}, you see actions related to the {@link -android.widget.LinearLayout}, such as a toggle to switch - between horizontal and vertical layout, and a toggle to control whether its children are - aligned along their text baseline. You will also see toolbar actions to control the individual - layout attributes of the child, such as whether the child should stretch out to match its - parent's width and height, a dropdown action to set the child's layout gravity, a button to open - a margin editor, and a layout weight editor.
        • - -
        • Edit a nested layout in its current context: If you are editing a layout - that includes another layout, you can edit the included layout in the layout that included - it.
        • - -
        • Preview drag and drop location: When you drag and drop a UI widget onto the canvas, ruler - markers appear showing you the approximate location of the UI widget depending on the - type of layout, such as {@link android.widget.RelativeLayout} or {@link - android.widget.LinearLayout}.
        • - -
        • Preview animations: You can preview view and layout animations when you select Android 2.1 - or later for the platform version in the configuration bar.
        • - -
        • Render layouts in real-time: Layouts are rendered as accurately as possible according to - the platform version, including the appropriate system and action bars.
        • - -
        • Support for fragments: Fragments can be rendered in the same screen as the layout that - includes the fragments.
        • - -
        - - screenshot of the canvas - -

        Figure 2. Canvas portion of the layout editor showing - a rendered preview of an application

        - - screenshot of the outline view - -

        Figure 3. Outline view showing current layout's structure

        - -

        Palette

        - - - -

        The palette contains the UI widgets that you can drag and drop onto the canvas and add to your - layout. The pallete categorizes the widgets and shows rendered previews - for easier lookup. The main features of the palette include:

        - -
          -
        • Different modes of rendered previews include: icons only, icons and text, tiny previews, - small previews, and previews (rendered in real size). Previews are only available for layouts - rendered with the latest revisions of Android 2.1 (API Level 7) or later.
        • - -
        • Custom views in your project or library projects are added under custom views - category.
        • - -
        • Arrange UI widgets alphabetically or by category.
        • -
        - palette screenshot - -

        Figure 4. Palette showing available UI widgets

        - -

        Configuration chooser

        - - - - -

        The configuration chooser allows you to create and configure different configurations of - a layout for different situations, such as one for landscape and one for portrait mode. You can - set the following options for each configuration of a layout: +

        As with ADT, support for the Ant + tool for building from the command line has ended. + Gradle is now the supported method + of building Android apps.

        -
          -
        • Screen type combo box: Predefined screen settings for common device configurations. You - can also create your own by selecting Custom....
        • - -
        • Screen orientation combo box: Portrait or Landscape screen orientation.
        • - -
        • Theme combo box: Predefined themes or a custom theme that you have created.
        • - -
        • Platform combo box: Platform version used to render the canvas and palette as well as - displaying appropriate themes.
        • - -
        • Custom layout combo boxes: The locale, dock, and time of day combo boxes let you select - different versions of the same layout depending on the device's current state. You can - create a new version of a layout with the Create button.
        • -
        - - - - -

        Figure 5. Configuration chooser

        - -

        Layout Refactoring Support

        - - - -

        In both the graphical and XML layout editor, there are many features that help you quickly - refactor your layouts. The following list describes the major refactoring support:

        - -
        - -
        Change layout
        -
        This lets you change the layout on the fly and re-renders the canvas for you. - You can apply this refactoring to any layout and the layout is converted to the new type if - possible. In many cases, the opening and closing tags of the layout's XML element are changed - along with things such as ID attributes and their references. However, for some supported - types, ADT attempts to preserve the layout, such as changing a {@link - android.widget.LinearLayout} to a {@link android.widget.RelativeLayout}.
        - -
        Change widget
        -
        This lets you select one or more widgets and converts them to a new widget type. In - addition to changing the element name, it also removes any - attributes that are not supported by the new widget type and adds in any mandatory attributes - required by the new widget type. If the current ID of a widget includes the - current widget type in its ID (such as a <Button> widget named - "button1"), then the ID is changed to match the new widget type and all - references are updated.
        - -
        Extract as include
        -
        This lets you extract views inside of an existing layout into their own separate layout - file. An include tag that points to the newly created layout file is inserted - into the existing layout file. Right-click the view or layout and select Extract as - Include....
        - -
        Extract string
        -
        Extract strings from either XML or Java files into their own separate resource file.
        - -
        Extract style
        -
        Extract style-related attributes from a layout and define them in a new - styles.xml file. You can select multiple views and this refactoring extracts all - of the same styles into one style and assigns that style to all the views that use it.
        - -
        Wrap-in container
        -
        This lets you select one or more sibling elements and wrap them in a new container. This - can be applied to the root element as well, in which case the namespace declaration attributes - will be transferred to the new root. This refactoring also transfers layout_ - attribute references to the new root, For example, suppose you have a {@link android.widget.RelativeLayout}. - If other widgets have layout constraints pointing to your widget, wrapping the widget causes - these constraints to point to the parent instead.
        - -
        Quick Assistant
        -
        Provides refactoring suggestions depending on the current context. Press - Ctrl-1 (or Cmd-1 on - Mac) in an editor, and Eclipse provides a list of possible refactorings depending on the - context. The Quick Assistant provides fast access to all of the above refactorings, where applicable. - For example, if you are editing an XML value and decide you want to extract it out - as a string, place the text cursor in the string and press Ctrl-1 to see the refactoring context - menu.
        -
        - - - - - -

        Updating the ADT Plugin

        - -

        From time to time, a new revision of the ADT Plugin becomes available, with -new features and bug fixes. Generally, when a new revision of ADT is available, -you should update to it as soon as convenient.

        - -

        In some cases, a new revision of ADT will have a dependency on a specific -revision of the Android SDK Tools. If such dependencies exist, you will need to -update the SDK Tools package of the SDK after installing the new revision of -ADT. To update the SDK Tools package, use the Android SDK Manager, as -described in Adding SDK Packages.

        - -

        To learn about new features of each ADT revision and also any dependencies on -the SDK Tools, see the listings in the Revisions -section. To determine the version currently installed, open the -Eclipse Installed Software window using Help -> Software Updates and refer to the version listed for -"Android Development Tools".

        - -

        Follow the steps below to check whether an update is available and, if so, -to install it.

        - -
          -
        1. Select Help > Check for Updates. -

          If there are no updates available, a dialog will say so and you're done.

        2. -
        3. If there are updates available, select Android DDMS, Android Development Tools, - and Android Hierarchy Viewer, then click Next.
        4. -
        5. In the Update Details dialog, click Next.
        6. -
        7. Read and accept the license agreement and then click Finish. - This will download and install the latest version of Android DDMS and - Android Development Tools.
        8. -
        9. Restart Eclipse.
        10. -
        - - -

        If you encounter problems during the update, remove the existing ADT plugin from Eclipse, then -perform a fresh installation, using the instructions for Installing the ADT -Plugin.

        diff --git a/docs/html/tools/help/am-cpu.jd b/docs/html/tools/help/am-cpu.jd new file mode 100644 index 0000000000000000000000000000000000000000..62b35909a3151db6631621448a23a92a6cbf627a --- /dev/null +++ b/docs/html/tools/help/am-cpu.jd @@ -0,0 +1,325 @@ +page.title=CPU Monitor +parent.title=Android Monitor +parent.link=android-monitor.html +page.tags=monitor +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Displaying a Running App in the CPU Monitor
        2. +
        3. Performing a Method Trace in the CPU Monitor
        4. +
        5. Viewing a Saved Method Trace
        6. +
        7. Sorting Method Trace Data
        8. +
        9. Renaming a Method Trace File
        10. +
        11. Locating a Method Trace File on Disk
        12. +
        13. Deleting a Method Trace File
        14. +
        + +

        See also

        +
          +
        1. + Android Monitor +
        2. +
        3. logcat Monitor +
        4. + +
        5. Memory Monitor +
        6. + +
        7. GPU Monitor +
        8. + +
        9. + Network Monitor +
        10. +
        + + +

        + Dependencies and Prerequisites +

        + +
          +
        • Make sure your development computer detects your hardware device, which often happens + automatically when you connect it to a USB port. +
        • + +
        • + Enable USB debugging in + Developer Options on the device or emulator. +
        • + +
        • In your app, set the debuggable property to true in the manifest or + build.gradle file (it’s initially set by default). +
        • + +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • + +
        • + Android Device Monitor can’t be running. +
        • + +
        + +
        +
        + + +

        + The CPU Monitor lets you easily monitor the central processing unit (CPU) usage of your app. It + displays CPU usage in real time and displays the percentage of total CPU time (including all cores) + used by user and kernel mode. In user mode, the code must use system APIs to access hardware or + memory, and crashes are usually recoverable. In kernel mode, the code can directly access + hardware, including memory addresses; crashes halt the device. +

        + +

        + Displaying a Running App in the CPU Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Optionally connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Click the CPU tab. +
        6. + +
        7. Open an app project and run it on a hardware device or + emulator. +
        8. + +
        9. Enable the CPU Monitor by clicking Pause Pause icon to deselect + it. +
        10. + +

          + The CPU Monitor starts to display any CPU usage. + In the graph, the y-axis displays the percentage of CPU used. The x-axis records the time elapsed + and starts with seconds, and then minutes and seconds, and so on. +

          + +
        11. To stop the CPU Monitor, click Pause Pause icon again to select + it. +
        12. +
        + + +

        + Performing a Method Trace in the CPU Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. + Display a running app in the CPU + Monitor. +
        2. + +
        3. Start a trace by clicking Start Method Tracing Start Method Tracing icon to + select it. +
        4. + +
        5. To stop the trace, click Stop Method Tracing Stop Method Tracing icon to + deselect it. +
        6. + +

          + The method trace appears in the Code Editor area: +

          +Method Trace +

          + Android Studio creates the method trace file + with the filename Trace_yyyy.mm.dd_hh.mm.ss.trace + using the year, month, day, hour, minute, and second of the capture, for example, + Trace_2015.11.17_14.58.48.trace. +

          +
        7. Specify display options: +
            +
          • Select a Thread. +
          • + +
          • Select an x-axis time for the graphic and the method list: +
          • + +
              +
            • + Wall Clock Time - Total CPU time elapsed between the method call and + return. +
            • + +
            • + Thread Time - Total time during which the JRE scheduled the + thread during call processing. It’s less than or equal to the Wall Clock Time: less if + the JRE interrupted the thread, and equal if it didn’t. + The thread might not run continuously; when it’s not executing, that time is excluded. + If threads are interrupted often and it’s not by design, the interruptions affect app + performance. However, an example of a by-design use is synchronous operations that take + a long time, such as file transfers and reads from disk, where the method could be the + asynchronous wrapper for the synchronous reader. +
            • +
            + +
          • + Optionally select Color by inclusive time. +
          • +
          + +

          + The display shows the following information: +

          + + + + + + + + + + + + + + + + + + + + + + + + + +
          FieldDescription
          NameThe name of the method.
          Invocation CountHow many times the method was called.
          Inclusive Time (microseconds)Time spent in the method and all of its children, either wall clock or thread time, + depending on your selection in the x-axis menu.
          Exclusive Time (microseconds)Time spent just in the method (excluding time spent in its children), either wall clock + or thread time, depending on your selection in the x-axis menu.
          +

          Note: Running the method trace significantly affects CPU timings. + Use the method trace to understand the flow of the program, but not for performance timings.

          +

          + The graphic represents the wall clock or thread time for each method. Hover the cursor + over the display to receive information about the method. This information also appears + in the table. +

          +
        +

        + Viewing a Saved Method Trace +

        + +

        + After you do a method trace, Android Studio automatically stores it so you can view it + again. To examine the trace, follow these steps: +

        + +
          +
        1. Click Captures in the main window. +
        2. + +

          + The Captures window appears. +

          + +
        3. Open the Methods Tracing folder. +
        4. + +
        5. Double-click the file to view it. +
        6. +
        + +

        + Sorting Method Trace Data +

        + +

        + You can sort the data by method name, count, inclusive time, and exclusive time. Follow this step: +

        + + +
          +
        • Click a column heading to sort the table by ascending or descending order. +
        • +
        + +

        + Renaming a Method Trace File +

        + +

        + Rename a method trace file from within Android Studio so it + continues to appear in the Captures window. Follow these steps: +

        + +
          +
        1. In the Captures window, right-click the file and select Rename. +
        2. + +
        3. In the dialog, specify the name of the file and click OK. +
        4. +
        + +

        + Locating a Method Trace File on Disk +

        + +

        + You can quickly discover where Android Studio stored method trace files on disk. Follow this step: +

        + + + +
          +
        • In the Captures window, right-click a method trace file and select Show in + files. +
        • + +

          + Android Studio opens an operating system file browser displaying the location where the file + resides. +

          +
        +

        + Note: If you move a method trace file, Android Studio no longer displays the file + in the Captures window. To display it, use File > + Open. Also, rename a file from the Captures + window and not in the operating system file browser. +

        + +

        + Deleting a Method Trace File +

        + +

        + Follow this step in Android Studio: +

        + +
          +
        • In the Captures window, right-click a method trace file and select + Delete. +
        • +
        + +

        + Android Studio deletes the file from the Captures dialog and from disk. +

        \ No newline at end of file diff --git a/docs/html/tools/help/am-gpu.jd b/docs/html/tools/help/am-gpu.jd new file mode 100644 index 0000000000000000000000000000000000000000..a244b224d49d9aaee3fc54522ce826807d12f430 --- /dev/null +++ b/docs/html/tools/help/am-gpu.jd @@ -0,0 +1,136 @@ +page.title=GPU Monitor +parent.title=Android Monitor +parent.link=android-monitor.html +page.tags=monitor +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Displaying a Running App in the GPU Monitor
        2. +
        + +

        See also

        +
          +
        1. + Android Monitor +
        2. +
        3. logcat Monitor +
        4. + +
        5. Memory Monitor +
        6. + +
        7. CPU Monitor +
        8. + +
        9. + Network Monitor +
        10. +
        + +

        + Dependencies and Prerequisites +

        + +
          +
        • Make sure your development computer detects your hardware device, which often happens + automatically when you connect it to a USB port. +
        • + +
        • + Enable USB debugging in + Developer Options on the device or emulator. +
        • + +
        • For Android 5.0 (API level 21) and Android 5.1 (API level 22), in Developer + Options on the device or emulator, set Profile GPU rendering to + In adb shell dumpsys gfxinfo. +
        • + +
        • In your app, set the debuggable property to true in the manifest or + build.gradle file (it’s initially set by default). +
        • + + +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • + +
        • + Android Device Monitor can’t be running. +
        • +
        + +
        +
        + +

        + The GPU Monitor gives you a quick visual representation of how much time it takes to render the + frames of a UI window. It profiles the amount of time it takes for the render thread to prepare, + process, and execute the draw commands. The GPU Monitor can help you to: +

        + +
          +
        • Quickly see how a UI window performs. +
        • + +
        • Identify whether any part of the rendering pipeline stands out in using processing time. +
        • + +
        • Look for spikes in frame rendering time associated with user or program actions. +
        • +
        + +

        + For example, if displaying a static photo continues to take Graphics Processor Unit + (GPU) resources long after it has finished + drawing on the screen, that’s a likely candidate for optimization. +

        + + +

        + Displaying a Running App in the GPU Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Optionally connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Click the GPU tab. +
        6. + +
        7. Open an app project and run it on a hardware device or + emulator. +
        8. + +
        9. Enable the GPU Monitor by clicking Pause Pause icon to deselect + it. +
        10. + +

          + Any GPU usage begins to appear in the GPU Monitor: +

          + + +

          + The y-axis is the amount of time it takes the GPU to execute, process, prepare, and draw frames, + in milliseconds. The x-axis records the time elapsed; it starts with seconds, and then minutes + and seconds, and so on. +

          + +
        11. To stop the GPU Monitor, click Pause Pause icon again to select + it. +
        12. +
        diff --git a/docs/html/tools/help/am-logcat.jd b/docs/html/tools/help/am-logcat.jd new file mode 100644 index 0000000000000000000000000000000000000000..57da848ae1d7a45c260f62f730d2e6fe9da188d6 --- /dev/null +++ b/docs/html/tools/help/am-logcat.jd @@ -0,0 +1,521 @@ +page.title=logcat Monitor +parent.title=Android Monitor +parent.link=android-monitor.html +page.tags=monitor +@jd:body +
        +
        +

        + In this document +

        + +
          +
        1. + logcat Message Format +
        2. + +
        3. + Displaying a Running App in the logcat Monitor +
        4. + +
        5. + Setting the Log Level +
        6. + +
        7. + Searching logcat Messages +
        8. + +
        9. + Filtering logcat Messages +
        10. + +
        11. + Configuring the logcat Header Display +
        12. + +
        13. + Moving Up and Down the Stack Trace +
        14. + +
        15. + Moving to the End of the Log +
        16. + +
        17. + Printing the Log +
        18. + +
        19. + Clearing the Log +
        20. + +
        21. + Restarting the Log +
        22. +
        + +

        + See also +

        + +
          +
        1. + Reading and Writing Logs +
        2. +
        3. + Android Monitor +
        4. + +
        5. Memory Monitor +
        6. + +
        7. CPU Monitor +
        8. + +
        9. GPU Monitor +
        10. + +
        11. + Network Monitor +
        12. +
        + +

        + Dependencies and Prerequisites +

        + +
          +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • +
        • + Android Device Monitor can’t be running. +
        • +
        +
        +
        + +

        + The Android logging system provides a mechanism for collecting and viewing system debug output. + logcat Monitor displays messages that you added to your app by using the Log class, as well as system + messages, such as stack traces when the emulator throws an error or a garbage collection occurs. + The monitor displays messages in real time and also keeps a history so you can view older + messages. +

        + +

        + To display just the information of interest, you can create filters, modify how much information + is displayed in messages, set priority levels, display messages produced by app code + only, and search the log. By default, logcat Monitor shows the log output related to the running + application only. +

        + +

        + You can traverse the stack trace when your app throws an exception, as well as view the + associated code. This feature can help you fix exceptions and improve app operation. +

        + +

        + logcat Message Format +

        + +

        + Every Android log message has a tag and a priority associated with it. The tag of a system + log message + is a short string indicating the system component from which the message originates (for example, + ActivityManager). A user-defined tag can be any string that you find helpful, such + as the name of the current class (the recommended tag). You define it in a Log + method call, for example: +

        + +
        +Log.d(tag, message);
        +
        +

        + The priority is one of the following values: +

        + +
          +
        • + V — Verbose (lowest priority) +
        • + +
        • + D — Debug +
        • + +
        • + I — Info +
        • + +
        • + W — Warning +
        • + +
        • + E — Error +
        • + +
        • + A — Assert +
        • +
        + +

        + The log message format is: +

        + +
        +date time PID-TID/package priority/tag: message
        +
        + +

        + For example, the following log message has a priority of V and a tag of + AuthZen: +

        + +
        +12-10 13:02:50.071 1901-4229/com.google.android.gms V/AuthZen: Handling delegate intent.
        +
        +

        + PID stands for process identifier and TID is thread identifier; they can be the same if there’s + only one thread. +

        + +

        + Displaying a Running App in the logcat Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Optionally connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Click the logcat tab. +
        6. + +
        7. Open an app project and run it on a hardware device or + emulator. +
        8. +

          + By default, the logcat Monitor displays messages for the app running on the device or emulator: +

          + +

          + To change this default, see Filtering logcat Messages. +

          +
        + +

        + Setting the Log Level +

        + +

        + You can control how many messages appear in logcat Monitor by setting the log level. You can + display all messages, or just the messages indicating the most severe conditions. +

        + +

        + Remember that logcat Monitor continues to collect all messages regardless of the log level setting. + The setting just determines what logcat Monitor displays. +

        + +

        + Follow this step: +

        + +
          +
        • In the Log level menu, select one of the following values: +
        • + +
            +
          • + Verbose - Show all log messages (the default). +
          • + +
          • + Debug - Show debug log messages that are useful during development only, + as well as the message levels lower in this list. +
          • + +
          • + Info - Show expected log messages for regular usage, as well as the + message levels lower in this list. +
          • + +
          • + Warn - Show possible issues that are not yet errors, as well as the + message levels lower in this list. +
          • + +
          • + Error - Show issues that have caused errors, as well as the message levels + lower in this list. +
          • + +
          • + Assert - Show issues that the developer expects should never happen. +
          • +
          + +
        + +

        + Searching logcat Messages +

        + +

        + You can search the messages currently displayed in logcat Monitor. Follow these steps: +

        + +
          +
        1. Optionally select Regex if you want to use a regular expression + search pattern. +
        2. + +
        3. Type a character sequence in the search field Search icon. +
        4. + +

          + The logcat Monitor display changes accordingly. +

          + + +
        5. Press Enter to store the sequence in the menu during this session. +
        6. + +
        7. To repeat a search, choose it from the search menu. Select or deselect + Regex as needed (the setting isn’t remembered). +
        8. +
        + +

        + Filtering logcat Messages +

        + +

        + One way to reduce the log output to a manageable level is to restrict it by using a filter. +

        + +

        + Note: The filter applies to your full logcat history, not just those messages + currently displayed in logcat Monitor. Make sure your other display options are set + appropriately so you can see the filter output you want to examine. +

        + +

        + To define and apply a filter, follow these steps: +

        + +
          +
        1. In the filter menu, select a filter option: +
            +
          • + Show only selected application - Display the messages produced by the + app code only. +
          • + +
          • + No Filters - Apply no filters (the default). +
          • + +
          • + Edit Filter Configuration - Create or modify a custom filter. +
          • +
          + +

          + After you define filters, you can also select them in the menu. To remove them from the + menu, delete them. +

          + +
        2. If you selected Edit Filter Configuration, create or modify a + filter. +
            +
          1. Specify the filter parameters in the Create New Logcat Filter dialog: +
          2. + + +
              +
            • + Filter Name - Type the name of a filter you want to define, or + select it in the left pane to modify an existing filter. The name can contain + lowercase characters, underscores, and digits only. +
            • + +
            • + Log Tag - Optionally specify a tag. For more information, see + logcat Message Format. +
            • + +
            • + Log Message - Optionally specify log message text. For more + information, see logcat Message Format. +
            • + +
            • + Package Name - Optionally specify a package name. For more + information, see logcat Message Format. +
            • + +
            • + PID - Optionally specify a process ID. For more information, see + logcat Message Format. +
            • + +
            • + Log Level - Optionally select a log level. For more information, + see Setting the Log Level. +
            • + +
            • + Regex - Select this option to use regular expression syntax for + that parameter. +
            • +
            + +
          3. + Click + to add it to the left pane. +
          4. + +

            + To remove a filter, select it in the left pane and click -. +

            + +
          5. + When you’re finished, click OK. If you click + Cancel, any filter additions or modifications are lost. +
          6. +
          +
        + +

        + Configuring the logcat Header Display +

        + +

        + You can customize the header display to show just the information you’re interested + in: +

        + +
          +
        • Select Use Soft Wraps Use Soft Wraps icon + to see the entire + message and prevent it from running off of the right edge. +
        • + +
        • Click Configure Logcat Header Configure Logcat header icon + to specify + elements of the messages that you want to show or hide, and then click + OK. +
        • +
        + +

        + For more information about message elements, see logcat Message Format. +

        + +

        + Moving Up and Down the Stack Trace +

        + +

        + When the app throws an exception, the message includes a stack trace of method calls. + logcat + Monitor lets you quickly locate stack traces in the log and view the associated code + in the Code Editor. If needed (and possible), the decompiler derives source code that + you can view. +

        + +
          +
        • Click Up the Stack Trace Up the Stack Trace icon + to move to the + previous method in relation to the current position in the log. +
        • + +
        • Click Down the Stack Trace Down the Stack Trace icon + to move to + the next method in relation to the current position in the log. +
        • +
        + +

        + Moving to the End of the Log +

        + +

        + Clicking a particular message stops the display of messages. You can quickly move to + the end of the log to see the real-time message flow. +

        + +
          +
        • Click Scroll to the End Scroll to the End icon. +
        • + +
        • Press the End key. +
        • + +
        • Scroll or press the Page Down key until you reach the end. +
        • +
        + +

        + Printing the Log +

        +

        + Follow these steps: +

        +
          +
        1. Click Print Print icon. +
        2. +
        3. + In the Print dialog, optionally change print parameters, and then click + Print. +
        + +

        + Clearing the Log +

        +

        + To clear (flush) the entire log, follow this step: +

        +
          +
        • Click Clear logcat Clear logcat icon. +
        • +
        + + + +

        + Restarting the Log +

        + +

        + If there is a problem and the log is no longer progressing, you can restart the log. Follow this + step: +

        + +
          +
        • Click Restart Restart icon. +
        • +
        \ No newline at end of file diff --git a/docs/html/tools/help/am-memory.jd b/docs/html/tools/help/am-memory.jd new file mode 100644 index 0000000000000000000000000000000000000000..24006cdbbb7010ec3f218d76354f34c19a3001c8 --- /dev/null +++ b/docs/html/tools/help/am-memory.jd @@ -0,0 +1,916 @@ +page.title=Memory Monitor +parent.title=Android Monitor +parent.link=android-monitor.html +page.tags=monitor +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Memory Monitor Workflow +
            +
          1. Garbage collection roots and dominator trees
          2. +
          3. Memory leak and use analysis
          4. +
          5. Memory management for different virtual machines
          6. +
          +
        2. +
        3. Displaying a Running App in the Memory Monitor
        4. +
        5. Forcing a Garbage Collection Event
        6. +
        7. Dumping and Analyzing the Java Heap +
            +
          1. Taking and displaying a snapshot of the Java heap
          2. +
          3. Diving into heap dump data in the HPROF Viewer
          4. +
          5. Analyzing heap dump data in the HPROF Analyzer
          6. +
          7. Sorting heap dump data
          8. +
          9. Displaying Java source
          10. +
          11. Viewing a saved HPROF file
          12. +
          13. Renaming an HPROF file
          14. +
          15. Locating a heap dump file on disk
          16. +
          17. Deleting a heap dump file
          18. +
          19. Converting a heap dump file to standard HPROF format
          20. +
          +
        8. +
        9. Tracking and Analyzing Memory Allocation +
            +
          1. Taking and displaying a snapshot of allocation data
          2. +
          3. Sorting allocation data
          4. +
          5. Displaying Java source
          6. +
          7. Viewing a saved allocation tracking file
          8. +
          9. Renaming an allocation tracking file
          10. +
          11. Locating an allocation tracking file
          12. +
          13. Deleting an allocation tracking file
          14. +
          +
        10. +
        + +

        See also

        +
          +
        1. Managing Your App's Memory
        2. +
        3. Addressing Garbage Collection Issues
        4. +
        5. Investigating Your RAM Usage
        6. + +
        7. + Android Monitor +
        8. +
        9. logcat Monitor +
        10. + +
        11. CPU Monitor +
        12. + +
        13. GPU Monitor +
        14. + +
        15. + Network Monitor +
        16. +
        + + +

        + Dependencies and Prerequisites +

        + +
          +
        • + Make sure your development computer detects your hardware device, which often happens + automatically when you connect it to a USB port. +
        • +
        • + Enable USB debugging in + Developer Options on the device or emulator. +
        • + +
        • In your app, set the debuggable property to true in the manifest or + build.gradle file (it’s initially set by default). +
        • + +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • + +
        • + Android Device Monitor can’t be running. +
        • +
        + +
        +
        + +

        + Android Studio provides a Memory Monitor so you can more easily monitor app performance and + memory usage to find deallocated objects, locate memory leaks, and track the amount of memory the + connected device is using. The Memory Monitor reports how your app allocates memory and helps you + to visualize the memory your app uses. It lets you: +

        + +
          +
        • Show a graph of available and allocated memory over time. +
        • +
        • Show garbage collection (GC) events + over time. +
        • +
        • Initiate garbage collection events. +
        • + +
        • Quickly test whether app slowness might be related to excessive garbage collection events. +
        • + +
        • Quickly test whether app crashes may be related to running out of memory. +
        • +
        + +

        + Memory Monitor Workflow +

        + +

        + To profile and optimize memory use, the typical workflow is to run your app and do the following: +

        + +
          +
        1. Profile the app using the Memory Monitor to find out whether undesirable garbage collection + event patterns might be causing performance problems. +
        2. + +
        3. If you see many garbage collection events in a short amount of time, dump the Java heap to + identify candidate object types that get or stay allocated unexpectedly or unnecessarily. +
        4. + +
        5. Start allocation tracking to determine where any problems are happening in your code. +
        6. +
        + +

        + The Java heap data shows in real-time what types of objects your application has allocated, how + many, and their sizes on the heap. Viewing the heap helps you to: +

        + +
          +
        • Get a sense of how your app allocates and frees memory. +
        • + +
        • Identify memory leaks. +
        • +
        + +

        + Allocation tracking records app memory allocations and lists all allocations for the + profiling cycle, including the call stack, size, and allocating code. It helps you to: +

        + +
          +
        • Identify where many similar object types, from roughly the same call stack, are allocated and + deallocated over a very short period of time. +
        • + +
        • Find the places in your code that may contribute to inefficient memory use. +
        • +
        + +

        + Garbage collection roots and dominator trees +

        + +

        + When you dump the Java heap, the Memory Monitor creates an Android-specific Heap/CPU Profiling + (HPROF) file that you can view in the HPROF Viewer. The HPROF Viewer indicates a garbage + collection root with the GC Root icon icon (and a depth of zero) + and a + dominator with the Dominator icon icon. +

        + +

        + There are several kinds of garbage collection roots in Java: +

        + +
          +
        • references on the stack
        • +
        • Java Native Interface (JNI) native objects and memory
        • +
        • static variables and functions
        • +
        • threads and objects that can be referenced
        • +
        • classes loaded by the bootstrap loader
        • +
        • finalizers and unfinalized objects
        • +
        • busy monitor objects
        • +
        + +

        + The HPROF file provides the list of roots to the HPROF Viewer. +

        + +

        + A dominator tree traces paths to objects created by the app. An object dominates another object + if the only way to reach the other object is, directly or indirectly, through the dominator + object. When you examine objects and paths created by an app in an effort to optimize memory use, + try to remove objects that are no longer needed. You can release a dominator object to + release all subordinate objects. For example, in the following figure, if you were to + remove object B, that would also release the memory used by the objects it dominates, which are + objects C, D, E, and F. In fact, if objects C, D, E, and F were marked for removal, but object B + was still referring to them, that could be the reason that they weren’t released. +

        + + + +

        + Memory leak and use analysis +

        + +

        + An app performs better if it uses memory efficiently and releases the memory when it’s no longer + needed. + Memory leaks that are large or that grow over time are the most important to correct. +

        + +

        + One way to optimize memory usage is to analyze large arrays. For example, can you reduce the size + of individual elements in the array to save memory? Does a dominator object point to + an element in the array, preventing it from being garbage-collected? If the dominator object + directly points to an element in the array, the dominator is either the contiguous memory + representing the underlying data of the array, some part of the array, or the array itself. +

        + +

        + Another area that deserves attention is objects that the app no longer needs but continues to + reference. You can gather heap dumps over different periods of time and compare them to determine + if you have a growing memory leak, such as an object type that your code creates multiple times + but doesn’t destroy. These objects could be part of a growing array or an object tree, for + example. To track down this problem, compare the heap dumps and see if you have a particular + object type that continues to have more and more instances over time. +

        + +

        + Continually growing object trees that contain root or dominator objects can prevent subordinate + objects from being garbage-collected. This issue is a common cause of memory leaks, out-of-memory + errors, + and crashes. Your app could have a small number of objects that are preventing a large number of + subordinate objects from being destroyed, so it runs out of memory quickly. To find these issues, + get a heap dump and examine the amount of memory held by root and dominator objects. If the + memory is substantial, you’ve likely found a good place to start optimizing your memory use. +

        + +

        + As you start narrowing down memory issues, you should also use the Allocation Tracker to get a + better understanding of where your memory-hogging objects are allocated. The Allocation Tracker + can be valuable not only for looking at specific uses of memory, but also for analyzing critical + code paths, such as loading and scrolling. For example, tracking allocations when flinging a list + in your app + allows you to see all of the allocations that need to be done for that behavior, what thread they + are on, and where they came from. This information is extremely valuable for tightening up these + paths to reduce the work they need and improve the overall smoothness of the UI. +

        + +

        + It’s useful to examine your algorithms for allocations that are unnecessary or that create the + same object many times instead of reusing them. For example, do you create temporary objects and + variables within recursive loops? If so, try creating an object or variable before + the loop for use within the loop. Otherwise, your app might needlessly allocate many objects and + variables, depending on the number of recursions. +

        + +

        + It’s important to perform allocation tests on portions of your code that create the most and + largest objects, as those areas offer the most optimization opportunities. In addition to unit + tests, you should test your app with production-realistic data loads, especially those algorithms + that are data-driven. Also, make sure to account for the app caching and startup phase, which can + sometimes be slow; allocation analysis is best done after that phase to produce accurate results. +

        + +

        + After you optimize code, be sure to test that it worked. You need to test under different load + conditions and also without running the Memory Monitor tools. Compare results before and after + optimization to make sure that performance has actually improved. +

        + +

        + Memory management for different virtual machines +

        + +

        + Android Monitor uses the Virtual Machine (VM) that the device or emulator uses: +

        + +
          +
        • Android 4.3 (API level 18) and lower uses the Dalvik VM. +
        • + +
        • In Android 4.4 (API level 19), the Android RunTime (ART) VM is an option, while the Dalvik VM + is the default. +
        • + +
        • Android 5.0 (API level 21) and higher uses the ART VM. +
        • +
        + +

        + The VM handles garbage collection. The Dalvik VM uses a mark-and-sweep scheme for garbage + collection. The ART VM uses a generational scheme, combined with mark-and-sweep when memory needs + a more thorough garbage collection, such as when memory becomes excessively fragmented. The + logcat Monitor displays some messages that indicate the type of garbage collection that occurred + and why. +

        + +

        + Memory Monitor results can vary between the different VMs. As a result, if you’re supporting both + VMs, you might want to test with both. In addition, the VMs available for different API levels + can have different behavior. For example, the Dalvik VM in Android 2.3 (API level 10) and lower + uses externally allocated memory while higher versions allocate in the Dalvik heap only. +

        + +

        + You can’t reconfigure the Dalvik and ART VMs to tune performance. Instead, you should examine + your app code to determine how to improve its operation, for example, reducing the size of very + large arrays. +

        + +

        + There are programmatic ways to manipulate when the VM performs garbage collection, although it’s + not a best practice. These techniques can be specific to the VM. For more information, see + Addressing + Garbage Collection (GC) Issues and Investigating Your RAM + Usage. +

        + +

        + The ART VM adds a number of performance, development, and debugging improvements over the Dalvik + VM. For more information, see ART and Dalvik. +

        + +

        + Displaying a Running App in the Memory Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Optionally connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Click the Memory tab. +
        6. + +
        7. Open an app project and run it on a + hardware device or emulator. +
        8. + +
        9. Enable the Memory Monitor by clicking Pause Pause icon to deselect it. +
        10. + + +

          + In the graph, the y-axis displays the free and allocated RAM in megabytes. The x-axis shows the + time elapsed; it starts with seconds, and then minutes and seconds, and so on. The amount of free + memory, measured in megabytes, + is shown in a light color, and allocated memory is a darker color. When there’s a sharp drop in + allocated memory, that indicates a garbage collection event.

          + + + +

          + To force a garbage collection event, click Initiate GC Initiate GC icon. +

          + +

          In the following figure, the VM initiated the first garbage collection event, while the + developer forced the second. +

          + + +
        11. Interact with your app and watch how it affects memory usage in the Memory Monitor. You can + identify garbage collection patterns for your app and determine whether they are healthy and what + you expect. +
        12. + + +

          + The graph can show you potential issues: +

          + +
            +
          • Excessive garbage collection events slow down the app. +
          • + +
          • The app runs out of memory, which causes it to crash. +
          • + +
          • Potential memory leaks. +
          • +
          + +

          + For example, you might see the following signs of problems: +

          + +
            +
          • Your app is static, but you see memory being allocated in the monitor. +
          • + +
          • You see spikes of memory allocations in the monitor, but you don’t think there’s any app + logic to cause this behavior. +
          • +
          +
        13. To stop the Memory Monitor, click Pause Pause icon again to select it. +
        14. + +
        + +

        + Forcing a Garbage Collection Event +

        + +

        + Normally, VMs perform garbage collection only when absolutely needed, since it’s expensive. + However, it can be useful to force garbage collection in certain circumstances. For example, when + locating memory leaks, if you want to determine whether a large object was successfully released + already, you can initiate garbage collection much more aggressively than usual. +

        + +

        + To force a garbage collection event: +

        + + + +

        + Dumping and Analyzing the Java Heap +

        + +

        + When you're monitoring memory usage in Android Studio you can, at the same time, dump the Java + heap to a heap snapshot in an Android-specific HPROF binary format file. The HPROF Viewer + displays classes, instances of each class, and a reference tree to help you track memory usage + and find memory leaks. HPROF is a heap dump format originally supported by J2SE. +

        +

        The Java heap display does the following:

        + +
          +
        • Shows snapshots of a number of objects allocated by type. +
        • + +
        • Samples data every time a garbage collection event occurs naturally or is triggered by you. +
        • + +
        • Helps identify which object types might be involved in memory leaks. +
        • +
        + +

        + However, you have to look for changes over time yourself by tracking what's happening in the + graph. +

        + +

        + The HPROF Analyzer finds the following potential issues: +

        + +
          +
        • All destroyed activity instances that are reachable from garbage collection roots. +
        • + +
        • Where the target program has strings that repeat values. +
        • +
        + +

        + A dominator is at the top of a tree. If you remove it, you also remove the branches of the tree + it dominates, so it’s a potential way to free memory. +

        + +

        + Taking and displaying a snapshot of the Java heap +

        + +

        + To see a snapshot of the Java heap, follow these steps: +

        + +
          +
        1. While the Memory Monitor is running, click Dump Java Heap + Dump Java Heap icon. +
        2. + + +

          + When the icon on the Memory Monitor display changes from + Dump Java Heap Start icon to + Dump Java Heap End icon, the file is ready. Android Studio creates the heap snapshot + file with the + filename Snapshot_yyyy.mm.dd_hh.mm.ss.hprof using + the year, month, day, hour, minute, and second of the capture, for example, + Snapshot_2015.11.17_14.58.48.hprof. +

          + +
        3. Click Captures in the main window. +
        4. + +

          + The Captures window appears. +

          + +
        5. Double-click the file to view it in the HPROF Viewer. +
        6. + +

          + The HPROF Viewer appears: +

          + +

          + The tool displays the following information: +

          + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          ColumnDescription
          Class NameThe Java class responsible for the memory.
          Total CountTotal number of instances outstanding.
          Heap CountNumber of instances in the selected heap.
          SizeofSize of the instances (currently, 0 if the size is variable).
          Shallow SizeTotal size of all instances in this heap.
          Retained SizeSize of memory that all instances of this class is dominating.
          InstanceA specific instance of the class.
          Reference TreeReferences that point to the selected instance, as well as references pointing to the + references.
          DepthThe shortest number of hops from any GC root to the selected instance.
          Shallow SizeSize of this instance.
          Dominating SizeSize of memory that this instance is dominating.
          + +
        7. Select the Heap menu option you want to display: +
            +
          • + App heap - The heap used by the current app. +
          • + +
          • Image heap - The memory mapped copy of the + current app on disk. +
          • + + +
          • + Zygote heap - The common set of libraries and runtime classes and data that + all apps are forked + from. The zygote space is created during device startup and is never allocated into. +
          • + +
          + +
        8. Select the View menu option you want to display: +
            +
          • + Class List View +
          • + +
          • + Package Tree View +
          • +
          +
        + + +

        + Diving into heap dump data in the HPROF Viewer +

        +

        The following steps outline the typical workflow:

        +
          +
        1. In the HPROF viewer, select a class name.
        2. +
        3. Select an instance of that class.
        4. +
        5. Examine the reference tree.
        6. +
        7. Right-click an item to Jump to source or Go to instance, + as needed.
        8. +
        + + + +

        Analyzing heap dump data in the HPROF Analyzer

        +

        You can detect leaked activities and find duplicate strings with the HPROF Analyzer. + Follow these steps:

        +
          +
        1. In the Captures window, double-click an .hprof file to display it in the + HPROF Viewer.
        2. +
        3. Click Capture Analysis on the right side of the main Android Studio window.
        4. + + +

          The HPROF Analyzer appears to the right of the HPROF Analyzer, by default:

          + + + +
        5. In the Analyzer Tasks list, select the items you want to find.
        6. +
        7. Click Perform Analysis Perform Analysis icon.
        8. +
        9. Examine the items in Analysis Results. Click an item to display it in the + HPROF Viewer.
        10. +
        + + + +

        Sorting heap dump data

        +

        Follow this step:

        +
          +
        • In the HPROF Viewer, click a column heading to sort the table by ascending or descending + order.
        • +
        + + +

        Displaying Java source

        +

        For some items displayed in the HPROF Viewer, you can go straight to its source code. + Follow this step:

        +
          +
        • In the HPROF Viewer, right-click a class, instance, or item in the reference tree, and then + select Jump to Source.
        • + + +

          The source code appears in the Code Editor.

          +
        + +

        Viewing a saved HPROF file

        +

        After you do a heap dump, Android Studio automatically stores it so you can view it again. + Follow these steps:

        + +
          +
        1. Click Captures in the main window.
        2. + +

          The Captures window appears.

          +
        3. Open the Heap Snapshot folder.
        4. +
        5. Double-click the file to view it.
        6. +
        + + +

        Renaming an HPROF file

        + +

        If you rename a file from within Android Studio, it continues to appear in Captures + window. Follow these steps:

        +
          +
        1. In the Captures window, right-click the file and select Rename.
        2. +
        3. In the dialog, specify the name of the file and click OK.
        4. +
        + + +

        Locating a heap dump file on disk

        +

        You can quickly discover where Android Studio stored HPROF files on disk.

        + + +

        Follow this step in Android Studio:

        +
          +
        • In the Captures window, right-click a heap snapshot file and select + Show in files.
        • + +

          Android Studio opens an operating system file browser displaying the location where the file + resides.

          +
        +

        Note: If you move an HPROF file, Android Studio no longer + displays it in the Captures window. To display it, use + File > Open. Also, if you want to rename the file, do it + from the Captures window and not in the operating system file browser.

        + +

        Deleting a heap dump file

        + +

        To delete a heap dump file, follow this step:

        +
          +
        • In the Captures window, right-click a heap snapshot file and select + Delete.
        • + +

          Android Studio deletes the file from the Captures dialog and from disk.

          +
        + +

        Converting a heap dump file to standard HPROF format

        +

        You can convert an HPROF file to standard format so you can use it outside of Android Studio with + other analysis tools. Follow these steps:

        +
          +
        1. In the Captures window, right-click a heap snapshot file and select Export to + standard .hprof.
        2. +
        3. In the Convert Android Java Heap Dump dialog, specify a filename and click + OK.
        4. + + +

          Android Studio creates a binary HPROF file in the location you specified.

          +
        + +

        + Tracking and Analyzing Memory Allocation +

        + +

        Android Studio allows you to track memory allocation as it monitors memory use. Tracking memory + allocation allows you to monitor where objects are being allocated when you perform certain + actions. Knowing these allocations enables you to adjust the method calls related to those actions + to optimize app performance and memory use.

        + +

        The Allocation Tracker does the following:

        +
          +
        • Shows when and where your code allocates object types, their size, allocating thread, and stack + traces.
        • +
        • Helps recognize memory churn through recurring allocation/deallocation patterns.
        • +
        • Help you track down memory leaks when used in combination with the HPROF Viewer. For example, + if you see a bitmap object resident on the heap, you can find its allocation location with + Allocation Tracker.
        • +
        + + +

        However, it takes time and experience to learn to interpret the output from this tool.

        + +

        Taking and displaying a snapshot of allocation data

        + +

        Follow these steps:

        +
          +
        1. While the Memory Monitor is running, click Start Allocation Tracking + Start Allocation Tracking icon.
        2. +
        3. Click Start Allocation Tracking + Start Allocation Tracking icon again to + deselect it and end the snapshot.
        4. + +

          The Memory Monitor displays the period when it took the snapshot. In the following + figure, you can see the snapshot period, as shown on the left. By comparison, when you dump the + Java heap, the Memory Monitor displays just the point where the heap snapshot was taken, as + shown on the right.

          + + +

          Android Studio creates the heap snapshot file with the + filename Allocations_yyyy.mm.dd_hh.mm.ss.alloc using the year, month, day, + hour, minute, and second of the capture, for example, + Allocations_2015.11.17_14.58.48.alloc.

          +
        5. Click Captures in the main window.
        6. + + +

          The Captures window appears.

          +
        7. Double-click the file to view it in the Allocation Tracker.
        8. +
        9. Optionally click the graphic icon to display a visual representation of the data. +
        10. +

          + The Allocation Tracker appears: +

          + +

          + + +

          The tool displays the following information:

          + + + + + + + + + + + + + + + + + + + + +
          ColumnDescription
          MethodThe Java method responsible for the allocation.
          CountTotal number of instances allocated.
          SizeThe total amount of allocated memory in bytes.
          + + +
        11. Select the Group By menu option you want to display:
        12. +
            +
          • Group by Allocator
          • +
          • Group by Method
          • +
          + +
        + + + +

        Sorting allocation data

        + +

        Follow this step:

        +
          +
        • In the Allocation Tracker, click a column heading to sort the table by ascending or + descending order.
        • +
        + + +

        Displaying Java source

        +

        For some items displayed in the Allocation Tracker, you can view the Java source. Follow one of + these steps:

        +
          +
        • In the Allocation Tracker, right-click a method and then select Jump to Source. +
        • +
        • In the Allocation Tracker, select a method and then click Jump to Source + Jump to Source icon.
        • +
        + +

        The source code appears in the Code Editor.

        + +

        Viewing a saved allocation tracking file

        +

        After you monitor allocation tracking, Android Studio automatically stores it so you can view it + again. Follow these steps:

        + + +
          +
        1. Click Captures in the main window.
        2. + + +

          The Captures window appears.

          +
        3. Open the Allocation Tracking folder.
        4. +
        5. Double-click the file to view it.
        6. +
        + + +

        Renaming an allocation tracking file

        + +

        If you rename a file from within Android Studio, it continues to appear in the Captures + window. Follow these steps:

        +
          +
        1. In the Captures window, right-click the file and select Rename.
        2. +
        3. In the Rename dialog, specify the name of the file and click OK.
        4. +
        + + +

        Locating an allocation tracking file

        +

        You can quickly discover where Android Studio stored allocation tracking files on disk.

        + + +

        Follow this step in Android Studio:

        +
          +
        • In the Captures window, right-click allocation file and select + Show in Files.
        • + +

          Android Studio opens an operating system file browser displaying the location where the file + resides.

          +
        + +

        Note: If you move an allocation tracking file, Android Studio + no longer displays it in the Captures window. To display the file, use + File + > Open. Also, rename the file from the Captures + window and not in the operating system file browser.

        + +

        Deleting an allocation tracking file

        + + +

        Follow this step:

        +
          +
        • In the Captures window, right-click an allocation tracking file and select + Delete.
        • + +

          Android Studio deletes the file from the Captures dialog and from disk.

          +
        diff --git a/docs/html/tools/help/am-network.jd b/docs/html/tools/help/am-network.jd new file mode 100644 index 0000000000000000000000000000000000000000..ae116ac4e58e994f05758c1992750d3a3a41b40b --- /dev/null +++ b/docs/html/tools/help/am-network.jd @@ -0,0 +1,130 @@ +page.title=Network Monitor +parent.title=Android Monitor +parent.link=android-monitor.html +page.tags=monitor +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Displaying a Running App in the Network Monitor
        2. +
        + +

        See also

        +
          +
        1. + Android Monitor +
        2. +
        3. logcat Monitor +
        4. + +
        5. Memory Monitor +
        6. + +
        7. CPU Monitor +
        8. + +
        9. GPU Monitor +
        10. +
        + +

        Video

        +
          +
        1. Battery Drain and Networking
        2. +
        + +

        + Dependencies and Prerequisites +

        + +
          +
        • + Connect a + hardware device to your development computer. +
        • + +
        • Make sure your development computer detects your hardware device, which often happens + automatically when you connect it to a USB port. +
        • + +
        • + Enable USB debugging in + Developer Options on the device. +
        • + +
        • In your app, set the debuggable property to true in the manifest or + build.gradle file (it’s initially set by default). +
        • + +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • + +
        • + Android Device Monitor can’t be running. +
        • +
        + +
        +
        + +

        + The Network Monitor makes it possible to track when your application is making network requests. + Using this tool, you can monitor how and when your app transfers data, and optimize the underlying + code appropriately. +

        + +

        + By monitoring the frequency of data transfers, and the amount of data transferred during each + connection, you can identify areas of your app that can be made more efficient and use less + battery power. + Generally, you should look for short spikes that can be delayed, or that could cause a later + transfer to be preempted. +

        + + +

        + Displaying a Running App in the Network Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Click the Network tab. +
        6. + +
        7. Open an app project and run it on the hardware device. +
        8. + +
        9. To start the Network Monitor, click Pause Pause icon to + deselect it. +
        10. +

          + Any network traffic begins to appear in the Network Monitor: +

          + +

          + The Network Monitor adds up the amount of time it takes for the device to transmit and receive + kilobytes of data. + The y-axis is in kilobytes per second. The x-axis starts with seconds, and then minutes and + seconds, and so on. +

          +
        11. To stop the Network Monitor, click Pause Pause icon again to + select it. +
        12. +
        \ No newline at end of file diff --git a/docs/html/tools/help/android-monitor.jd b/docs/html/tools/help/android-monitor.jd new file mode 100644 index 0000000000000000000000000000000000000000..4c9f80f8299585a4e0be37649f66fb634469612e --- /dev/null +++ b/docs/html/tools/help/android-monitor.jd @@ -0,0 +1,462 @@ +page.title=Android Monitor +parent.title=Tools +parent.link=index.html +page.tags=monitor +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Displaying Android Monitor
        2. +
        3. Profiling a Running App in Android Monitor
        4. +
        5. Switching between Devices and Apps
        6. +
        7. Taking a Screen Capture of the Device
        8. +
        9. Recording a Video from the Screen
        10. +
        11. Examining System Information
        12. +
        13. Terminating the App
        14. +
        15. Rearranging Android Monitor Windows
        16. +
        17. Removing an App from a Device
        18. +
        + +

        See also

        +
          +
        1. logcat Monitor +
        2. + +
        3. Memory Monitor +
        4. + +
        5. CPU Monitor +
        6. + +
        7. GPU Monitor +
        8. + +
        9. + Network Monitor +
        10. +
        + + +

        + Dependencies and Prerequisites +

        + +
          +
        • In your app, set the debuggable property to true in the manifest or + build.gradle file (it’s initially set by default). +
        • + +
        • Enable ADB integration through Tools > Android > + Enable ADB Integration. +
        • + +
        • Make sure your development computer detects your hardware device, which often happens + automatically when you connect it to a USB port. +
        • +
        • + Enable USB debugging in + Developer Options on the device or emulator. +
        • + +
        • + Android Device Monitor can’t be running. +
        • +
        + +
        +
        + +

        + Android Monitor helps you to profile the performance of your apps so you can optimize, debug, and + improve them. It lets you monitor the following aspects of your apps from a hardware device or + the Android Studio emulator: +

        + +
          +
        • + Log messages, either + system- or user-defined +
        • + +
        • Memory, CPU, and GPU usage +
        • + +
        • Network traffic (hardware device only) +
        • +
        + +

        + Android Monitor contains the logcat, Memory, CPU, GPU, and Network Monitors that you can use + separately to examine these aspects of your apps. +

        + + +

        + Displaying Android Monitor +

        + +

        + Android Monitor is integrated into the Android Studio main window: +

        + +
          +
        • To display Android Monitor, click Android Monitor icon, which by default + is at the bottom of the main window. +
        • + +
        • To hide Android Monitor, click Android Monitor icon again. +
        • +
        + + +

        + Profiling a Running App in Android Monitor +

        + +

        + Follow these steps: +

        + +
          +
        1. Optionally connect a hardware device. +
        2. + +
        3. + Display Android Monitor. +
        4. + +
        5. Open an app project and run the app on a device or + emulator. +
        6. + +
        7. Click the tab for the monitor you want to view and start the monitor, if needed: + +
        +

        + Switching between Devices and Apps +

        + +

        + By default, Android Monitor displays data for your most recently run app. You can switch to + another device and app as needed. In addition to currently running apps, you can view + information about apps that are no longer running so you can continue to view any information + about them that you gathered previously. +

        + +

        + At the top of the Android Monitor main window are two menus listing devices and processes. To + switch to another device, process, or both, follow these steps: +

        + +
          +
        1. Select the device or emulator. +
        2. + +

          + The Device menu lists the devices and emulators that are running or have run during your + current session. There are various status messages that can appear in the Device menu: +

          + +
            +
          • + DISCONNECTED - You closed an emulator or unplugged a device from the + computer. +
          • + +
          • + UNAUTHORIZED - A device needs you to accept the incoming computer + connection. For example, if the connected device displays an Allow USB Debugging + dialog, click OK to allow the connection. +
          • + +
          • + OFFLINE - Android Monitor can’t communicate with a device, even though it + has detected that device. +
          • +
          + +
        3. Select the process. +
        4. +
        + +

        + The Process menu lists the processes that are running or have run during your current session. If + a process is no longer running, the menu displays a status of DEAD. +

        + +

        + Taking a Screen Capture of the Device +

        + +

        + You can take a PNG screenshot of the display on a connected device or the emulator. You can use + the images for your marketing materials as well as for debugging, for example. +

        +

        + Follow these steps: +

        +
          +
        1. + Run your + app from within Android Studio. +
        2. + +
        3. + Select the device and the process in the Android Monitor + menus, if needed. +
        4. + +
        5. Interact with the display on the device or emulator to stage the image you want. +
        6. + +
        7. Click Screen Capture Screen Capture icon in the + Android Monitor toolbar. +
        8. +

          The screenshot appears in a Screenshot Editor window.

          + +
        9. Optionally change the image: +
            +
          • + Reload - Click to take a new screenshot. +
          • + +
          • + Rotate - Click to rotate the image 90 degrees clockwise. +
          • + +
          • + Frame Screenshot - Select this option and choose a device to add an image + of the device to the outside of the screenshot. Select Drop Shadow, + Screen Glare, or both to add these effects to your image. +
          • + +
          • + Chessboard and Grid - Select an option to display these + behind your image. +
          • + +
          • + Zoom In, Zoom Out, or Actual Size - + Click these options to get different perspectives of your image without changing the image + itself. +
          • +
          +
        10. +
        11. Click Save to save the image. +
        12. +
        + +

        + Recording a Video from the Screen +

        + +

        + Android Studio lets you record an MP4 video from your hardware device for a maximum of three + minutes. You can use the video for your marketing materials as well as for debugging, for + example. +

        +

        + Follow these steps: +

        +
          +
        1. + Run your + app from within Android Studio. +
        2. + +
        3. + Select the device and the process in the Android Monitor + menus, if needed. +
        4. + +
        5. Interact with the display on the device or emulator to stage the start of the video. +
        6. + +
        7. Click Screen Record Screen Record icon in the Android Monitor toolbar. +
        8. +

          The screenshot appears in a Screenshot Editor window.

          + +
        9. In the Screen Recorder Options dialog, optionally change the recording options: +
        10. +
            +
          • + Bit Rate - Type a bit rate. The default is 4 Mbps. +
          • + +
          • + Resolution - Type a width and height value in pixels. The value must be a + multiple of 16. The default is the resolution of the device. +
          • +
          +
        11. Click Start Recording to start the recording. +
        12. + +
        13. Click Stop Recording to stop the recording. +
        14. + +
        15. In the Save As dialog, save the MP4 file. +
        16. + +
        17. In the Screen Recorder dialog, click one of the buttons to show the file location, + open the recording in a player, or to dismiss the dialog. +
        18. +
        + +

        + Examining System Information +

        +

        + You can view dumpsys output from within Android Monitor. Follow these steps: +

        + +
          +
        1. + Run your + app from within Android Studio. +
        2. + +
        3. + Select the device and the process in the Android Monitor + menus, if needed. +
        4. + +
        5. Click System Information System Information icon and then a + menu item in the Android Monitor + toolbar. +
        6. + +

          + The menu items display different types of dumpsys output: +

          + + + +

          + The information appears in an editable text file in the Code Editor. +

          +
        + +

        + Terminating the App +

        + +

        + If you want to stop an app you’ve run from Android Studio, follow these steps: +

        + +
          +
        1. + Select the device and the process in the Android Monitor + menus, if needed. +
        2. + +
        3. Click Terminate Application Terminate App icon. +
        4. + + +

          + The process status changes to DEAD in the Processes menu. The emulator or device + continues to run, but the app closes. Any running monitors in Android Monitor stop. +

          +
        + +

        + Rearranging Android Monitor Windows +

        + +

        + You can rearrange the Android Monitor windows for optimal viewing during your tests: +

        + +
          +
        • To reorder the monitors, move the tabs back and forth. +
        • + +
        • To move a monitor to a standalone window, grab a tab and move it to a different location on + the screen. Or, select Gear menu icon > Floating Mode. +
        • + + +
        • To dock a standalone window, grab it and move it to the Android Monitor area. Or, select + Gear menu icon > Floating Mode to deselect it. +
        • + + +
        • To combine two monitor displays together, grab a tab and move it onto another monitor. +
        • + +
        • To hide a monitor, click the Hide icon icon. To make it reappear, click the icon of + the monitor on the far right of the row of tabs. +
        • +
        + +

        + Removing an App from a Device +

        + +

        + To remove an app from a device you use for development, use the normal uninstall procedure on the + device. +

        + +

        + If you run a new version of an app from Android Studio that’s been already installed on a + hardware device, the device displays an Application Installation Failed dialog. Click + OK to install the new version of the app. +

        + + + + diff --git a/docs/html/tools/help/android.jd b/docs/html/tools/help/android.jd old mode 100644 new mode 100755 index 0d7d2aaca8c11e7b41e5517cf4479a2b3460b2d2..fa359e99e559e7464f65438a9a872ef910d25da4 --- a/docs/html/tools/help/android.jd +++ b/docs/html/tools/help/android.jd @@ -17,7 +17,7 @@ parent.link=index.html "{@docRoot}tools/help/sdk-manager.html">SDK Manager.
      -

      If you are using Android Studio or Eclipse, the android tool's features are +

      If you are using Android Studio, the android tool's features are integrated into the IDE, so you should not need to use this tool directly.

      Note: The documentation of options below is not exhaustive diff --git a/docs/html/tools/help/app-link-indexing.jd b/docs/html/tools/help/app-link-indexing.jd new file mode 100644 index 0000000000000000000000000000000000000000..611373a28ab5e455b07ba22f6fccc6865f43cb6f --- /dev/null +++ b/docs/html/tools/help/app-link-indexing.jd @@ -0,0 +1,514 @@ +page.title=Deep Link and App Indexing API Support in Android Studio +parent.title=Tools +parent.link=index.html +page.tags=app indexing +@jd:body + +

      + +

      Android Studio helps you add deep links, app indexing, and search functionality to your apps. + These features can help to drive more traffic to your + app, discover which app content is used most, make it easier for users to find content in an + installed app, and attract new users.

      + +

      Typical Workflow

      + +

      To use Android Studio to add deep link, app indexing, and search features to your app, follow + these basic steps:

      + +
        +
      1. Add intent filters and code to handle incoming intents.
      2. +
      3. Associate a website with your app.
      4. +
      5. Add App Indexing API code.
      6. +
      + +

      Intent filters and the App Indexing API are ways to implement deep links and app indexing, but + there are other possible implementations as well. See + Alternate Android Indexing Methods + for more information.

      + +

      Intent filters for deep links

      + +

      Android Studio can create a basic intent filter in your manifest that you can customize to + define deep link URLs for your app. You can then write Java code in an activity to handle the + intent. This implementation lets users directly open the specified app activity by + clicking a deep link. Users can see the deep links in google.com in a browser, in the + Google Search app, and in Google Now on Tap.

      + +

      Website association with deep links

      + +

      After setting up deep links for your app, you can associate your website with your app by using + the Google Search Console and Google Play Developer Console. Afterward, Google indexes your app + for URLs defined in + your intent filters and begins to include them in search results. In addition, you can optionally + exclude app content from Google Search. After you associate a website with your app, features + such as Now on Tap and enhanced search result display (like including your app icon) + become available.

      + +

      As an alternative to associating your app with a website, + for Android 6.0 (API level 23) and higher, you can add + default handlers and verification for deep links + instead.

      + +

      Chrome displaying google.com serves search results with deep links that are accessible to both + signed-in users and those who aren't. Google Search app users must be signed in to see deep links + in their search results.

      + +

      App Indexing API code in activities

      + +

      Next, if you want to support additional search features, such as autocompletions, you can + add App Indexing API code to your app. Android Studio can create skeleton code in an activity + that you can then customize to support app indexing. The App Indexing API allows app indexing + even if + Googlebot + can’t get content from your app.

      + +

      Deep link and App Indexing API testing

      + +

      Android Studio helps you test your code with the following features:

      + +
        +
      • Deep link testing helps you verify that a specified deep link can launch an app.
      • +
      • The logcat Monitor helps you test App Indexing API calls in an activity.
      • +
      • The Android Lint tool has warnings for certain issues involving deep links and the App Indexing + API. These warnings and errors appear in the Code Editor and in Lint inspection results.
      • +
      + +

      The details for implementing deep links and app indexing are described next. + + +

      Adding an Intent Filter for Deep Linking and Google Search

      + +

      To use Android Studio features to add an intent filter defining a deep link, follow these + steps:

      + +
        +
      1. In the Android view + of the Project window, double-click the AndroidManifest.xml file to open it + in the Code Editor.
      2. +
      3. Insert an intent filter in one of the following ways:
      4. +
          +
        • In an <activity> element, click in the left column so the light bulb + Lightbulb icon appears. Click + Lightbulb icon + and select Create Deep Link.
        • +
        • Right-click in an <activity> element and select Generate + > Deep Link.
        • +
        • Place your cursor in an activity, and then select Code > + Generate > Deep Link.
        • +
        + +

        The Code Editor adds skeleton code using the + intention action and + generate code mechanisms.

        + +

        The Code Editor adds an intent filter similar to the following:

        +
        +<!-- ATTENTION: This intent was auto-generated. Follow instructions at
        + https://g.co/AppIndexing/AndroidStudio to publish your Android app deep links. -->
        +<intent-filter>
        +   <action android:name="android.intent.action.VIEW" />
        +
        +   <category android:name="android.intent.category.DEFAULT" />
        +   <category android:name="android.intent.category.BROWSABLE" />
        +   <!-- ATTENTION: This data URL was auto-generated.
        +     We recommend that you use the HTTP scheme.
        +     TODO: Change the host or pathPrefix as necessary. -->
        +   <data
        +       android:host="www.example.com"
        +       android:pathPrefix="/gizmos"
        +       android:scheme="http" />
        +</intent-filter>
        +
        + +
      5. Modify the + <data> element + and optionally the <category> element, as needed.
      6. + + +

        We recommend that you define a <data> element that supports URLs that you’ll + add in the future. In the previous sample code, for example, Google will index any URLs starting + with www.example.com/gizmos. Also, remember to + include a deep link for your app home screen so it’s included in search results.

        + +

        Deep link URLs can be the same as the URLs of the comparable pages on your website.

        + +
      7. In the corresponding activity, + add Java code + to read data from the intent filter and direct the app to respond accordingly.
      8. +
      9. Test your deep link.
      10. + +
      + +

      To support Google Search for your deep links, follow these steps:

      +
        +
      1. Define an association + between your app and your website.
      2. +

        Alternatively, for Android 6.0 (API level 23) and higher, add + link default handling and verification.

        +
      3. Optionally + exclude app URLs + from the Google index.
      4. +
      5. Optionally add App Indexing API code to support additional search + features.
      6. +
      + + +

      To test and debug your links, you can use the following Android Studio features:

      + + +

      In addition, you can + preview your APK in the Google Search Console + to test your deep links, whether the app is associated with a website or not.

      + + + +

      Adding App Indexing API Skeleton Code to an Activity

      + +

      After adding deep links, you can add App Indexing API code to an activity to support additional + search features.

      + +

      To add App Indexing API code to an activity, follow these steps:

      +
        +
      1. In Android view + in the Project window, double-click the activity Java file to open it in the + Code Editor.
      2. +
      3. Insert skeleton code in one of the following ways:
      4. +
          +
        • In an activity definition, click in the Java code so the light bulb + Lightbulb icon appears. + Click Lightbulb icon + and select Insert App Indexing API Code.
        • +
        • Right-click in an activity definition and select Generate > + App Indexing API Code.
        • +
        • Place the cursor in an activity, and then select Code > + Generate > + App Indexing API Code.
        • +
        + + +

        The Code Editor adds skeleton code using the + intention action and + generate code + mechanisms.

        + +

        If you don’t see the App Indexing API Code menu item, make sure your cursor is + within an activity, and check your code for App Indexing API methods. The Code Editor can insert + skeleton Java code into an activity in the following circumstances:

        + +
          +
        • The activity doesn’t have an onStart() method, or the onStart() + method doesn’t contain an AppIndexApi.start() or AppIndexApi.view() + call.
        • +
        • The activity doesn’t have an onStop() method, or the onStop() + method doesn’t contain an AppIndexApi.end() or AppIndexApi.viewEnd() + call.
        • +
        + + +

        The Code Editor adds Java code similar to the following:

        +
        +   /**
        +    * ATTENTION: This was auto-generated to implement the App Indexing API.
        +    * See https://g.co/AppIndexing/AndroidStudio for more information.
        +    */
        +   private GoogleApiClient client;
        +
        +       // ATTENTION: This was auto-generated to implement the App Indexing API.
        +       // See https://g.co/AppIndexing/AndroidStudio for more information.
        +       client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
        +   }
        +
        +
        +   @Override
        +   public void onStart() {
        +       super.onStart();
        +
        +       // ATTENTION: This was auto-generated to implement the App Indexing API.
        +       // See https://g.co/AppIndexing/AndroidStudio for more information.
        +       client.connect();
        +       Action viewAction = Action.newAction(
        +               Action.TYPE_VIEW, // TODO: choose an action type.
        +               "Main Page", // TODO: Define a title for the content shown.
        +               // TODO: If you have web page content that matches
        +               // this app activity's content,
        +               // make sure this auto-generated web page URL is correct.
        +               // Otherwise, set the URL to null.
        +               Uri.parse("http://host/path"),
        +               // TODO: Make sure this auto-generated app deep link URI is correct.
        +               Uri.parse("android-app://com.example/http/host/path")
        +       );
        +       AppIndex.AppIndexApi.start(client, viewAction);
        +   }
        +
        +   @Override
        +   public void onStop() {
        +       super.onStop();
        +
        +       // ATTENTION: This was auto-generated to implement the App Indexing API.
        +       // See https://g.co/AppIndexing/AndroidStudio for more information.
        +       Action viewAction = Action.newAction(
        +               Action.TYPE_VIEW, // TODO: choose an action type.
        +               "Main Page", // TODO: Define a title for the content shown.
        +               // TODO: If you have web page content that matches
        +               // this app activity's content,
        +               // make sure this auto-generated web page URL is correct.
        +               // Otherwise, set the URL to null.
        +               Uri.parse("http://host/path"),
        +               // TODO: Make sure this auto-generated app deep link URI is correct.
        +               Uri.parse("android-app://com.example/http/host/path")
        +       );
        +       AppIndex.AppIndexApi.end(client, viewAction);
        +       client.disconnect();
        +   }
        +}
        +
        + +

        For more information about the App Indexing API methods, see + Android API for App Indexing. + For information about the action types, see the + Action Class Constant Summary. +

        + +

        If your app isn’t already configured for the Google Play Services App Indexing API, the Code + Editor also modifies your build.gradle and AndroidManifest.xml files + to include it. If your app already depends on it but the version is lower than 8.1.0, your app + is upgraded to version 8.1.0. For more information and to correct any issues, see + Add Google Play Services + and Setting Up Google Play Services. +

        + +
      5. Customize the skeleton code, as needed.
      6. + +

        Pay attention to the comments, which help you find areas that need work, such as setting the + title and URLs. For more information, see + Add the App Indexing API. +

        +
      7. Verify that your app indexing code is working by using + the logcat Monitor.
      8. +
      + + + +

      To test and debug your App Indexing API code, you can use the following Android Studio + features:

      + + +

      In addition, you can + preview your APK in the Google Search Console.

      + + +

      Testing a Deep Link

      + +

      When you run your app from Android Studio, you can specify a deep link to launch so you can + test it.

      + +

      To launch a deep link from Android Studio, follow these steps:

      +
        +
      1. In Android Studio, open your project in + Android view.
      2. +
      3. After opening a project, select Run > Edit Configurations. +
      4. +
      5. In the Run/Debug Configurations dialog, beneath Android Application, + select the module you want to test.
      6. +
      7. Select the General tab.
      8. +
      9. In the Launch field, select Deep Link.
      10. +
      11. In the Deep Link field, click to select from a list of + defined deep links.
      12. + +

        Or type the URL you want to test, for example, http://example.com/gizmos.

        +
      13. Click OK.
      14. +
      15. Select Run > Run app or Debug app.
      16. +
      17. If the Device Chooser dialog appears, select a connected device or an + emulator, and click OK.
      18. + +

        If the link is successful, the app launches in the device or emulator, and displays the app at + the specified activity. Otherwise, an error message appears in the Run window.

        +
      + +

      For more information about creating run configurations at the project, default, and module + levels, see + Building and Running from Android Studio. +

      + +

      You can view App Indexing API log messages while the app is running, as described next.

      + + +

      Viewing App Indexing API Messages in the logcat Monitor

      + +

      The logcat Monitor can display app indexing log messages to determine if your App Indexing API + code is pushing the correct data to the cloud. For example, you can check the app title and the + URL. The logcat Monitor is part of Android Monitor in Android Studio.

      + +

      Follow these steps:

      +
        +
      1. Run your app in Android Studio so it launches a deep link.
      2. +
      3. Display Android Monitor + and click the logcat tab.
      4. +
      5. Set the log level to + Verbose.
      6. +
      7. In the filter menu, select No Filters.
      8. +
      9. Search the log for the string + "appindex".
      10. + +

        App indexing log messages should appear. If they don’t, check the following items:

        +
          +
        • Is Google Play Services installed on the device or emulator? You can check the settings on + the device.
        • +
        • Is the Google Play Services version on the device or emulator lower than the version specified + in the build.gradle file? If so, it might be out-of-date and should be upgraded to + a higher version.
        • +
        + +

        For more information, see the + Google Play Services Download + page and Setting Up Google Play Services. +

        +
      11. Visit app pages that trigger App Indexing API calls.
      12. +
      + + +

      Configuring Lint

      + +

      You can use the Android Studio built-in Lint tool to check whether you have valid deep links + defined in the manifest and have implemented the App Indexing API correctly in activities.

      + +

      You can view deep link and app indexing warnings and errors in two ways:

      +
        +
      • As pop-up text in the Code Editor. When Lint finds a problem, it highlights the problematic + code in yellow, or underlines the code in red for more serious issues.
      • +
      • In the Lint Inspection Results window after you select Analyze > + Inspect Code.
      • +
      + + + +

      To set default Lint checks for deep links and the App Indexing API, follow these steps:

      +
        +
      1. In Android Studio, open your project in + Android view. +
      2. +
      3. Select File > Other Settings > + Default Settings.
      4. +
      5. In the Default Preferences dialog, select Editor > + Inspections.
      6. +
      7. In the Profile field, select Default or + Project Default to set the scope for Android Studio or just for this project, + respectively.
      8. +
      9. Expand the Android Lint category and change the Lint settings as needed:
      10. +
          +
        • Missing Support for Google App Indexing - Reports a warning if the app hasn’t + implemented deep links, which are used by Google Search. This warning setting is enabled by + default.
        • +
        • Missing Support for Google App Indexing API - Reports if an app hasn’t + implemented the App Indexing API at all. This warning setting is disabled by default.
        • +
        • Incorrect Usage of App Link for Google App Indexing - Reports deep link + errors in manifest code. This error setting is enabled by default.
        • +
        + +

        For example, the following Lint warning appears for the first setting:

        +

        + +
      11. Click OK.
      12. +
      + + + +

      To produce a list of Lint checks displayed in the Inspection Results window, + follow these steps:

      +
        +
      1. In Android Studio, open your project in + Android view + and select a portion of your project that you want to test.
      2. +
      3. Select Analyze > Inspect Code.
      4. +
      5. In the Specify Inspection Scope dialog, select the inspection scope and profile.
      6. + +

        The scope specifies the files you want to analyze, and the profile specifies the Lint checks + you’d like to perform.

        +
      7. If you want to change the Lint settings, click . In the Inspections + dialog, optionally click Manage to define a new profile, specify the Lint + settings you want, and then click OK.
      8. +

        In the Inspections dialog, you can search for the string "app indexing" +to find the deep link and App Indexing API Lint checks. Note that changing Lint settings for a +profile in the Inspections dialog doesn’t change the default settings, as described in +the previous procedure. It does change the settings for profiles displayed in the +Inspections dialog, however.

        +
      9. Click OK.
      10. +

        The results appear in the Inspection Results window.

        + +
      diff --git a/docs/html/tools/help/avd-manager.jd b/docs/html/tools/help/avd-manager.jd old mode 100644 new mode 100755 index 20f6253cdbf6ecd04e6bbc80056e5e849a5dbf81..b3dcad56823d9b062309b882935feb607809e5f8 --- a/docs/html/tools/help/avd-manager.jd +++ b/docs/html/tools/help/avd-manager.jd @@ -8,9 +8,6 @@ and manage Android Virtual Devices (AVDs), which are required by the

      You can launch the AVD Manager in one of the following ways:

        -
      • In Eclipse: select Window > Android Virtual Device Manager, or click - the AVD Manager icon in the toolbar.
      • -
      • In Android Studio: select Tools > Android > AVD Manager, or click the AVD Manager icon in the toolbar.
      • diff --git a/docs/html/tools/help/gltracer.jd b/docs/html/tools/help/gltracer.jd old mode 100644 new mode 100755 index 700ee39c5766e7481bab8c63fcc00339c8d43ba8..866bdc93578d24d473b4a9c18495df8da5507da7 --- a/docs/html/tools/help/gltracer.jd +++ b/docs/html/tools/help/gltracer.jd @@ -26,17 +26,6 @@ Level 16) or higher.

        Running Tracer

        -

        Tracer can be run as part of the Eclipse Android Development Tools (ADT) plugin or as part of the -Device Monitor tool.

        - -

        To run Tracer in Eclipse:

        - -
          -
        1. Start Eclipse and open a workspace that contains an Android project.
        2. -
        3. Activate the perspective for Tracer by choosing Window > Open Perspective > -Other...
        4. -
        5. Select Tracer for OpenGL ES and click OK.
        6. -

        To run Tracer in Device Monitor:

        @@ -64,7 +53,7 @@ analysis.

        1. Connect the Android device using a USB cable and make sure it is enabled for debugging. For more information, see Using Hardware Devices.
        2. -
        3. In Eclipse or Device Monitor, activate the Tracer for OpenGL ES +
        4. In the Device Monitor, activate the Tracer for OpenGL ES perspective.
        5. On the toolbar, click the trace capture button ().
        6. @@ -94,10 +83,10 @@ Be aware that using this option can result in large trace files.

          To review a captured trace:

            -
          1. In Eclipse or Device Monitor, activate the Tracer for OpenGL ES +
          2. In Device Monitor, activate the Tracer for OpenGL ES perspective.
          3. On the toolbar, click the trace load button ().
          4. After loading a trace, select a frame and review the OpenGL ES calls. Drawing commands are highlighted in blue.
          5. -
          \ No newline at end of file +
        diff --git a/docs/html/tools/help/image-asset-studio.jd b/docs/html/tools/help/image-asset-studio.jd new file mode 100644 index 0000000000000000000000000000000000000000..818b7442007f4a3275c2cfe697d66d4bb77dc0fb --- /dev/null +++ b/docs/html/tools/help/image-asset-studio.jd @@ -0,0 +1,200 @@ +page.title=Image Asset Studio +parent.title=Tools +parent.link=index.html +page.tags=image asset +@jd:body + + + +

        Image Asset Studio helps you to generate custom icons for your Android applications from existing + image, clipart, or text-string resources. It generates a set of icons at the appropriate resolution + for each generalized screen + density that your app supports. + The newly generated icons are placed in density-specific folders (for example, mipmap-mdpi/ + or drawable-xxxhdpi/), which + reside in the application’s res/ folder. At runtime, Android uses the appropriate + resource based on the screen density of the device your application is running on.

        + +

        Image Asset Studio generates the following asset types:

        +
          +
        • Launcher icons.
        • +
        • Action bar and tab icons.
        • +
        • Notification icons.
        • +
        + +

        This guide shows how to generate these assets using Image Asset Studio.

        + +

        Accessing Image Asset Studio

        +

        Follow these steps to access Image Asset Studio:

        +
          +
        1. In Android Studio, open an Android app project.
        2. +
        3. In the project-view-pane + on the left side of the screen, select Android from the dropdown menu. The Android + project view appears in the pane. +
        4. +
        5. Right-click the res/folder and select New > Image +Asset. The Image Asset Studio window appears.
        6. +
        + + +

        Creating Icons

        +

        You can generate icons from image, clipart +, or text-string resources. This section explains how to work with +each of these resources. +

        + + +

        From an image resource

        +
          +
        1. Open the Asset Type dropdown menu and select an icon type.
        2. +
        3. From the available Foreground options, select Image.
        4. +
        5. Specify the asset to use by entering a filename in the Image file field or by + navigating to and selecting a file via the file browser. Image Asset Studio supports the following + file types, in descending order of desirability: PNG, JPG, and GIF. +

          The icon to be generated appears in the Preview pane, displayed at sizes corresponding + to different screen densities.

          + +

          The rest of the settings on this screen are optional. +To learn how to customize your icons using these options, + see Customizing Icons.

        6. +
        7. Save your icons to the project folder. +
        8. +
        + + +

        From a clipart resource

        +
          +
        1. Open the Asset Type dropdown menu and select an icon type.
        2. +
        3. From the available Foreground options, select Clipart. The +Choose button appears.
        4. +
        5. Click Choose. A window containing clipart resources appears.
        6. +
        7. Select a clipart resource. +

          The icon to be generated appears in the Preview pane, displayed at sizes corresponding +to different screen densities.

          +

          The rest of the settings on this screen are optional. + To learn how to customize your icons using these options, + see Customizing Icons.

        8. +
        9. Save your icons to the project folder. +
        10. +
        + + + +

        From a text-string resource

        +
          +
        1. Open the Asset Type dropdown menu and select an icon type.
        2. +
        3. From the available Foreground options, select Text.
        4. +
        5. Enter into the Text field the text you want to turn into an icon. +

          The icon to be generated appears in the Preview pane, displayed at sizes corresponding +to different screen densities.

        6. +
        7. To change the font, select a font from the Font dropdown menu. +

          The rest of the settings on this screen are optional. +To learn how to customize your icons using these options, +see Customizing Icons.

        8. +
        9. Save your icons to the project folder. +
        10. +
        + + +

        Customizing Icons

        +

        Image Asset Studio lets you customize various visual effects for your icons. The following options +are of particular note.

        + +
          +
        • Trim surrounding blank space: This option allows you to adjust the margin between the +icon graphic and border. +
          + +
          Figure 1: An icon before and after trimming.
          +
          +
        • Additional Padding: This option allows you to adjust the icon's padding on all four +sides. Use the slider to select a value between 0 and 100 pixels. For example, for a 100px X 100px +image with padding of 25px, the resulting image is 150px X 150px.
        • +
        • Theme: This option allows you to set a theme for your action bar and tab icons. + You can tint icons using the HOLO_DARK or HOLO_LIGHT theme or create + a custom theme using the color palette.
        • +
        • Resource name: This option allows you to specify a resource name other than the + default one. If an asset with the specified resource name already exists, Image Asset Studio warns + you that it is going to overwrite the existing asset with this one.
        + + +

        Saving Icons

        +

        This section explains how to save your icons to the res/ folder. Do so by following + these steps:

        +
          +
        1. From the initial screen that appeared when you opened Image Asset Studio, click + Next. Image Asset Studio opens a new window.
        2. +
        3. Confirm the target module and resource directory settings for the icon. + If you don't want to use the default target module or resource directory, you can + enter your own values for them. +
        4. +
        5. Click Finish. The Image Asset Studio generates new icon files, in PNG format, + according to the options you have chosen. +
        6. +
        + +

        Note: Launcher icon files reside in a different location from that + of other icons. They are located in the mipmap/ folder. All other icon files reside + in the drawable/ folder of your project.

        + + +

        Configuring Build Properties

        +

        To change the module and resource directory, follow these steps:

        +
          +
        1. In the Target Module field, select a module to add the resource to. +For more information, +see +Creating an Android Module. +
        2. +
        3. In the Res Directory field, select an option for where to place the image + asset: +
            +
          • src/main/res: This source set applies to all build variants, including debug + and release.
          • +
          • src/debug/res, src/release/res: These source sets override the main + source set and apply to one version of a build. The debug source set is for debugging only. +
          • +
          • User-defined: To define your own source set, select File > Project + Structure > app > Build Types. + For example, you could define a beta source set and create a version of an icon that includes the + text "BETA” in the bottom right corner. For more information, see + + Work with build variants.
          • +
          +
        + + + + +

        Referring to an Image Resource in Code

        + +

        After you have an image resource in the res/directory of your project, you can + reference it from your Java code or your XML layout using its + + resource ID.

        + +

        For example, the following XML references the ic_launcher icon in the +mipmap/ folder.

        +
        +<application android:name="ApplicationTitle"
        +    android:label="@string/app_label"
        +    android:icon="@mipmap/ic_launcher" >
        + +

        The following Java code sets an +ImageView to use + the drawable/myimage.png resource:

        +
        ImageView imageView = (ImageView) findViewById(R.id.myimageview);
        +imageView.setImageResource(R.drawable.myimage);
        diff --git a/docs/html/tools/help/index.jd b/docs/html/tools/help/index.jd old mode 100644 new mode 100755 index 53247d1e582562e902770a58a7367d62e99d2501..411f908463ba567168ba0591528d1bffa98ee619 --- a/docs/html/tools/help/index.jd +++ b/docs/html/tools/help/index.jd @@ -43,6 +43,7 @@ avd) the emulator (emulator), and the Dalvik Debug Monitor S
        android
        Lets you manage AVDs, projects, and the installed components of the SDK.
        +
        Hierarchy Viewer (hierarchyviewer)
        Provides a visual representation of the layout's View hierarchy with performance information for each node in the layout, and a magnified view of the display to closely examine the @@ -58,6 +59,7 @@ avd) the emulator (emulator), and the Dalvik Debug Monitor S
        sqlite3
        Lets you access the SQLite data files created and used by Android applications.
        + @@ -65,6 +67,10 @@ avd) the emulator (emulator), and the Dalvik Debug Monitor S

        Debugging Tools

        +
        Android Monitor
        +
        Android Monitor is integrated into Android Studio and provides logcat, memory, CPU, GPU, and + network monitors for app debugging and analysis.
        +
        adb
        Android Debug Bridge (adb) is a versatile command line tool that lets you communicate with an emulator instance or connected Android-powered device. It also provides access to the @@ -73,9 +79,8 @@ avd) the emulator (emulator), and the Dalvik Debug Monitor S
        ADB Shell Commands
        Learn the commands available for advanced command-line operations.
        -
        Dalvik Debug Monitor -Server (ddms)
        -
        Lets you debug Android applications.
        +
        Dalvik Debug Monitor Server (ddms)
        +
        Lets you debug Android apps.
        Device Monitor
        Android Device Monitor is a stand-alone tool that provides a graphical user interface for @@ -100,6 +105,11 @@ you can view the file in a profiling tool of your choice.
        traceview
        Provides a graphical viewer for execution logs saved by your application.
        +
        Tracer for OpenGL ES
        +
        Allows you to capture OpenGL ES + commands and frame-by-frame images to help you understand how your app is executing + graphics commands.
        +
        @@ -134,10 +144,6 @@ content is allowed.
        A command line utility that lets you encode PNG images to the ETC1 compression standard and decode ETC1 compressed images back to PNG.
        -
        Tracer for OpenGL ES
        -
        Allows you to capture OpenGL ES commands and frame by frame images to help you understand - how your graphics commands are being executed.
        - @@ -153,8 +159,8 @@ device.

        The other platform tools, such as aidl, aapt, dexdump, and dx, are typically called by the Android -build tools or Android Development Tools (ADT), so you rarely need to invoke these tools directly. -As a general rule, you should rely on the build tools or the ADT plugin to call them as needed.

        +build tools, so you rarely need to invoke these tools directly. +As a general rule, you should rely on the build tools to call them as needed.

        Note: The Android SDK provides additional shell tools that can be accessed through adb, such as bmgr and diff --git a/docs/html/tools/help/layoutopt.jd b/docs/html/tools/help/layoutopt.jd old mode 100644 new mode 100755 index 1a183263f1b9ef46c1e10360a7a2aaa89088370c..1bc6c64aad4b4b437d6e3bd315ec2737fdac4b16 --- a/docs/html/tools/help/layoutopt.jd +++ b/docs/html/tools/help/layoutopt.jd @@ -4,13 +4,18 @@ parent.link=index.html @jd:body -

        Note: The Android layoutopt tool has been replaced by the {@code lint} tool beginning in ADT and SDK Tools revision 16. The {@code lint} tool reports UI layout performance issues in a similar way as layoutopt, and detects additional problems.

        -

        For more information about using {@code lint}, see Improving Your Code with lint and the lint reference documentation.

        +

        Note: The Android layoutopt tool has been replaced +by the {@code lint} tool beginning in SDK Tools revision 16. The {@code lint} tool reports UI +layout performance issues in a similar way as layoutopt, and detects additional problems.

        +

        For more information about using {@code lint}, see +Improving Your Code with lint and the +lint reference documentation.

        layoutopt is a command-line tool that helps you optimize the layouts and layout hierarchies of your applications.

        -

        This document is a reference to the available command line options. For more information and sample +

        This document is a reference to the available command line options. For more information and +sample output of the tool, see Optimizing layouts with layoutopt.

        diff --git a/docs/html/tools/help/logcat.jd b/docs/html/tools/help/logcat.jd index b30971ef110907a3a822e95fcee9d94930d51739..1d758486c972db29eb9275660dd92efa01e81b2e 100644 --- a/docs/html/tools/help/logcat.jd +++ b/docs/html/tools/help/logcat.jd @@ -3,15 +3,25 @@ parent.title=Tools parent.link=index.html @jd:body +
        +
        +

        See also

        + +
          +
        1. Android Monitor
        2. +
        +
        +
        +

        The Android logging system provides a mechanism for collecting and viewing system debug output. Logs from various applications and portions of the system are collected in a series of - circular buffers, which then can be viewed and filtered by the logcat command. You can use + circular buffers, which then can be viewed and filtered by the logcat command. You can use logcat from an ADB shell to view the log messages.

        For complete information about logcat options and filtering specifications, see Reading and Writing Logs.

        -

        For more information on accessing logcat from DDMS, instead of the command line, see +

        For more information on accessing logcat from DDMS, instead of the command line, see Using DDMS.

        Syntax

        @@ -34,7 +44,7 @@ $ adb shell

        Options

        The following table describes the command line options of logcat.

        - + @@ -46,7 +56,7 @@ $ adb shell diff --git a/docs/html/tools/help/monitor.jd b/docs/html/tools/help/monitor.jd old mode 100644 new mode 100755 index 5f7b5ce9a19b021cfedc6d45e36193ae869753bb..3541cc30d928546f284ef3198d8989dc57dba3bb --- a/docs/html/tools/help/monitor.jd +++ b/docs/html/tools/help/monitor.jd @@ -8,6 +8,7 @@ page.title=Device Monitor
        1. Investigating Your RAM Usage
        2. +
        3. Android Monitor
        @@ -47,6 +48,4 @@ encapsulates the following tools:

        Start an Android emulator or connect an Android device via USB cable, and connect Device Monitor to the device by selecting it in the Devices window.

        -

        Note: Only one debugger can be connected to your device at a time. -If you're using ADT, you may need to close the debugging tool before launching the Device Monitor -in order for the device to be fully debuggable.

        +

        Note: Only one debugger can be connected to your device at a time.

        diff --git a/docs/html/tools/help/proguard.jd b/docs/html/tools/help/proguard.jd old mode 100644 new mode 100755 index b5d84ed85259769cce35c9d0d940bebf776ca921..e26aca0e823baf2622401b5099cd416abf8936a5 --- a/docs/html/tools/help/proguard.jd +++ b/docs/html/tools/help/proguard.jd @@ -1,6 +1,7 @@ page.title=ProGuard parent.title=Tools parent.link=index.html +page.metaDescription=Use ProGuard to shrink, optimize, and obfuscate your code prior to release. @jd:body
        @@ -117,76 +118,6 @@ parent.link=index.html - -

        Enabling ProGuard (Ant Builds)

        - -

        When you create an Android project, a proguard.cfg file is automatically - generated in the root directory of the project. This file defines how ProGuard optimizes and - obfuscates your code, so it is very important that you understand how to customize it for your - needs. The default configuration file only covers general cases, so you most likely have to edit - it for your own needs. See the following section about Configuring - ProGuard for information on customizing the ProGuard configuration file.

        - -

        To enable ProGuard so that it runs as part of an Ant or Eclipse build, set the - proguard.config property in the <project_root>/project.properties - file. The path can be an absolute path or a path relative to the project's root.

        - -

        If you left the proguard.cfg file in its default location (the project's root - directory), you can specify its location like this:

        - -
        -proguard.config=proguard.cfg
        -
        - -

        -You can also move the the file to anywhere you want, and specify the absolute path to it: -

        - -
        -proguard.config=/path/to/proguard.cfg
        -
        - -

        When you build your application in release mode, either by running ant release or - by using the Export Wizard in Eclipse, the build system automatically checks to see if - the proguard.config property is set. If it is, ProGuard automatically processes - the application's bytecode before packaging everything into an .apk file. Building in debug mode - does not invoke ProGuard, because it makes debugging more cumbersome.

        - -

        ProGuard outputs the following files after it runs:

        - -
        -
        dump.txt
        -
        Describes the internal structure of all the class files in the .apk file
        - -
        mapping.txt
        -
        Lists the mapping between the original and obfuscated class, method, and field names. - This file is important when you receive a bug report from a release build, because it - translates the obfuscated stack trace back to the original class, method, and member names. - See Decoding Obfuscated Stack Traces for more information.
        - -
        seeds.txt
        -
        Lists the classes and members that are not obfuscated
        - -
        usage.txt
        -
        Lists the code that was stripped from the .apk
        - - -

        These files are located in the following directories:

        - -
          -
        • <project_root>/bin/proguard if you are using Ant.
        • - -
        • <project_root>/proguard if you are using Eclipse.
        • -
        - - -

        Caution: Every time you run a build in release mode, these files are - overwritten with the latest files generated by ProGuard. Save a copy of them each time you release your - application in order to de-obfuscate bug reports from your release builds. - For more information on why saving these files is important, see - Debugging considerations for published applications. -

        -

        Configuring ProGuard

        For some situations, the default configurations in the ProGuard configuration file will @@ -246,9 +177,7 @@ proguard.config=/path/to/proguard.cfg By retaining a copy of the mapping.txt file for each release build, you ensure that you can debug a problem if a user encounters a bug and submits an obfuscated stack trace. A project's mapping.txt file is overwritten every time you do a release build, so you must be - careful about saving the versions that you need. For Eclipse, this file is stored in - <project_root>/bin/proguard/. For Android Studio, this file is stored in - the app build/outs/ folder.

        + careful about saving the versions that you need. The file is stored in the app build/outs/ folder.

        For example, say you publish an application and continue developing new features of the application for a new version. You then do a release build using ProGuard soon after. The diff --git a/docs/html/tools/help/project-mgmt.jd b/docs/html/tools/help/project-mgmt.jd new file mode 100644 index 0000000000000000000000000000000000000000..693f0de1dc9de3d9ddff4dd54cbea7cd655adb18 --- /dev/null +++ b/docs/html/tools/help/project-mgmt.jd @@ -0,0 +1,174 @@ +page.title=Project Structure Management +parent.title=Tools +parent.link=index.html +page.tags="android studio,project structure,target sdk,minimum sdk" +@jd:body + +

        +
        +

        In this document

        +
          +
        1. Developer Services
        2. +
        3. Modules
        4. +
        + +
        +
        + +

        + You can use the Android Studio File > Project Structure dialog box to + change configuration settings for your Android Studio project. This dialog + box is useful if you need to change some of the settings you chose when you + created the project. +

        + +

        + The Project Structure dialog box contains the following sections: +

        + +
          +
        • + SDK Location: Sets the location of the JDK, Android SDK, + and Android NDK that the project uses. +
        • + +
        • + Project: Sets version information for Gradle and for + the Android plugin + for Gradle. +
        • + +
        • + Developer Services: Contains settings for Android Studio + add-in components from Google or third parties. +
        • + +
        • + Modules: Used to set or change various module-specific + build settings, including the target and minimum SDK, the app signature, + and library dependencies. Most importantly, this is where you change + the settings for your project's app module. +
        • +
        + +

        + The following two sections provide information on the Developer Services and Modules + sections. +

        + +

        + Developer Services +

        + +

        + The Developer Services section of the Project Structure + dialog box contains configuration pages + for several services that you can be use with your app. This section + contains the following pages: +

        + +
          +
        • + AdMob: Allows you to turn on Google's AdMob + component, which helps you understand your users and show them tailored + advertisements. +
        • + +
        • + Analytics: Allows you to turn on Google + Analytics, which helps you measure user interactions with your app + across various devices and environments. +
        • + +
        • + Authentication: Allows users to use Google Sign-In to sign in to your app with their Google + accounts. +
        • + +
        • + Cloud: Allows you to turn on Firebase cloud-based + services for your app. +
        • + +
        • + Notifications: Allows you to use Google Cloud Messaging to communicate between your app + and your server. +
        • +
        + +

        + Turning on any of these services may cause Android Studio to add necessary + dependencies and permissions to your app. Each configuration page lists these + and other actions that Android Studio takes if you enable the associated service. +

        + +

        + Modules +

        + +

        + The Modules settings section lets you change configuration + options for each of your project's modules. This section contains one page + for each module in your app. In many cases, the project has just a single + module, named app. However, if your project targets multiple form + factors, it might have several modules. For example, if your project contains + both a tablet app and a wearable app, it might have two modules, named + mobile and wear. For more information about project + modules, see Android Application + Modules. +

        + +

        + Each module's settings page is divided into the following panes: +

        + +
          +
        • + Properties: Specifies the versions of the SDK and build + tools to use to compile the module. +
        • + +
        • + Signing: Specifies the certificate to use to + sign your + APK. +
        • + +
        • + Flavors: Lets you create multiple build flavors, where + each flavor specifies a set of configuration settings, such as the + module's minimum and target SDK version, and the + version code and + version name. For example, you might define one flavor that has a + minimum SDK of 15 and a target SDK of 21, and another flavor that has a + minimum SDK of 19 and a target SDK of 23. +
        • + +
        • + Build Types: Lets you create and modify build + configurations, as described in Configuring Gradle + Builds. By default, every module has debug and + release build types, but you can define more as needed. +
        • + +
        • + Dependencies: Lists the library, file, and module + dependencies for this module. You can add, modify, and delete dependencies + from this pane. For more information about module dependencies, see + Declare + dependencies in Configuring Gradle + Builds. +
        • +
        diff --git a/docs/html/tools/help/sdk-manager.jd b/docs/html/tools/help/sdk-manager.jd old mode 100644 new mode 100755 index cc95edf6bf6a3be2e9e03355f535ef4e7993189a..aaa1930953830087d24ae37a652456a6288f2ec3 --- a/docs/html/tools/help/sdk-manager.jd +++ b/docs/html/tools/help/sdk-manager.jd @@ -34,7 +34,7 @@ SDK checking:

        Tip: The standalone SDK Manager is still available from the -command line, but we recommend it only for use with Eclipse ADT and standalone SDK installations.

        +command line, but we recommend it only for use with standalone SDK installations.

        By default, the SDK Manager installs the latest packages and tools. Click the checkbox next to each additional SDK platform and tool that you want to install. Clear the @@ -95,19 +95,27 @@ Android Virtual Devices (AVDs) in the AVD Manager.

        Android Support Library
        -
        Recommended. Includes a static library that allows you to use some of the latest -Android APIs (such as fragments, -plus others not included in the framework at all) on devices running -a platform version as old as Android 1.6. All of the activity templates available when creating -a new project with the ADT Plugin -require this. For more information, read Support Library.
        +
        Recommended. Includes a static library that provides an extended set of + APIs that are compatible with most versions of Android. It's required for + products such as Android Wear, Android TV, and Google Cast. For more + information, read Support + Library. +
        Android Support Repository
        Recommended. Includes local Maven repository for Support libraries.
        Google Play services
        -
        Recommended. Includes Google Play services client library and sample code.
        +
        Recommended. Includes the Google Play services client library, which + provides a variety of features and services for your + apps, such as Google + sign-in,Google + Maps, Games + achievements and leaderboards, and much more.
        Google Repository
        Recommended. Includes local Maven repository for Google libraries.
        diff --git a/docs/html/tools/help/shell.jd b/docs/html/tools/help/shell.jd index 41bef2223aa8fc2c03c423da994a053314b6fa25..fabcf4b07bd20773fb0da6e79c893f6793a5ce64 100644 --- a/docs/html/tools/help/shell.jd +++ b/docs/html/tools/help/shell.jd @@ -570,6 +570,7 @@ install [options] <PATH>
      • {@code -s}: Install package on the shared mass storage (such as sdcard).
      • {@code -f}: Install package on the internal system memory.
      • {@code -d}: Allow version code downgrade. +
      • {@code -g}: Grant all permissions listed in the app manifest. @@ -622,21 +623,23 @@ disable-user [options] <PACKAGE_OR_COMPONENT>
      • - - @@ -865,7 +868,8 @@ $ adb pull /sdcard/demo.mp4 +(DDMS) tool offers an integrated debug environment that you may find easier to use. + @@ -895,4 +899,4 @@ $ adb pull /sdcard/demo.mp4 -
        Option-b <buffer> Loads an alternate log buffer for viewing, such as events or - radio. The main buffer is used by default. See radio. The main buffer is used by default. See Viewing Alternative Log Buffers.
        -grant <PACKAGE_PERMISSION> +grant <PACKAGE_NAME> <PERMISSION> Grant permissions - to applications. Only optional permissions the application has - declared can be granted. +Grant a permission to an app. On devices running Android 6.0 (API level 23) +and higher, may be any permission declared in the app manifest. On devices +running Android 5.1 (API level 22) and lower, must be an optional permission defined by the +app.
        -revoke <PACKAGE_PERMISSION> +revoke <PACKAGE_NAME> <PERMISSION> Revoke permissions - to applications. Only optional permissions the application has - declared can be revoked. +Revoke a permission from an app. On devices running Android 6.0 (API level +23) and higher, may be any permission declared in the app manifest. On devices +running Android 5.1 (API level 22) and lower, must be an optional permission defined by the +app.
        dumpsys Dumps system data to the screen. The Dalvik Debug Monitor Server -(DDMS) tool offers integrated debug environment that you may find easier to use.
         
        \ No newline at end of file + diff --git a/docs/html/tools/help/systrace.jd b/docs/html/tools/help/systrace.jd old mode 100644 new mode 100755 index 2a8e86f47264d65b831e4ef3eef1e26d84ea8f75..236d282c5ae293cf938f8f0ca3b1a36891525264 --- a/docs/html/tools/help/systrace.jd +++ b/docs/html/tools/help/systrace.jd @@ -30,36 +30,15 @@ of these methods.

        User Interface

        -

        The Systrace tool can be run from the -Android Developer Tools (ADT) in Eclipse, -Android Studio, +

        The Systrace tool can be run from +Android Studio or the Android Device Monitor.

        To run the Systrace capture user interface:

        - Using Eclipse

        - -
        -
          -
        1. In Eclipse, open an Android application project.
        2. -
        3. Switch to the DDMS perspective, by selecting Window > Perspectives > - DDMS.
        4. -
        5. In the Devices tab, select the device on which to run a trace. If no - devices are listed, make sure your device is connected via USB cable and that debugging is - enabled on the device.
        6. -
        7. Click the Systrace icon at the top of the Devices panel to configure tracing.
        8. -
        9. Set the tracing options and click OK to start the trace.
        10. -
        -
        -
        - -
        -

        - Using Android Studio

        @@ -80,7 +59,7 @@ or the Android Device Monitor.

        - Using Device Monitor

        diff --git a/docs/html/tools/help/theme-editor.jd b/docs/html/tools/help/theme-editor.jd new file mode 100644 index 0000000000000000000000000000000000000000..19f765ffc754995fdaf7ff15f5b0acfdfcc1a55a --- /dev/null +++ b/docs/html/tools/help/theme-editor.jd @@ -0,0 +1,166 @@ +page.title=Theme Editor +parent.title=Tools +parent.link=index.html +page.tags=theme +@jd:body + +
        +
        +

        In this document

        +
          +
        1. Theme Editor Basics
        2. +
        3. Themes and Colors
        4. +
        5. Device-Specific Configurations
        6. +
        + + +

        Dependencies and Prerequisites

        +
          +
        • Android Studio 1.4.1 or higher
        • +
        + +
        +
        + +

        +The Theme Editor is a visual assistant that helps you: +

        +
      • Create and modify + +themes for your app.
      • +
      • Adjust themes for different resource classifiers.
      • +
      • Visualize the effect of color changes on common UI elements.
      • + +

        +This page introduces the fundamental tasks that you can perform with the Theme +Editor, and explains how to do so. +

        + + +

        Theme Editor Basics

        + +

        +This section describes how to access the Theme Editor, and how it is laid out. +

        + +

        Accessing the Theme Editor

        + +

        There are two ways to open the Theme Editor:

        + +
      • From an open {@code styles.xml} file, click Open editor near the top-right +of the file window.
      • + +
      • From the Tools menu, select Android > +Theme Editor.

        + +

        Navigating the Theme Editor

        +

        +The Theme Editor's main screen is divided into two sections. The left side of the editor +shows what specific UI elements, such as the app bar or a raised button, look like +when you apply the current theme to them. The right side of the editor displays +the settings for the color resources, such as Theme parent and +colorPrimary, that comprise the current theme. You can modify design +themes by changing these resource settings. +

        + +

        Themes and Colors

        + +

        +The Theme Editor allows you to create new themes, modify existing ones, and manage the +colors that make up the themes. +

        + +

        Creating New Themes

        + +

        +To create a theme, follow these steps: +

        +
          +
        1. Open the Theme dropdown menu near the top of the right +side of the Theme Editor.
        2. +
        3. Select Create New Theme. The New Theme dialog appears.
        4. +
        5. Enter a name for the new theme.
        6. +
        7. In the Parent theme name: field, select the parent from which the theme + inherits initial resources.
        8. +
        + +

        Renaming Themes

        + +

        +To rename a theme, perform the following steps: +

        +
          +
        1. Open the Theme dropdown menu near the top of the right +side of the Theme Editor.
        2. +
        3. Select Rename AppTheme. The Rename dialog appears.
        4. +
        5. Enter a new name for the theme.
        6. +
        7. (optional) To see how the changes will look, click Preview.
        8. +
        9. To apply the changes, click Refactor.
        10. +
        + +

        Changing Color Resources

        + +

        To change an existing color resource, such as colorPrimary, +follow these steps: +

        +
          +
        1. Click the colored square next to the name of the resource you want to change. +The Resources dialog appears, containing a color picker, material-color +palette, and other settings and information.
        2. +
        3. Change the color, opacity, and name of a theme's resources as desired.

          +
        4. To ensure that your theme uses a color from the material +palette, click CLOSEST MATERIAL COLOR, located next to Custom +color. Android Studio changes the color you picked to the material color most like it, +and replaces Custom color with the name of the color from the material palette.
        5. +
        + +

        You can also directly select colors from the material palette, which the editor displays +as two rows of colored squares beneath the color picker.

        + +

        Viewing State Lists and Colors

        + +

        +The Theme Editor allows you to preview + +colors associated with different states. To do so, open the Resource dialog, and click +the State List tab that appears at the top of the dialog.

        + +

        +To more fully control the states themselves, you can directly view and edit their +properties in the XML file, +such as {@code colors.xml}, that defines them. For more information, see the +documentation for the {@link android.content.res.ColorStateList} class. +

        + +

        Device-Specific Configurations

        + +

        +You can choose + +device-specific configurations for your app to support. Perform +the following steps to do so: +

        + +
          +
        1. Click the triangle next to Location, near the bottom of the +Resources dialog. The Location section expands, revealing +the name of the XML file containing the +resource, as well as a list of configuration-specific directories in which to +place that file.
        2. +
        3. If necessary, change the XML file name.
        4. +
        5. Check the boxes next to the directories corresponding to the +device-specific configurations you wish to support. Any configuration +for which you do not specify a directory defaults to using the +{@code values} directory.
        6. +
        + +

        For more information about the relationship +between directory names and configurations, see + +Supporting Multiple Screens. For more information about +supported directory names, see + +Providing Resources. +

        + + diff --git a/docs/html/tools/help/translations-editor.jd b/docs/html/tools/help/translations-editor.jd new file mode 100644 index 0000000000000000000000000000000000000000..ab557e1382704d20c8f0f1ed47b1aa5d5dffc2c1 --- /dev/null +++ b/docs/html/tools/help/translations-editor.jd @@ -0,0 +1,133 @@ +page.title=Translations Editor +parent.title=Tools +parent.link=index.html +page.tags=translations +@jd:body + + + +

        +If your application supports multiple languages, you need to properly manage your +translated string resources. Android Studio provides the Translations Editor to make viewing and +managing your translated resources easier. +

        + +

        About the Translations Editor

        + +

        +Translated resources are stored in multiple XML files in multiple directories in your project. +Manually finding and editing resource files across many translations can be difficult. Because of +this, your application might have missing translations that go unnoticed until after your +application has been built and distributed to users. +

        + +

        +The Translations Editor lets you view and update all your string resources in one convenient +place. The editor gives you a single view of all of your translated resources, making it easy to +change or add translations, and even find missing translations. +

        + + +

        Figure 1. Manage locales and strings in the +Translations Editor.

        + +

        Running the Translations Editor

        + +

        +Follow these steps to start the Translations Editor: +

        + +
          +
        1. In Android Studio, open an Android app project.
        2. +
        3. In the Project window, select the + Android view.
        4. +
        5. Open the res folder, and then open the values folder.
        6. +
        7. If a strings.xml folder is present, open this folder.
        8. +
        9. Right-click the strings.xml file (not folder) and select Open + Translations Editor.
        10. +
        + +

        +You can also access the Translations Editor by opening a strings.xml file for editing +and clicking the Open editor link, or clicking the globe icon + in the Design layout view and +choosing Edit Translations. +

        + +

        Managing String Resources

        + +

        +The Translations Editor provides a view of all your string resources and current locale +translations. The name of each resource is listed in the Key column, along with a default value +for the key, a checkbox to mark the key as untranslatable, and values for each locale translation. +

        + +

        +Edit translations by double-clicking on the translation and editing the value directly +in the list view, or selecting the translation and editing the Translation field +value at the bottom of the editor. The default value can be edited by double-clicking on +the default value or selecting the default value and updating the Default Value +field.

        + +

        +If a resource is missing a translation and isn't marked untranslatable, the key name is +displayed in red. If you hover over a red resource, Android Studio displays details about the +missing translations. +

        + +

        Adding a new string resource

        + +

        +Add a new resource by clicking the add icon . Android Studio prompts you for a key name, +default value, and resource folder that contains the strings.xml file where the new +resource is added. +

        + +

        Adding additional locales

        + +

        +Add additional locales by pressing the globe icon + and selecting the locale you want +to add. The Translations Editor adds the new locale column and creates the translation strings XML +file in the appropriate project directory. +

        + +

        +The Translations Editor has +BCP 47 support, and +combines language and region codes into a single selection for targeted localizations. +

        + +

        Ordering Translation Services

        + +

        +Clicking the Order a translation link opens a page in your browser where you +can upload string resource XML files and order translation services. App translation services are +available from Google Play and other vendors. For more information about Google Play App +Translation Services, see the +Google Play App Translation Service announcement. +

        + diff --git a/docs/html/tools/help/uiautomator/Configurator.jd b/docs/html/tools/help/uiautomator/Configurator.jd index c898772216d6507f6f8fbd3601f09f5a336e35bb..b6e15f65eb1541e20879c154098791ccb2038c5c 100644 --- a/docs/html/tools/help/uiautomator/Configurator.jd +++ b/docs/html/tools/help/uiautomator/Configurator.jd @@ -281,7 +281,7 @@ this will impact other tests cases.

        From class diff --git a/docs/html/tools/help/uiautomator/UiAutomatorTestCase.jd b/docs/html/tools/help/uiautomator/UiAutomatorTestCase.jd index 0d8d1bc0354d91e0119971da7a23eac66f0d7b4c..bd108ab4f0b6e067222a69e0324a5ebf7dc64638 100644 --- a/docs/html/tools/help/uiautomator/UiAutomatorTestCase.jd +++ b/docs/html/tools/help/uiautomator/UiAutomatorTestCase.jd @@ -166,7 +166,7 @@ parent.link=index.html From class @@ -304,7 +304,7 @@ From class From class @@ -938,7 +938,7 @@ From class From class @@ -1108,7 +1108,7 @@ From class From interface diff --git a/docs/html/tools/help/uiautomator/UiCollection.jd b/docs/html/tools/help/uiautomator/UiCollection.jd index 3fc32b73c68c5192246430dc771d9489b57ff029..78c5254a80db407640c009076f85f96c96f4f37a 100644 --- a/docs/html/tools/help/uiautomator/UiCollection.jd +++ b/docs/html/tools/help/uiautomator/UiCollection.jd @@ -161,7 +161,7 @@ parent.link=index.html From class @@ -826,7 +826,7 @@ From class From class diff --git a/docs/html/tools/help/uiautomator/UiDevice.jd b/docs/html/tools/help/uiautomator/UiDevice.jd index 574245efe10739258deda6af2d45c89b4ddd9e73..1203841ffda55207ab8ed5abe98b22c5da00ee55 100644 --- a/docs/html/tools/help/uiautomator/UiDevice.jd +++ b/docs/html/tools/help/uiautomator/UiDevice.jd @@ -1015,7 +1015,7 @@ the d-pad or pressing the Home and Menu buttons.

        From class diff --git a/docs/html/tools/help/uiautomator/UiObject.jd b/docs/html/tools/help/uiautomator/UiObject.jd index 45007e0a17992c2d6ec6c2e38965faff930cc486..4ce257bd1bef1a375363a69a5c5f81b2d685c032 100644 --- a/docs/html/tools/help/uiautomator/UiObject.jd +++ b/docs/html/tools/help/uiautomator/UiObject.jd @@ -940,7 +940,7 @@ different views that match the selector criteria.

        From class diff --git a/docs/html/tools/help/uiautomator/UiObjectNotFoundException.jd b/docs/html/tools/help/uiautomator/UiObjectNotFoundException.jd index b41cfe5590d7d2e3ae4a9aad2b2c16998493e5a0..c34fdfdbd5f750dc289752ac8046f17e6a29443d 100644 --- a/docs/html/tools/help/uiautomator/UiObjectNotFoundException.jd +++ b/docs/html/tools/help/uiautomator/UiObjectNotFoundException.jd @@ -111,7 +111,7 @@ parent.link=index.html From class @@ -313,7 +313,7 @@ From class From class diff --git a/docs/html/tools/help/uiautomator/UiScrollable.jd b/docs/html/tools/help/uiautomator/UiScrollable.jd index 7405f3b6d8e43b57671e1dd0fe1c798fff85e3f7..8f6b1dd54487f40792441089afecadee3cc61110 100644 --- a/docs/html/tools/help/uiautomator/UiScrollable.jd +++ b/docs/html/tools/help/uiautomator/UiScrollable.jd @@ -38,7 +38,7 @@ be used with horizontally or vertically scrollable controls.

        From class com.android.uiautomator.core.UiObject
        @@ -696,7 +696,7 @@ be used with horizontally or vertically scrollable controls.

        From class @@ -798,7 +798,7 @@ From class From class @@ -1606,7 +1606,7 @@ From class From class diff --git a/docs/html/tools/help/uiautomator/UiSelector.jd b/docs/html/tools/help/uiautomator/UiSelector.jd index 70845402bb6d8cdb622af560ff8aa62f8e652a20..1f045fd88249a16bf7b40f2a8cd46b23dce1d375 100644 --- a/docs/html/tools/help/uiautomator/UiSelector.jd +++ b/docs/html/tools/help/uiautomator/UiSelector.jd @@ -650,7 +650,7 @@ hierarchy.

        From class diff --git a/docs/html/tools/help/vector-asset-studio.jd b/docs/html/tools/help/vector-asset-studio.jd new file mode 100644 index 0000000000000000000000000000000000000000..491a699265c7a444b8787af941899d2e57b743d3 --- /dev/null +++ b/docs/html/tools/help/vector-asset-studio.jd @@ -0,0 +1,506 @@ +page.title=Vector Asset Studio +parent.title=Tools +parent.link=index.html +page.tags=vector +@jd:body + + + +

        Vector Asset Studio helps you add material icons +and import Scalable Vector Graphic (SVG) files into your app project as a drawable resource. +Compared to raster images, vector drawables can reduce the size of your app and be resized without +loss of image quality. They help you to more easily support different Android devices with varying +screen sizes and resolutions because you can display one vector drawable on all of them.

        + +

        About Vector Asset Studio

        + +

        Vector Asset Studio adds a vector graphic to the project as an XML file that describes the image. +Maintaining one XML file can be easier than updating multiple raster graphics at various resolutions.

        + +

        Android 4.4 (API level 20) and lower doesn't support vector drawables. If your minimum API level +is set at one of these API levels, Vector Asset Studio also directs Gradle to generate raster images +of the vector drawable for backward-compatibility. You can refer to vector assets as +{@link android.graphics.drawable.Drawable} in Java code or @drawable in XML code; when +your app runs, the corresponding vector or raster image displays automatically depending on the API +level.

        + + +

        Supported vector graphic types

        + +

        The Google material design specification provides material icons +that you can use in your Android apps. Vector +Asset Studio helps you choose, import, and size material icons, as well as define opacity and the +Right-to-Left (RTL) mirroring setting.

        + +

        Vector Asset Studio also helps you to import your own SVG files. SVG is an XML-based open +standard of the World Wide Web Consortium (W3C). Vector Asset Studio supports the essential +standard, but not all features. When you specify an SVG file, Vector Asset Studio gives immediate +feedback about whether the graphics code is supported or not. If the SVG code is supported, it +converts the file into an XML file containing {@link +android.graphics.drawable.VectorDrawable} code.

        + +

        Considerations for SVG files

        + +

        A vector drawable is appropriate for simple icons. The +material icons provide good +examples of the types +of images that work well as vector drawables in an app. In contrast, many app launch icons do have +many details, so they work better as raster images.

        + +

        The initial loading of a vector graphic can cost more CPU cycles than the corresponding raster +image. Afterward, memory use and performance are similar between the two. We recommend that you +limit a vector image to a maximum of 200 x 200 dp; otherwise, it can take too long to draw.

        + +

        Although vector drawables do support one or more colors, in many cases it makes sense to color +icons black (android:fillColor="#FF000000"). Using this approach, you can add a +tint to the vector drawable +that you placed in a layout, and the icon color changes to the tint color. If the icon color +isn't black, the icon color might instead blend with the tint color.

        + +

        Vector drawable support at different API levels

        + +

        Android 5.0 (API level 21) and higher provides vector drawable support. If your app has a +minimum API level that is lower, Vector Asset Studio adds the vector drawable file to your +project; also, at build time, Gradle creates Portable Network Graphic (PNG) raster images at various +resolutions. Gradle generates the PNG densities specified by the Domain Specific Language (DSL) +generatedDensities property +in a build.gradle file. To generate PNGs, the build system requires Android +Plugin for Gradle 1.5.0 or higher.

        + +

        For Android 5.0 (API level 21) and higher, Vector Asset Studio supports all of the {@link +android.graphics.drawable.VectorDrawable} elements. For backward compatibility with Android 4.4 (API +level 20) and lower, Vector Asset Studio supports the following XML elements:

        + +
        +
        +
        + +

        <vector>

        +
          +
        • android:width
        • +
        • android:height
        • +
        • android:viewportWidth
        • +
        • android:viewportHeight
        • +
        • android:alpha
        • +
        + +
        + +
        + +

        <group>

        +
          +
        • android:rotation
        • +
        • android:pivotX
        • +
        • android:pivotY
        • +
        • android:scaleX
        • +
        • android:scaleY
        • +
        • android:translateX
        • +
        • android:translateY
        • +
        + +
        + + +
        + +

        <path>

        +
          +
        • android:pathData
        • +
        • android:fillColor
        • +
        • android:strokeColor
        • +
        • android:strokeWidth
        • +
        • android:strokeAlpha
        • +
        • android:fillAlpha
        • +
        • android:strokeLineCap
        • +
        • android:strokeLineJoin
        • +
        • android:strokeMiterLimit
        • +
        + +
        + +
        +
        + +

        Only Android 5.0 (API level 21) and higher supports dynamic attributes, for example, android:fillColor="?android:attr/colorControlNormal".

        + +

        You can change the XML code that Vector Asset Studio generates, although it’s not a best practice. +Changing the values in the code should not cause any issues, as long as they’re valid and static. If +you want to add XML elements, you need to make sure that they’re supported based on your minimum API +level.

        + +

        For Android 5.0 (API level 21) and higher, you can use the {@link +android.graphics.drawable.AnimatedVectorDrawable} class to animate the properties of {@link +android.graphics.drawable.VectorDrawable}. For more information, see +Animating Vector Drawables.

        + +

        Running Vector Asset Studio

        + +

        Follow these steps to start Vector Asset Studio:

        + +
          +
        1. In Android Studio, open an Android app project.
        2. +
        3. In the Project window, select the + Android view.
        4. +
        5. Right-click the res folder and select New > + Vector Asset.
        6. +

          Some other project views and folders have this menu item as well.

          +

          Vector Asset Studio appears.

          + +
        7. If a Need newer Android plugin for Gradle dialog appears instead, correct + your Gradle version as follows:
        8. +
            +
          1. Select File > Project Structure.
          2. +
          3. In the Project Structure dialog, select Project.
          4. +
          5. In the Android Plugin Version field, change the Android Plugin for Gradle + version to 1.5.0 or higher, and click OK.
          6. +

            Gradle syncs the project.

            +
          7. In the Android view + of the Project window, right-click the res folder and select + New > Vector Asset.
          8. +

            Vector Asset Studio appears.

            +
          +
        9. Continue with Importing a Vector Graphic.
        10. +
        + +

        Importing a Vector Graphic

        + +

        Vector Asset Studio helps you to import a vector graphics file into your app project. Follow one + of the following procedures:

        + + +

        Adding a material icon

        + +

        After you open Vector Asset Studio, you can add a material icon as follows:

        + +
          +
        1. In Vector Asset Studio, select Material Icon.
        2. +
        3. Click Choose.
        4. +
        5. Select a material icon and click OK.
        6. +

          The icon appears in the Vector Drawable Preview.

          +
        7. Optionally change the resource name, size, opacity, and Right-To-Left (RTL) mirroring setting: +
            +
          • Resource name - Type a new name if you don’t want to use the default name. + Vector Asset Studio automatically creates a unique name (adds a number to the end of the name) + if that resource name already exists in the project. The name can contain lowercase + characters, underscores, and digits only.
          • +
          • Override default size from material design - Select this option if you + want to adjust the size of the image. When you type a new size, the change appears in the + preview area.
          • +

            The default is 24 x 24 dp, which is defined in the + material design + specification. Deselect the checkbox to return to the default.

            +
          • Opacity - Use the slider to adjust the opacity of the image. The change + appears in the preview area.
          • +
          • Enable auto mirroring for RTL layout - Select this option if you want a + mirror image to display when the layout is right to left, instead of left to right. For + example, some languages are read right to left; if you have an arrow icon, you might want to + display a mirror image of it in this case. Note that if you’re working with an older project, + you might also + need to add android:supportsRtl="true" to your app manifest. Auto-mirroring is + supported on Android 5.0 (API level 21) and higher only.
          • +
          +
        8. Click Next.
        9. +
        10. Optionally change the module and resource directory:
        11. +
            +
          • Target Module - Select a module in the project where you want to add the + resource. For more information, see + Creating an Android Module.
          • +
          • Res Directory - Select the resource source set where you want to add the + vector asset: src/main/res, src/debug/res, src/release/res, + or a user-defined source set. The main source set applies to all build variants, including + debug and release. The debug and release source sets override the main source set and apply + to one version of a build. The debug source set is for debugging only. To define a new source + set, select File > Project Structure > app > + Build Types. For example, you could define a beta source set and create a + version of an icon that includes the text "BETA” in the bottom right corner. For more information, see + Working with Build Variants.
          • +
          +

          The Output Directories area displays the vector image and the directory + where it will appear.

          +
        12. Click Finish.
        13. +

          Vector Asset Studio adds an XML file defining the vector drawable to the project in the + app/src/main/res/drawable/ folder. From the + Android view of the Project + window, you can view the generated vector XML file in the drawable folder.

          +
        14. Build the project.
        15. +

          If the minimum API level is Android 4.4 (API level 20) and lower, Vector Asset Studio generates + PNG files. From the Project view + of the Project window, you can view the generated PNG and XML files in the + app/build/generated/res/pngs/debug/ folder.

          +

          You should not edit these generated raster files, but instead work with the vector XML file. The + build system regenerates the raster files automatically when needed so you don’t need to maintain + them.

          +
        + +

        Importing a Scalable Vector Graphic (SVG)

        + +

        After you open Vector Asset Studio, you can import an SVG file as follows:

        + +
          +
        1. In Vector Asset Studio, select Local SVG file.
        2. +

          The file must be on a local drive. If it’s located on the network, for example, you need to + download it to a local drive first.

          +
        3. Specify an Image file by clicking .
        4. +

          The image appears in the Vector Drawable Preview.

          +

          However, if the SVG file contains unsupported features, an error appears at the bottom left of + Vector Asset Studio, as shown in the following figure.

          + +

          In this case, you can’t use Vector Asset Studio to add the graphics file. Click + More to view the errors. For a list of supported elements, see + Vector Drawable Support at Different API Levels.

          +
        5. Optionally change the resource name, size, opacity, and Right-To-Left (RTL) mirroring setting:
        6. +
            +
          • Resource name - Type a new name if you don’t want to use the default + name. Vector Asset Studio automatically creates a unique name (adds a number to the end of the + name) if that resource name already exists in the project. The name can contain lowercase + characters, underscores, and digits only.
          • +
          • Override default size from material design - Select this option if you + want to adjust the size of the image. After you select it, the size changes to the size of the + image itself. Whenever you change the size, the change appears in the preview area.
          • +

            The default is 24 x 24 dp, which is defined in the + material design + specification. Deselect the checkbox to return to the default.

            +
          • Opacity - Use the slider to adjust the opacity of the image. The change + appears in the preview area.
          • +
          • Enable auto mirroring for RTL layout - Select this option if you want a + mirror image to display when the layout is right to left, instead of left to right. For + example, some languages are read right to left; if you have an arrow icon, you might want to + display a mirror image of it in this case. Note that if you’re working with an older project, + you might + need to add android:supportsRtl="true" to your app manifest. Auto-mirroring is + supported on Android 5.0 (API level 21) and higher only.
          • +
          +
        7. Click Next.
        8. +
        9. Optionally change the module and resource directory:
        10. +
            +
          • Target Module - Select a module in the project where you want to add the + resource. For more information, see + Creating an Android Module.
          • +
          • Res Directory - Select the resource source set where you want to add the + vector asset: src/main/res, src/debug/res, src/release/res, + or a user-defined source set. The main source set applies to all build variants, including + debug and release. The debug and release source sets override the main source set and apply + to one version of a build. The debug source set is for debugging only. To define a new source + set, select File > Project Structure > app > + Build Types. For example, you could define a beta source set and create a + version of an icon that includes the text "BETA” in the bottom right corner. For more information, see + Working with Build Variants.
          • +
          +

          The Output Directories area displays the vector image and the directory + where it will appear.

          +
        11. Click Finish.
        12. +

          Vector Asset Studio adds an XML file defining the vector drawable to the project in the + app/src/main/res/drawable/ folder. From the + Android view of the Project + window, you can view the generated vector XML file in the drawable folder.

          +
        13. Build the project.
        14. +

          If the minimum API level is Android 4.4 (API level 20) and lower, Vector Asset Studio generates + PNG files. From the Project view + of the Project window, you can view the generated PNG and XML files in the + app/build/generated/res/pngs/debug/ folder.

          +

          You should not edit these generated raster files, but instead work with the vector XML file. The + build system regenerates the raster files automatically when needed so you don’t need to maintain + them.

          +
        + +

        Adding a Vector Drawable to a Layout

        + +

        In a layout file, you can set any icon-related widget, such as {@link android.widget.ImageButton}, +{@link android.widget.ImageView}, and so on, to point to a vector asset. For example, the following +layout shows a vector asset displayed on a button:

        + + + +

        Follow these steps to display a vector asset on a widget, as shown in the figure:

        + +
          +
        1. Open a project and import a vector asset.
        2. +
        3. In the Android view of + the Project window, double-click a layout XML file, such as content_main.xml.
        4. +
        5. Click the Design tab to display the + Layout Editor.
        6. +
        7. Drag the {@link +android.widget.ImageButton} widget from the Palette window onto the Layout Editor.
        8. +
        9. In the Properties window, locate the src property of the + ImageButton instance and click .
        10. +
        11. In the Resources dialog, select the Project tab, navigate to the + Drawable folder, and select a vector asset. Click OK.
        12. +

          The vector asset appears on the ImageButton in the layout.

          +
        13. To change the color of the image to the accent color defined in the theme, locate the + tint property in the Properties window and click .
        14. +
        15. In the Resources dialog, select the Project tab, navigate to the + Color folder, and select colorAccent. Click OK.
        16. +

          The color of the image changes to the accent color in the layout.

          +
        + +

        The ImageButton code should be similar to the following:

        + +
        +<ImageButton
        +  android:id="@+id/imageButton"
        +  android:src="@drawable/ic_build_24dp"
        +  android:tint="@color/colorAccent"
        +  android:layout_width="wrap_content"
        +  android:layout_height="wrap_content"
        +  android:layout_below="@+id/textView2"
        +  android:layout_marginTop="168dp" />
        +
        + +

        Referring to a Vector Drawable in Code

        + +

        You can normally refer to a vector drawable resource in a generic way in your code, and when +your app runs, the corresponding vector or raster image displays automatically depending on the API +level:

        + +
          +
        • In most cases, you can refer to vector assets as @drawable in XML code or + {@link android.graphics.drawable.Drawable} in Java code.
        • +

          For example, the following layout XML code applies the image to a view:

          +
          +<ImageView
          +    android:layout_height="wrap_content"
          +    android:layout_width="wrap_content"
          +    android:src="@drawable/myimage" />
          +
          +

          The following Java code retrieves the image as a {@link android.graphics.drawable.Drawable}:

          +
          +Resources res = {@link android.content.Context#getResources()};
          +Drawable drawable = res.{@link android.content.res.Resources#getDrawable(int) getDrawable}(R.drawable.myimage);
          +
          +
        • Occasionally, you might need to typecast the drawable resource to its exact class, such as + when you need to use specific features of the {@link android.graphics.drawable.VectorDrawable} + class. To do so, you could use Java code such as the following:
        • +
          +if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
          +   VectorDrawable vectorDrawable =  (VectorDrawable) drawable;
          +} else {
          +   BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
          +}
          +
          +
        + + +

        Modifying XML Code Generated by Vector Asset Studio

        + +

        You can modify the vector asset XML code, but not the PNGs and corresponding XML code generated +at build time. However, we don't recommended it. Vector Asset Studio makes sure that the vector +drawable and the PNGs match, and that the manifest contains the proper code. If you add code that's +not supported on Android 4.4 (API level 20) and lower, your vector and PNG +images might differ. You also need to make sure that the manifest contains the code to support your +changes.

        + +

        Follow these steps to modify the vector XML file:

        + +
          +
        1. In the Project window, double-click the generated vector XML file in the + drawable folder.
        2. +

          The XML file appears in the editor and Preview windows.

          + + + +
        3. Edit the XML code based on what’s supported by the minimum API level:
        4. +
            +
          • Android 5.0 (API level 21) and higher - Vector Asset Studio supports all of the + {@link android.graphics.drawable.Drawable} and + {@link android.graphics.drawable.VectorDrawable} elements. You can add XML elements and + change values.
          • +
          • Android 4.4 (API level 20) and lower - Vector Asset Studio supports all of the + {@link android.graphics.drawable.Drawable} elements and a subset of the + {@link android.graphics.drawable.VectorDrawable} elements. See + Support at Different API Levels for a list. You can change values in + the generated code and add XML elements that are supported. Dynamic attributes aren't + supported.
          • +
          +

          For example, if you didn’t select the RTL option in Vector Asset Studio but realize you now + need it, you can add the autoMirrored + attribute later. To view the RTL version, select Preview Right-to-Left Layout in + the + menu of the Preview window. (Select None to remove the RTL preview.)

          + +

          Note: If you’re working with an older project, you might need to + add android:supportsRtl="true" to your app manifest. Also, because + autoMirrored is a dynamic attribute, it's supported on Android 5.0 (API level 21) and + higher only.

          +
        5. Build the project and check that the vector and raster images look the same.
        6. +

          Remember that the generated PNGs could display differently in the Preview window than + in the app due to different rendering engines and any changes made to the vector drawable before a + build. If you add code to the vector XML file created by Vector Asset Studio, any features + unsupported in Android 4.4 (API level 20) and lower don't appear in the generated PNG files. As a + result, when you add code, you should always check that the generated PNGs match the vector + drawable. To do so, you could double-click the PNG in the + Project view of the Project + window; the left margin of the code editor also displays the PNG image when your code refers to + the drawable.

          + +
        + + +

        Deleting a Vector Asset from a Project

        + +

        Follow these steps to remove a vector asset from a project:

        + +
          +
        1. In the Project window, delete the generated vector XML file by selecting the file and + pressing the Delete key (or select Edit > Delete).
        2. +

          The Safe Delete dialog appears.

          +
        3. Optionally select options to find where the file is used in the project, and click + OK.
        4. +

          Android Studio deletes the file from the project and the drive. However, if you chose to search + for places in the project where the file is used and some usages are found, you can view them and + decide whether to delete the file.

          +
        5. Select Build > Clean Project.
        6. +

          Any auto-generated PNG and XML files corresponding to the deleted vector + asset are removed from the project and the drive.

          +
        + +

        Delivering an App Containing Vector Drawables

        + +

        When your minimum API level includes Android 4.4 (API level 20) or lower, you have corresponding + vector and raster images in your project. In this case, you have two options for delivering your + APK files:

        + +
          +
        • Create one APK that includes both the vector images and the corresponding raster + representations. This solution is the simplest to implement.
        • +
        • Create separate APKs for different API levels. When you don’t include the corresponding + raster images in the APK for Android 5.0 (API level 21) and higher, the APK can be much smaller in + size. For more information, see Multiple APK Support.
        • +
        diff --git a/docs/html/tools/help/zipalign.jd b/docs/html/tools/help/zipalign.jd old mode 100644 new mode 100755 index a32a36585f9478ba8fbfed6385f0436e5c38579e..ed82b27b3d0731bf03d3b83826989b6797b2eade --- a/docs/html/tools/help/zipalign.jd +++ b/docs/html/tools/help/zipalign.jd @@ -16,12 +16,12 @@ when running the application.

        This tool should always be used to align your .apk file before distributing it to end-users. The Android build tools can handle -this for you. When using Eclipse with the ADT plugin, the Export Wizard -will automatically zipalign your .apk after it signs it with your private key. +this for you. Android Studio automatically aligns your .apk after it signs it with your +private key. The build scripts used -when compiling your application with Ant will also zipalign your .apk, +when compiling your application with Gradle also align your .apk, as long as you have provided the path to your keystore and the key alias in -your project {@code ant.properties} file, so that the build tools +your project {@code gradle.properties} file, so that the build tools can sign the package first.

        Caution: zipalign must only be performed diff --git a/docs/html/tools/performance/memory-monitor/index.jd b/docs/html/tools/performance/memory-monitor/index.jd index a083a14b05fabcdd779d9b74dc772f479a3b31b5..5b9d0c8e9f97345c86ea5b14e819cd14b914b376 100644 --- a/docs/html/tools/performance/memory-monitor/index.jd +++ b/docs/html/tools/performance/memory-monitor/index.jd @@ -78,7 +78,7 @@ page.article=true alt="" width="300px" />

        - Figure 1. Starting Memory Monitor. + Figure 1. Enable ADB Integration.

        @@ -87,24 +87,63 @@ page.article=true
      • Open your application in Android Studio, build the source, and run it on your device or emulator.
      • -
      • In Android Studio, choose Tools > Android > Memory Monitor. You - can also click the Android tab in the lower-left corner of the application - window to launch the Android runtime window. The CPU and Memory Monitor views appear.
      • - +
      • In Android Studio, choose Tools > Android > Enable ADB Integration. + +
    +
    + +
  • +
    +
      + +
      + +

      + Figure 2. The Android Monitor tab. +

      +
      + +
    1. Click the Android Monitor tab at the bottom of the runtime window.
    2. + +
    3. Click the Memory tab to show the Memory Monitor tool.
    4. +
  • +
  • +
    +
      + +
      + +

      + Figure 3. The Memory Monitor pane. +

      +
      + +
    1. Choose your device from the drop-down menu at the top left of the pane. +
    2. + +
    3. Choose the Activity to monitor from the drop-down menu at the top right of the pane. +
    4. + +
    +

  • -
      +

        - Figure 1. Allocated and free memory in Memory Monitor. + Figure 4. Allocated and free memory in Memory Monitor.

        @@ -130,14 +169,14 @@ page.article=true

      1. -
          +

            - Figure 2. Forcing a GC (Garbage Collection) event. + Figure 5. Forcing a GC (Garbage Collection) event.

            diff --git a/docs/html/tools/performance/profile-gpu-rendering/index.jd b/docs/html/tools/performance/profile-gpu-rendering/index.jd index 74862b59acc16b8ab6cd42230584cc38c96b9dda..5d22e8c25cdffe767876e48d278ad7c5a8c6d467 100644 --- a/docs/html/tools/performance/profile-gpu-rendering/index.jd +++ b/docs/html/tools/performance/profile-gpu-rendering/index.jd @@ -169,7 +169,7 @@ src="{@docRoot}images/tools/performance/profile-gpu-rendering/gettingstarted_ima screen asynchronously. In certain situations, the GPU can have too much work to do, and your CPU will have to wait before it can submit new commands. When this happens, you'll see spikes in the Process (orange bar) and Execute (red bar) - stages, and the sommand submission will block until more room is made on the + stages, and the command submission will block until more room is made on the GPU command queue.

            diff --git a/docs/html/tools/projects/index.jd b/docs/html/tools/projects/index.jd index 86654797aaa754850e386f747f7214c5864b7a81..344e844f100744837286f1151e865aa9c1eec553 100644 --- a/docs/html/tools/projects/index.jd +++ b/docs/html/tools/projects/index.jd @@ -100,7 +100,7 @@ project and override similar module file settings.

            build
            -
            This directory stories the build output for all project modules.
            +
            This directory stores the build output for all project modules.
            gradle
            @@ -176,8 +176,9 @@ project and override similar module file settings.

            src/
            Contains your stub Activity file, which is stored at - src/main/java//ActivityName>.java. All other source - code files (such as .java or .aidl files) go here as well.
            + src/main/java/<namespace.appname>/<ActivityName>.java. All + other source code files (such as .java or .aidl files) go here as + well.
            androidTest/
            @@ -185,7 +186,7 @@ project and override similar module file settings.

            Contains the instrumentation tests. For more information, see the Android Test documentation.
            -
            main/java/com.>project<.>app<
            +
            main/java/com.<project>.<app>
            Contains Java code source for the app activities.
            diff --git a/docs/html/tools/projects/projects-eclipse.jd b/docs/html/tools/projects/projects-eclipse.jd deleted file mode 100644 index af8501572f648794a7e542256b507e667097da63..0000000000000000000000000000000000000000 --- a/docs/html/tools/projects/projects-eclipse.jd +++ /dev/null @@ -1,273 +0,0 @@ -page.title=Managing Projects from Eclipse with ADT -parent.title=Managing Projects -parent.link=index.html -@jd:body - - - -

            Eclipse and the ADT plugin provide GUIs and wizards to create all three types of projects - (Android project, Library project, and Test project): - -

              -
            • An Android project contains all of the files and resources that are needed to build a project into - an .apk file for installation. You need to create an Android project for any application that you - want to eventually install on a device.
            • - -
            • You can also designate an Android project as a library project, which allows it to be shared - with other projects that depend on it. Once an Android project is designated as a library - project, it cannot be installed onto a device.
            • - -
            • Test projects extend JUnit test functionality to include Android specific functionality. For - more information on creating a test project, see Testing from Eclipse with ADT.
            • -
            - -

            Creating an Android Project

            - -

            The ADT plugin provides a New Project Wizard that you can use to quickly create a new - Android project (or a project from existing code). To create a new project:

            - -
              -
            1. Select File > New > Project.
            2. - -
            3. Select Android > Android Application Project, and click - Next.
            4. - -
            5. Enter the basic settings for the project: - -
                -
              • Enter an Application Name. This name is used as the title of your - application launcher icon when it is installed on a device.
              • - -
              • Enter a Project Name. This text is used as the name of the folder where - your project is created.
              • - -
              • Enter a Package Name. This class package namespace creates the initial - package structure for your applications code files and is added as the - {@code package} - attribute in your application's - Android manifest file. - This manifest value serves as the unique identifier for your application app when you - distribute it to users. The package name must follow the same rules as packages in the Java - programming language.
              • - -
              • Select a Minimum Required SDK. This setting indicates the lowest - version of the Android platform that your application supports. This value sets the - minSdkVersion attribute in the - <uses-sdk> - element of your manifest file.
              • - -
              • Select a Target SDK. This setting indicates the highest version of - Android with which you have tested with your application and sets the - {@code - targetSdkVersion} attribute in your application's' manifest file. - -

                Note: You can change the target SDK for your - project at any time: Right-click the project in the Package Explorer, select - Properties, select Android and then check the desired - Project Build Target.

                -
              • - -
              • Select a Compile With API version. This setting specifies what version - of the SDK to compile your project against. We strongly recommend using the most recent - version of the API.
              • - -
              • Select a Theme. This setting specifies which standard Android - visual style is applied to your - application.
              • - -
              • Click Next.
              • -
              -
            6. - -
            7. In the Configure Project page, select the desired settings and click - Next. Leave the Create activity option checked so you can - start your application with some essential components.
            8. - -
            9. In the Configure Launcher Icon page, create an icon and click - Next.
            10. - -
            11. In the Create Activity page, select activity template and click - Next. For more information about Android code templates, see - Using Code Templates. -
            12. - -
            13. Click Finish and the wizard creates a new project according to the options - you have chosen.
            14. -
            - -

            Tip: You can also start the New Project Wizard by clicking the - New icon in the toolbar.

            - - -

            Setting up a Library Project

            - -

            A library project is a standard Android project, so you can create a new one in the same way - as you would a new application project.

            - -

            To create a new library project:

            - -
              -
            1. Select File > New > Project.
            2. - -
            3. Select Android > Android Application Project, and click - Next.
            4. - -
            5. Enter the basic settings for the project, including Application Name, - Project Name, Package Name, and SDK settings.
            6. - -
            7. In the Configure Project page, select the Mark this project as a - library option to flag the project as a library.
            8. - -
            9. Set the other options as desired and click Next.
            10. - -
            11. Follow the instructions to complete the wizard and create a new library project.
            12. -
            - -

            You can also convert an existing application project into a library. To do so, simply open the - Properties for the project and select the is Library checkbox, as shown in - the figure below.

            - - -

            Figure 1. Marking a project as an Android library.

            - -

            To set the a project's properties to indicate that it is a library project:

            - -
              -
            1. In the Package Explorer, right-click the library project and select - Properties.
            2. - -
            3. In the Properties window, select the Android properties - group in the left pane and locate the Library properties in the right pane.
            4. - -
            5. Select the is Library check box and click Apply.
            6. - -
            7. Click OK to close the Properties window.
            8. -
            - -

            Once you create a library project or mark an existing project as a library, you can reference - the library project in other Android application projects. For more information, see the - Referencing a library project section. - - -

            Creating the manifest file

            - -

            A library project's manifest file must declare all of the shared components that it includes, - just as would a standard Android application. For more information, see the documentation for - AndroidManifest.xml.

            - -

            For example, the TicTacToeLib example library - project declares the activity GameActivity:

            -
            -<manifest>
            -  ...
            -  <application>
            -    ...
            -    <activity android:name="GameActivity" />
            -    ...
            -  </application>
            -</manifest>
            -
            - -

            Referencing a library project

            - -

            If you are developing an application and want to include the shared code or resources from a - library project, you can do so easily by adding a reference to the library project in the - application project's Properties.

            - -

            To add a reference to a library project, follow these steps:

            - -
              -
            1. Make sure that both the project library and the application project that depends on it are - in your workspace. If one of the projects is missing, import it into your workspace.
            2. - -
            3. In the Package Explorer, right-click the dependent project and select - Properties.
            4. - -
            5. In the Properties window, select the "Android" properties group at left - and locate the Library properties at right.
            6. - -
            7. Click Add to open the Project Selection dialog.
            8. - -
            9. From the list of available library projects, select a project and click - OK.
            10. - -
            11. When the dialog closes, click Apply in the Properties - window.
            12. - -
            13. Click OK to close the Properties window.
            14. -
            - -

            As soon as the Properties dialog closes, Eclipse rebuilds the project, including the contents - of the library project.

            - -

            Figure 2 shows the Properties dialog that lets you add library references and move - them up and down in priority.

            - -

            Figure 2. Adding a reference to a - library project in the properties of an application project.

            - -

            If you are adding references to multiple libraries, note that you can set their relative - priority (and merge order) by selecting a library and using the Up and - Down controls. The tools merge the referenced libraries with your application - starting from lowest priority (bottom of the list) to highest (top of the list). If more than one - library defines the same resource ID, the tools select the resource from the library with higher - priority. The application itself has highest priority and its resources are always used in - preference to identical resource IDs defined in libraries.

            - -

            Declaring library components in the manifest file

            - -

            In the manifest file of the application project, you must add declarations of all components - that the application will use that are imported from a library project. For example, you must - declare any <activity>, <service>, - <receiver>, <provider>, and so on, as well as - <permission>, <uses-library>, and similar elements.

            - -

            Declarations should reference the library components by their fully-qualified package names, - where appropriate.

            - -

            For example, the TicTacToeMain example - application declares the library activity GameActivity like this:

            -
            -<manifest>
            -  ...
            -  <application>
            -    ...
            -    <activity android:name="com.example.android.tictactoe.library.GameActivity" />
            -    ...
            -  </application>
            -</manifest>
            -
            - -

            For more information about the manifest file, see the documentation for AndroidManifest.xml.

            - - - - - - - diff --git a/docs/html/tools/projects/templates.jd b/docs/html/tools/projects/templates.jd index 002e2c5acf5b6478d5d11b2a0d78b2cd67cbe297..676ca5056d5587c7c4cb6478aa6fe27ed679308d 100644 --- a/docs/html/tools/projects/templates.jd +++ b/docs/html/tools/projects/templates.jd @@ -1,6 +1,6 @@ page.title=Using Code Templates page.image=images/cards/card-using-code-templates_16x9_2x.png -page.metaDescription=Quickly create Android app projects with various UI or functional components. +page.metaDescription=Quickly create Android app projects with various UI or functional components. page.tags=studio,templates,firstapp @jd:body @@ -29,7 +29,6 @@ page.tags=studio,templates,firstapp
    -

    The SDK tools provide templates for quickly creating Android application projects with the basic structure or for adding components to your existing application modules. The code templates provided by the Android SDK follow the Android design and development guidelines to get you on the @@ -45,6 +44,18 @@ page.tags=studio,templates,firstapp

  • Other Templates
  • +

    + The templates use support library objects + if such objects are available. The support library objects make new features + available on the widest range of platforms. For example, since the templates + use the appcompat + library, apps based on the templates use material design user interface + principles even if they are running on older Android devices that do not + directly support material design. +

    Application Templates

    diff --git a/docs/html/tools/publishing/app-signing-eclipse.jd b/docs/html/tools/publishing/app-signing-eclipse.jd deleted file mode 100644 index 738e48868319622c44d468cf1ee91b3c835289e1..0000000000000000000000000000000000000000 --- a/docs/html/tools/publishing/app-signing-eclipse.jd +++ /dev/null @@ -1,64 +0,0 @@ -page.title=Signing Your Applications from Eclipse with ADT -@jd:body - -
    -
    - -

    In this document

    - -
      -
    1. Signing Your App for Release
    2. -
    - -

    See also

    - -
      -
    1. Signing Your Applications
    2. -
    - -
    -
    - - -

    Android requires that all apps be digitally signed with a certificate before they can be -installed. Android uses this certificate to identify the author of an app, and the certificate -does not need to be signed by a certificate authority.

    - -

    This document provides detailed instructions about how to sign your apps in release mode with the -ADT plugin for Eclipse. For information about how to obtain a certificate for signing your app, see -Signing Your Applications. -

    - - -

    Signing Your App for Release

    - -

    To sign your app for release with ADT, follow these steps:

    - -
      -
    1. Select the project in the Package Explorer and select File > -Export.
    2. -
    3. On the Export window, select Export Android Application and click -Next.
    4. -
    5. On the Export Android Application window, select the project you want to sign and -click Next.
    6. -
    7. -

      On the next window, enter the location to create a keystore and a keystore password. If you -already have a keystore, select Use existing keystore, enter your keystore's -location and password, and go to step 6.

      - -

      Figure 6. Select a keystore in ADT.

      -
    8. -
    9. On the next window, provide the required information as shown in figure 5.

      -

      Your key should be valid for at least 25 years, so you can sign app updates with the same key -through the lifespan of your app.

      - -

      Figure 7. Create a private key in ADT.

      -
    10. -
    11. On the next window, select the location to export the signed APK.

      - -

      Figure 8. Export the signed APK in ADT.

      -
    12. -
    diff --git a/docs/html/tools/publishing/app-signing.jd b/docs/html/tools/publishing/app-signing.jd index 4265a636a7b716f90989d392c0fda1b0dbbe69b1..4b74f5ff81393ce932cd19891dade82b6cf6507a 100644 --- a/docs/html/tools/publishing/app-signing.jd +++ b/docs/html/tools/publishing/app-signing.jd @@ -16,7 +16,7 @@ page.title=Signing Your Applications
  • Signing Your App in Android Studio
      -
    1. Automatically Signing Your App
    2. +
    3. Automatically Signing Your App
  • Signing Your App with Android Studio
  • @@ -60,14 +60,14 @@ without typing the password every time you make a change to your project.

    you run or debug your project from the IDE.

    You can run and debug an app signed in debug mode on the emulator and on devices connected -to your development manchine through USB, but you cannot distribute an app signed in debug +to your development machine through USB, but you cannot distribute an app signed in debug mode.

    By default, the debug configuration uses a debug keystore, with a known password and a default key with a known password. -The debug keystore is located in $HOME/.android/debug.keystore, and is created if not present. - -The debug build type is set to use this debug SigningConfig automatically.

    +The debug keystore is located in $HOME/.android/debug.keystore, and +is created if not present. +The debug build type is set to use this debug SigningConfig automatically.

    For more information about how to build and run apps in debug mode, see Building and Running.

    @@ -82,7 +82,8 @@ set of private keys. You must keep your keystore in a safe and secure place.Create a private key. A private key represents the entity to be identified with the app, such as a person or a company.
  • Add the signing configuration to the build file for the app module:

    -

    +
    +
     ...
     android {
         ...
    @@ -103,12 +104,13 @@ android {
         }
     }
     ...
    -

    +
  • Invoke the assembleRelease build task from Android Studio.
  • -

    The package in app/build/apk/app-release.apk is now signed with your release key.

    +

    The package in app/build/apk/app-release.apk is now signed with your release +key.

    Note: Including the passwords for your release key and keystore inside the build file is not a good security practice. Alternatively, you can configure the build @@ -156,26 +158,40 @@ For more information on packaging and signing Android Wear apps, see

    1. On the menu bar, click Build > Generate Signed APK.
    2. -
    3. On the Generate Signed APK Wizard window, click Create new to create -a new keystore.

      If you already have a keystore, go to step 4.

    4. +
    5. On the Generate Signed APK Wizard window, click Create new to +create a new keystore.

      +

      If you already have a keystore, go to step 4.

    6. On the New Key Store window, provide the required information as shown -in figure 1.

      Your key should be valid for at least 25 years, so you can sign app updates +in figure 1.

      +

      Your key should be valid for at least 25 years, so you can sign app updates with the same key through the lifespan of your app.

      Figure 1. Create a new keystore in Android Studio.

    7. -
    8. On the Generate Signed APK Wizard window, select a keystore, a private key, and enter -the passwords for both. Then click Next.

      +
    9. On the Generate Signed APK Wizard window, select a keystore, a private key, and +enter the passwords for both. Then click Next.

      Figure 2. Select a private key in Android Studio.

    10. -
    11. On the next window, select a destination for the signed APK and click -Finish.

      +
    12. + On the next window, select a destination for the signed APK(s), select the + build type, (if applicable) choose the product flavor(s), and click + Finish.

      -

      Figure 3. Generate a signed APK in Android Studio.

      +

      + Figure 3. Generate signed APKs for the selected product + flavors. +

      +

      + Note: If your project uses product flavors, you can select + multiple product flavors while holding down the Ctrl key on + Windown/Linux, or the Command key on Mac OSX. Android Studio + will generate a separate APK for each selected product flavor. +

      +
    @@ -193,14 +209,16 @@ Settings
    . more than one), and enter the required information.

    -

    Figure 4. Create a signing configuration in Android Studio.

    +

    Figure 4. Create a signing configuration in Android +Studio.

  • Click on the Build Types tab.
  • Select the release build.
  • Under Signing Config, select the signing configuration you just created.

    -

    Figure 5. Select a signing configuration in Android Studio.

    +

    Figure 5. Select a signing configuration in Android +Studio.

  • Click OK.
  • @@ -299,9 +317,10 @@ an app in release mode from the command line:

    1. Generate a private key using - keytool. + keytool. For example:

      -
      +
       $ keytool -genkey -v -keystore my-release-key.keystore
       -alias alias_name -keyalg RSA -keysize 2048 -validity 10000
       
      @@ -317,7 +336,7 @@ $ keytool -genkey -v -keystore my-release-key.keystore

      Sign your app with your private key using jarsigner:

      -
      +
       $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1
       -keystore my-release-key.keystore my_application.apk alias_name
       
      @@ -326,14 +345,14 @@ $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1
    2. Verify that your APK is signed. For example:

      -
      +
       $ jarsigner -verify -verbose -certs my_application.apk
       
    3. Align the final APK package using zipalign.

      -
      +
       $ zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk
       

      zipalign ensures that all uncompressed data starts with a particular byte diff --git a/docs/html/tools/publishing/preparing.jd b/docs/html/tools/publishing/preparing.jd index 0b61aa7b3b6767a4284b4043e43126f9fd81bfae..3acaedcf40c84e77f38aacfa3435397977a1dd6b 100644 --- a/docs/html/tools/publishing/preparing.jd +++ b/docs/html/tools/publishing/preparing.jd @@ -1,4 +1,5 @@ page.title=Preparing for Release +page.metaDescription=Developer documentation on how to build the signed, release-ready APK. This process is the same for all Android apps. @jd:body

      diff --git a/docs/html/tools/publishing/publishing_overview.jd b/docs/html/tools/publishing/publishing_overview.jd index e42b2fbfd716d39459423bebb8775356d47207c5..167758a279bd850233224bcb33cc3f6d883d7e01 100644 --- a/docs/html/tools/publishing/publishing_overview.jd +++ b/docs/html/tools/publishing/publishing_overview.jd @@ -1,4 +1,5 @@ page.title=Publishing Overview +page.metaDescription=Start here for an overview of publishing options for Android apps. @jd:body
      diff --git a/docs/html/tools/publishing/versioning.jd b/docs/html/tools/publishing/versioning.jd index 4af6f306168fdcc28e2bf6cd7ef40e6e16013a3d..9908d811ca4d6394a63f1b8d22001a180da565b4 100644 --- a/docs/html/tools/publishing/versioning.jd +++ b/docs/html/tools/publishing/versioning.jd @@ -80,6 +80,13 @@ that the android:versionCode value does not necessarily have a strong resemblance to the application release version that is visible to the user (see android:versionName, below). Applications and publishing services should not display this version value to users.

      + +

      + Warning: The greatest possible value for + android:versionCode is MAXINT (2147483647). However, if + you upload an app with this value, your app can't ever be updated. +

      +
    4. android:versionName — A string value that represents the release version of the application code, as it should be shown to users. diff --git a/docs/html/tools/revisions/build-tools.jd b/docs/html/tools/revisions/build-tools.jd old mode 100644 new mode 100755 index 1912bb26b525568d42fed5517daf4f003c7488ec..5d9ba0514a6afab90bdd4e6988b8df78736b6bb9 --- a/docs/html/tools/revisions/build-tools.jd +++ b/docs/html/tools/revisions/build-tools.jd @@ -26,31 +26,7 @@ page.title=SDK Build Tools Release Notes

      To use a specific version of the Build Tools in your application project:

      -
      -

      - Using Eclipse

      -
      -
        -
      1. In the root folder of your application project, find the {@code project.properties} - file.
      2. -
      3. Open the file and specify the Build Tools version by adding a {@code buildtools} property - on a separate line: -
        -sdk.buildtools=17.0.0
        -
        -
      4. -
      -
      -
      - -
      -

      - Using Android Studio

      - -
      1. In the root folder of your application project, find the {@code build.gradle} file.
      2. @@ -65,8 +41,6 @@ android {
      -
      -

      Revisions

      @@ -77,7 +51,48 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 23.0.3 (March 2016) +

      +
      +
        +
      • Fix issues in the RenderScript + Support Library on arm64 devices.
      • +
      • Fix issues in the RenderScript + Support Library on certain Jelly Bean devices.
      • +
      • Support renderscriptTargetAPI 21+ when using Android + Plugin for Gradle, Revision 2.1.0 and above .
      • +
      +
      +
      + +
      +

      + Build Tools, Revision 23.0.2 (November 2015) +

      +
      +
        +
      • Improved the merging performance of the dx tool.
      • +
      • Fixed issues in the RenderScript + compiler for Windows.
      • +
      +
      +
      + +
      +

      + Build Tools, Revision 23.0.1 (October 2015) +

      +
      +

      Fixed issues in the RenderScript tools.

      +
      +
      + +
      +

      + Build Tools, Revision 23.0.0 (August 2015)

      @@ -88,7 +103,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 22.0.1 (March 2015)

      @@ -101,7 +116,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 22.0.0 (March 2015)

      @@ -112,7 +127,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.1.2 (February 2015)

      @@ -123,7 +138,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.1.1 (November 2014)

      @@ -134,7 +149,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.1 (October 2014)

      @@ -146,7 +161,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.0.2 (October 2014)

      @@ -157,7 +172,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.0.1 (October 2014)

      @@ -168,7 +183,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 21.0.0 (October 2014)

      @@ -191,7 +206,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 20.0.0 (June 2014)

      @@ -210,7 +225,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 19.1.0 (May 2014)

      @@ -230,7 +245,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 19.0.3 (March 2014)

      @@ -242,7 +257,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 19.0.2 (February 2014)

      @@ -269,7 +284,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 19.0.1 (December 2013)

      @@ -292,7 +307,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 19 (October 2013)

      @@ -304,7 +319,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 18.1.1 (September 2013)

      @@ -316,7 +331,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 18.1.0 (September 2013)

      @@ -328,7 +343,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 18.0.1 (July 2013)

      @@ -340,7 +355,7 @@ listing in the Android SDK Manager.

      - Build Tools, Revision 17 (May 2013)

      diff --git a/docs/html/tools/revisions/gradle-plugin.jd b/docs/html/tools/revisions/gradle-plugin.jd index 17e6d799ed3cb9e3822f9444b073e8c1ae425a0b..540bbcdcccb490d297df60375760d721c5990dc5 100644 --- a/docs/html/tools/revisions/gradle-plugin.jd +++ b/docs/html/tools/revisions/gradle-plugin.jd @@ -36,10 +36,145 @@ plugin you are using, check the version declaration in the project-level

      For a summary of known issues in Android Plugin for Gradle, see http://tools.android.com/knownissues.

      -

      - Android Plugin for Gradle, Revision 1.5.0 (November 2015) +

      + +
      + +
      +
      Dependencies:
      + +
      +
        +
      • Gradle 2.2.1 or higher.
      • +
      • Build Tools 21.1.1 or higher.
      • +
      +
      + +
      General Notes:
      +
      +
        +
      • Integrated the Data Binding Plugin into the Android Plugin for Gradle. To enable it, add + the following code to each per-project build.gradle file that uses the + plugin:
      • +
        +android {
        +    dataBinding {
        +        enabled = true
        +    }
        +}
        +
        +
      • Added a new Transform API + to allow third-party plugins to manipulate compiled .class files before they’re + converted to .dex files. The Transform API simplifies injecting custom class + manipulations while offering more flexibility regarding what you can manipulate. To insert a + transform into a build, create a new class implementing one of the Transform + interfaces, and register it with android.registerTransform(theTransform) or + android.registerTransform(theTransform, dependencies). There’s no need to + wire tasks together. Note the following about the Transform API:
      • +
          +
        • A transform can apply to one or more of the following: the current project, subprojects, + and external libraries.
        • +
        • A transform must be registered globally, which applies them to all variants.
        • +
        • Internal code processing, through the Java Code Coverage Library (JaCoCo), ProGuard, + and MultiDex, now uses the Transform API. However, the Java Android Compiler Kit + (Jack) doesn’t use this API: only the javac/dx code path does.
        • +
        • Gradle executes the transforms in this order: JaCoCo, third-party plugins, ProGuard. + The execution order for third-party plugins matches the order in which the transforms are + added by the third party plugins; third-party plugin developers can't control the execution + order of the transforms through an API.
        • +
        +
      • Deprecated the dex getter from the ApplicationVariant class. + You can't access the Dex task through the variant API anymore because it’s now + accomplished through a transform. There's + currently no replacement for controlling the dex process.
      • +
      • Fixed incremental support for assets.
      • +
      • Improved MultiDex support by making it available for test projects, and + tests now automatically have the com.android.support:multidex-instrumentation + dependency.
      • +
      • Added the ability to properly fail a Gradle build and report the underlying error cause + when the Gradle build invokes asynchronous tasks and there’s a failure in the worker + process.
      • +
      • Added support for configuring a specific Application Binary Interface (ABI) in variants + that contain multiple ABIs.
      • +
      • Added support for a comma-separated list of device serial numbers for the + ANDROID_SERIAL environment variable when installing or running tests.
      • +
      • Fixed an installation failure on devices running Android 5.0 (API level 20) and higher + when the APK name contains a space.
      • +
      • Fixed various issues related to the Android Asset Packaging Tool (AAPT) error output.
      • +
      • Added JaCoCo incremental instrumentation support for faster incremental builds. The + Android Plugin for Gradle now invokes the JaCoCo instrumenter directly. To force a newer + version of the JaCoCo instrumenter, you need to add it as a build script dependency.
      • +
      • Fixed JaCoCo support so it ignores files that aren’t classes.
      • +
      • Added vector drawable support for generating PNGs at build time for backward-compatibility. + Android Plugin for Gradle generates PNGs for every vector drawable found in a resource + directory that doesn’t specify an API version or specifies an + android:minSdkVersion attribute of 20 or lower in the + <uses-sdk> element in the app manifest. You can set PNG densities by + using the generatedDensities property in the defaultConfig or + productFlavor sections of a build.gradle file.
      • +
      • Added sharing of the mockable android.jar, which the plugin generates only + once and uses for unit testing. Multiple modules, such as app and + lib, now share it. Delete $rootDir/build to regenerate it.
      • +
      • Changed the processing of Java resources to occur before the obfuscation tasks instead of + during the packaging of the APK. This change allows the obfuscation tasks to have a chance + to adapt the Java resources following packages obfuscation.
      • +
      • Fixed an issue with using Java Native Interface (JNI) code in the experimental library + plugin.
      • +
      • Added the ability to set the platform version separately from the + android:compileSdkVersion attribute in the experimental library + plugin.
      • +
      +
      +
      +
      + +
      +

      + Android Plugin for Gradle, Revision 1.3.1 (August 2015) +

      + +
      + +
      +
      Dependencies:
      + +
      +
        +
      • Gradle 2.2.1 or higher.
      • +
      • Build Tools 21.1.1 or higher.
      • +
      +
      + +
      General Notes:
      +
      +
        +
      • Fixed the ZipAlign task to properly + consume the output of the previous task when using a customized filename.
      • +
      • Fixed Renderscript + packaging with the NDK.
      • +
      • Maintained support for the createDebugCoverageReport build task.
      • +
      • Fixed support for customized use of the archiveBaseName property + in the build.gradle build> file.
      • +
      • Fixed the Invalid ResourceType + lint warning caused by parameter method + annotation lookup when running + lint outside of Android Studio.
      • +
      +
      +
      +
      + + + + +
      +

      + Android Plugin for Gradle, Revision 1.3.0 (July 2015)

      @@ -139,7 +274,7 @@ android {

      - Android Plugin for Gradle, Revision 1.2.0 (April 2015)

      @@ -206,7 +341,7 @@ android {

      - Android Plugin for Gradle, Revision 1.1.3 (March 2015)

      @@ -236,7 +371,7 @@ android {

      - Android Plugin for Gradle, Revision 1.1.2 (February 2015)

      @@ -268,7 +403,7 @@ android {

      - Android Plugin for Gradle, Revision 1.1.1 (February 2015)

      @@ -305,7 +440,7 @@ android {

      - Android Plugin for Gradle, Revision 1.1.0 (February 2015)

      @@ -375,7 +510,7 @@ android {

      - Android Plugin for Gradle, Revision 1.0.1 (January 2015)

      @@ -416,7 +551,7 @@ android {

      - Android Plugin for Gradle, Revision 1.0.0 (December 2014)

      @@ -449,9 +584,9 @@ android {

      Updating the Android Plugin for Gradle Version

      The Android Plugin for Gradle version is specified in the -File > Project Structure menu or the project-level +File > Project Structure > Project menu and the project-level build.gradle file. The plugin version applies to all modules built in that -Android Studio project. This example updates the Android Plugin for Gradle to version 1.1.0: +Android Studio project. This example sets the Android Plugin for Gradle to version 1.1.0 from the build.gradle file:

       ...
            dependencies {
      @@ -465,8 +600,8 @@ Android Studio project. This example updates the Android Plugin for Gradle to ve
       version numbers. Using this feature can cause unexpected version updates and difficulty
       resolving version differences. 

      -

      If you're building with Gradle but using not Android Studio, the build process downloads the -latest Android Plugin for Gradle plugin when it runs.

      +

      If you're building with Gradle but not using Android Studio, the build process downloads the +latest Android Plugin for Gradle when it runs.

      @@ -474,8 +609,8 @@ latest Android Plugin for Gradle plugin when it runs.

      Android Studio requires Gradle version 2.2.1 or later. To view and update the Gradle version, edit the Gradle distribution reference in the -gradle/wrapper/gradle-wrapper.properties file. This example shows the -Android Plugin for Gradle version set to 2.2.1.

      +gradle/wrapper/gradle-wrapper.properties file. This example sets the +Gradle version to 2.2.1.

       ...
      @@ -483,14 +618,6 @@ distributionUrl=http\://services.gradle.org/distributions/gradle-2.2.1-all.zip
       ...
       
      - - -

      For more details about the supported Android Plugin for Gradle properties and syntax, click the link to the -Plugin Language Reference.

      - - - - - +Plugin Language Reference.

      \ No newline at end of file diff --git a/docs/html/tools/revisions/platforms.jd b/docs/html/tools/revisions/platforms.jd index 61bfcbb8cb5b9eba737134765d35a72624540822..b3941aed6a683d97e1c741e7b3573529c6b46435 100644 --- a/docs/html/tools/revisions/platforms.jd +++ b/docs/html/tools/revisions/platforms.jd @@ -26,7 +26,7 @@ for app development. If you want details about the features and APIs added in ea version, instead read the highlights in the About section.

      -

      In order to compile your application against a particular version of Android, you must use the +

      To compile your application against a particular version of Android, you must use the SDK Manager to download and install the SDK Platform for that release. If you want to test your application on an emulator, you must also download at least one System Image for that Android version.

      @@ -58,7 +58,25 @@ packages may not be available for download.

      - Revision 2 (November 2015) +

      + +
      + +

      Fixed bugs in the layout rendering library used by Android Studio.

      +

      Dependencies:

      +
        +
      • Android SDK Platform-tools r23 or higher is required.
      • +
      • Android SDK Tools 24.3.4 or higher is required.
      • +
      +
      + +
      + +
      +

      + Revision 1 (August 2015)

      @@ -80,7 +98,7 @@ class="toggle-content-img" alt="" />Revision 1 (August 2015)

      - Revision 1 (March 2015)

      @@ -102,7 +120,7 @@ class="toggle-content-img" alt="" />Revision 1 (March 2015)

      - Revision 2 (December 2014)

      @@ -118,7 +136,7 @@ class="toggle-content-img" alt="" />Revision 2 (December 2014)

      - Revision 1 (October 2014)

      @@ -150,7 +168,7 @@ class="toggle-content-img" alt="" />Revision 1 (October 2014)

      - Revision 2 (October 2014)

      @@ -167,7 +185,7 @@ class="toggle-content-img" alt="" />Revision 2 (October 2014)

      - Revision 1 (June 2014)

      @@ -196,7 +214,7 @@ class="toggle-content-img" alt="" />Revision 1 (June 2014)

      - Revision 2 (December 2013)

      @@ -215,7 +233,7 @@ class="toggle-content-img" alt="" />Revision 2 (December 2013)

      - Revision 1 (October 2013)

      @@ -238,7 +256,7 @@ class="toggle-content-img" alt="" />Revision 1 (October 2013)

      - Revision 4 (March 2014)

      @@ -253,7 +271,7 @@ class="toggle-content-img" alt="" />Revision 4 (March 2014)

      - Revision 3 (February 2014)

      @@ -268,7 +286,7 @@ class="toggle-content-img" alt="" />Revision 3 (February 2014)

      - Revision 2 (December 2013)

      @@ -282,7 +300,7 @@ class="toggle-content-img" alt="" />Revision 2 (December 2013)

      - Revision 1 (October 2013)

      @@ -303,7 +321,7 @@ class="toggle-content-img" alt="" />Revision 1 (October 2013)

      - Revision 2 (August 2013)

      @@ -322,7 +340,7 @@ class="toggle-content-img" alt="" />Revision 2 (August 2013)

      - Revision 1 (July 2013)

      @@ -342,7 +360,7 @@ class="toggle-content-img" alt="" />Revision 1 (July 2013)

      - Revision 3 (September 2013)

      @@ -357,7 +375,7 @@ class="toggle-content-img" alt="" />Revision 3 (September 2013)

      - Revision 2 (August 2013)

      @@ -372,7 +390,7 @@ class="toggle-content-img" alt="" />Revision 2 (August 2013)

      - Revision 1 (July 2013)

      @@ -393,7 +411,7 @@ class="toggle-content-img" alt="" />Revision 1 (July 2013)

      - Revision 2 (February 2013)

      @@ -411,7 +429,7 @@ class="toggle-content-img" alt="" />Revision 2 (February 2013)

      - Revision 1 (November 2012)

      @@ -499,7 +517,7 @@ Devices in the AVD Manager:<

      - Revision 3 (October 2012)

      @@ -517,7 +535,7 @@ class="toggle-content-img" alt="" />Revision 3 (October 2012)

      - Revision 2 (July 2012)

      @@ -535,7 +553,7 @@ class="toggle-content-img" alt="" />Revision 2 (July 2012)

      - Revision 1 (June 2012)

      @@ -612,7 +630,7 @@ emulator configuration.

      - Revision 3 (March 2012)

      @@ -633,7 +651,7 @@ hardware graphics acceleration when used with SDK Tools r17 or higher.

      - Revision 2 (January 2012)

      @@ -651,7 +669,7 @@ class="toggle-content-img" alt="" />Revision 2 (January 2012)

      - Revision 1 (December 2011)

      @@ -721,7 +739,7 @@ class="toggle-content-img" alt="" />Revision 1 (December 2011)

      - Android 4.0, Revision 2 (December 2011)

      @@ -737,7 +755,7 @@ class="toggle-content-img" alt="" />Android 4.0, Revision 2 (December 20

      - Android 4.0, Revision 1 (October 2011)

      @@ -814,7 +832,7 @@ the one for the WXGA720 skin, we recommend that you primarily use the traditiona

      - Android 3.2, Revision 1 (July 2011)

      @@ -868,7 +886,7 @@ Screens.

      - Android 3.1, Revision 3 (July 2011)

      @@ -896,7 +914,7 @@ ADT 12.

      - Android 3.1, Revision 2 (May 2011)

      @@ -922,7 +940,7 @@ running in ADT.

      - Android 3.1, Revision 1 (May 2011)

      @@ -971,7 +989,7 @@ Screens.

      - Android 3.0, Revision 2 (July 2011)

      @@ -998,7 +1016,7 @@ ADT 12.

      - Android 3.0, Revision 1 (February 2011)

      @@ -1049,7 +1067,7 @@ ADT 12.

      - Android 2.3.4, Revision 1 (May 2011)

      @@ -1109,7 +1127,7 @@ emulator skins are:

      - Android 2.3.3, Revision 2 (July 2011)

      @@ -1136,7 +1154,7 @@ ADT 12.

      - Android 2.3.3, Revision 1 (February 2011)

      @@ -1196,7 +1214,7 @@ emulator skins are:

      - Android 2.3, Revision 1 (December 2010)

      @@ -1252,7 +1270,7 @@ emulator skins are:

      - Android {@sdkPlatformVersion}, Revision 3 (July 2011)

      @@ -1279,7 +1297,7 @@ ADT 12.

      - Android {@sdkPlatformVersion}, Revision 2 (July 2010)

      @@ -1307,7 +1325,7 @@ Backup.
    5. - Android {@sdkPlatformVersion}, Revision 1 (May 2010)

      diff --git a/docs/html/tools/revisions/studio.jd b/docs/html/tools/revisions/studio.jd old mode 100644 new mode 100755 index 298b173559a762b5102653cc62b36519fc58d37a..a65c1646dc6a52d48247074046db28e483989157 --- a/docs/html/tools/revisions/studio.jd +++ b/docs/html/tools/revisions/studio.jd @@ -39,10 +39,158 @@ select Android Studio > Check for updates).

      The sections below provide notes about successive releases of Android Studio, as denoted by revision number.

      -

      - Android Studio v1.5.1 (December 2015) +

      +
      +

      Fixes and enhancements:

      +
        +
      • Fixed a rendering failure issue in the Layout Editor. + Issue: 194612
      • +
      • Added the ability to vary description manifest attributes by configuration. + Issue: 194705 +
      • +
      • Improved the contrast of the Android Studio Darcula appearance theme in Vector Asset Studio. + Issue: 191819 +
      • +
      • Added Help button support to Vector Asset Studio. +
      • +
      • Added support for the % operator for data binding. Issue: 194045 +
      • +
      • Fixed a case where launching an app for debugging resulted in the debugger connecting to the + wrong device. Issue: 195167 +
      • +
      • Fixed a null pointer exception that could occur when attempting to run an app in + certain scenarios. +
      • +
      +
      +
      + +
      +

      + Android Studio v1.5.0 (November 2015) +

      +
      +

      Fixes and enhancements:

      +
        +
      • Added new Memory Monitor analysis abilities to Android Monitor. When you view an HPROF file + captured from this monitor, the display is now more helpful so you can more quickly locate + problems, such as memory leaks. To use this monitor, click Android Monitor at + the bottom of the main window. In Android Monitor, click the Memory tab. While + the monitor is running, click the Dump Java Heap icon, and then click + Captures in the main window and double-click the file to view it. Click + Capture Analysis on the right. (The + Android Device Monitor can't be running at the same time as Android Monitor.)
      • +
      • Added new deep link and app link support. The Code Editor can automatically create an + intent filter for deep linking in the AndroidManifest.xml file. It can also + generate code to help you integrate with the + App Indexing API + in an activity in a Java file. A deep link testing feature + helps you verify that a specified deep link can launch an app. In the General + tab of the Run/Debug Configurations dialog, you can specify deep link launch + options. You can also test App Indexing API calls in an activity by using the Android Monitor + logcat display. The Android lint tool now has warnings for + certain issues involving deep links and the App Indexing API. +
      • +
      • Added the ability to use short names when code-completing custom views in the Code Editor. +
      • +
      • Added support for more {@link android.graphics.drawable.VectorDrawable} elements to + Vector Asset Studio + for backward-compatibility. Vector Asset Studio can use these elements to convert vector + drawables into PNG raster images to use with Android 4.4 (API level 20) and lower. +
      • +
      • Added new lint checks for Android TV and Android Auto to give you + immediate, actionable feedback in Android Studio, along with several quick fixes. For example, + for Android TV, it can report and provide a quick fix for permissions, unsupported hardware, + uses-feature element, and missing banner issues. For Android Auto, it can + validate the correct usage in the descriptor file referred from your + AndroidManifest.xml file, report if there isn't an intent filter for the + MediaBrowserService class, and identify certain voice actions issues. +
      • +
      • Added new lint checks for insecure broadcast receivers, + SSLCertificateSocketFactory and HostnameVerifier class uses, and + File.setReadable() and File.setWritable() calls. It also detects + invalid manifest resource lookups, especially for resources that vary by configuration. +
      • +
      • Fixed a number of stability issues. +
      • +
      +
      +
      + + +
      +

      + Android Studio v1.4.1 (October 2015) +

      +
      +

      Fixes and enhancements:

      +
        +
      • Fixed a Gradle model caching issue that could lead to excessive Gradle syncing when the IDE + was restarted. +
      • +
      • Fixed a native debugging deadlock issue. +
      • +
      • Fixed an issue blocking users of the Subversion 1.9 version control system. +
      • +
      • Fixed a Device Chooser dialog problem where after connecting a device that was + unauthorized you could no longer select the emulator. Issue: 189658 +
      • +
      • Fixed incorrect translation error reporting for locales that have a region qualifier and a + translation in the region (but not in the base locale). Issue: 188577 +
      • +
      • Fixed a deadlock issue in the Theme Editor related to its interaction with the Layout + Editor. Issue: 188070 +
      • +
      • Fixed a Theme Editor reload and edit conflict causing attributes to not properly update. + Issue: 187726 +
      • +
      • Improved Theme Editor performance. +
      • +
      • Fixed an issue where the android:required attribute was ignored in the manifest. + Issue: 187665 +
      • +
      +
      +
      + + +
      +

      + Android Studio v1.4.0 (September 2015) +

      +
      +

      Fixes and enhancements:

      +
        +
      • Added the Vector Asset Studio + tool for importing vector graphics, such as material icons + and SVG files. To use this tool, in the Android view of the Project window, right-click the + res folder and select New > Vector Asset. +
      • +
      • Added new Android Monitor functions, GPU and Network. To use these monitors, click + Android Monitor at the bottom of the main window. The Android Device Monitor + can't be running at the same time as Android Monitor. +
      • +
      • Added an early preview of the new Theme Editor. To use this feature, select + Tools > Android > Theme Editor. +
      • +
      • Updated the Android templates for the Design Support Library. Templates now include support + for the Material Design specification, as well as the appcompat Support Library + for backwards compatibility. +
      • +
      +
      +
      + +
      +

      + Android Studio v1.3.2 (August 2015)

      @@ -65,7 +213,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.3.1 (August 2015)

      @@ -85,7 +233,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.3.0 (July 2015)

      @@ -106,7 +254,7 @@ Android Studio, as denoted by revision number.

    6. Integrated the SDK Manager into Android Studio to simplify package and tools access and provide update notifications.

      Note: The standalone SDK Manager is still available from - the command line, but is recommended for use with only Eclipse ADT and standalone SDK + the command line, but is recommended for use only with standalone SDK installations.

    7. Added the finger command in the emulator console to simulate fingerprint @@ -142,7 +290,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.2.2 (June 2015)

      @@ -156,7 +304,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.2.1 (May 2015)

      @@ -171,7 +319,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.2.0 (April 2015)

      @@ -218,7 +366,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.1.0 (February 2015)

      @@ -246,7 +394,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.0.1 (December 2014)

      @@ -269,7 +417,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v1.0 (December 2014)

      @@ -281,7 +429,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.8.14 (October 2014)

      @@ -294,7 +442,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.8.6 (August 2014)

      @@ -305,7 +453,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.8.0 (June 2014)

      @@ -317,7 +465,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.5.2 (May 2014)

      @@ -330,7 +478,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.4.6 (March 2014)

      @@ -343,7 +491,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.4.2 (Jan 2014)

      @@ -356,7 +504,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.3.2 (Oct 2013)

      @@ -369,7 +517,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.2.x (July 2013)

      @@ -430,7 +578,7 @@ Android Studio, as denoted by revision number.

      - Android Studio v0.1.x (May 2013)

      diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd index 5a9d9d098c9ba58918cec1e102ea9b16b6619d1c..d5f581f260619c781e6a89fde771b391f1ce1f88 100644 --- a/docs/html/tools/sdk/eclipse-adt.jd +++ b/docs/html/tools/sdk/eclipse-adt.jd @@ -52,7 +52,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.7 (August 2015)

      @@ -85,7 +85,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.6 (March 2015)

      @@ -119,7 +119,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.4 (October 2014)

      @@ -153,7 +153,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.3 (August 2014)

      @@ -189,7 +189,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.2 (July 2014)

      @@ -224,7 +224,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 23.0.0 (June 2014)

      @@ -256,7 +256,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.6.3 (April 2014)

      @@ -296,7 +296,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.6.2 (March 2014)

      @@ -341,7 +341,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.6.1 (March 2014)

      @@ -389,7 +389,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.6.0 (March 2014)

      @@ -453,7 +453,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.3.0 (October 2013)

      @@ -490,7 +490,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.2.1 (September 2013)

      @@ -524,7 +524,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.2 (September 2013)

      @@ -562,7 +562,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.0.5 (July 2013)

      @@ -597,7 +597,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.0.4 (July 2013)

      @@ -634,7 +634,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.0.1 (May 2013)

      @@ -671,7 +671,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 22.0.0 (May 2013)

      @@ -715,7 +715,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 21.1.0 (February 2013)

      @@ -773,7 +773,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 21.0.1 (December 2012)

      @@ -893,7 +893,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 21.0.0 (November 2012)

      @@ -1015,7 +1015,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 20.0.3 (August 2012)

      @@ -1050,7 +1050,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 20.0.2 (July 2012)

      @@ -1086,7 +1086,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - ADT 20.0.1 (July 2012)

      @@ -1128,7 +1128,7 @@ unprotected receivers for default Android actions.
    8. - ADT 20.0.0 (June 2012)

      @@ -1252,7 +1252,7 @@ functions.

      - ADT 18.0.0 (April 2012)

      @@ -1288,7 +1288,7 @@ functions.

      - ADT 17.0.0 (March 2012)

      @@ -1391,7 +1391,7 @@ select. (Issue

      - ADT 16.0.1 (December 2011)

      @@ -1426,7 +1426,7 @@ select. (Issue

      - ADT 16.0.0 (December 2011)

      @@ -1459,7 +1459,7 @@ href="http://tools.android.com/recent/lint">more info)

      - ADT 15.0.1 (November 2011)

      @@ -1494,7 +1494,7 @@ href="http://tools.android.com/recent/lint">more info)

      - ADT 15.0.0 (October 2011)

      @@ -1527,7 +1527,7 @@ href="http://tools.android.com/recent/lint">more info)

      - ADT 14.0.0 (October 2011)

      @@ -1647,7 +1647,7 @@ Linux.

      - ADT 12.0.0 (July 2011)

      @@ -1701,7 +1701,7 @@ the Android SDK Manager to do so.

      - ADT 11.0.0 (June 2011)

      @@ -1837,7 +1837,7 @@ href="http://tools.android.com/recent">Android Tools Project Site.

      - ADT 10.0.1 (March 2011)

      @@ -1868,7 +1868,7 @@ requires Eclipse 3.5 or higher (as of 10.0.0).

      - ADT 10.0.0 (February 2011)

      @@ -1918,7 +1918,7 @@ so.

      - ADT 9.0.0 (January 2011)

      @@ -2027,7 +2027,7 @@ so.

      - ADT 8.0.1 (December 2010)

      @@ -2056,7 +2056,7 @@ so.

      - ADT 8.0.0 (December 2010)

      @@ -2103,7 +2103,7 @@ example, ADT 8.x is for SDK Tools r8.

      - ADT 0.9.9 (September 2010)

      @@ -2131,7 +2131,7 @@ so.

      - ADT 0.9.8 (September 2010)

      @@ -2180,7 +2180,7 @@ project.

      - ADT 0.9.7 (May 2010)

      @@ -2206,7 +2206,7 @@ project support through the Ant build system.

      - ADT 0.9.6 (March 2010)

      @@ -2275,7 +2275,7 @@ targets for application launches.

      - ADT 0.9.5 (December 2009)

      @@ -2305,7 +2305,7 @@ see Exploring the SDK.

      - ADT 0.9.4 (October 2009)

      diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd index a43c9696dd20429fd7c97c7beb293ce14f23a6e0..1625716004f54b1641761e1b51500f522804e8fd 100644 --- a/docs/html/tools/sdk/tools-notes.jd +++ b/docs/html/tools/sdk/tools-notes.jd @@ -21,12 +21,95 @@ Tools you are using, refer to the "Installed Packages" listing in the Android SD

      For a summary of all known issues in SDK Tools, see http://tools.android.com/knownissues.

      +
      +

      + SDK Platform-tools, Revision 23.1.0 (December 2015) +

      +
      +
      +
      General Notes:
      +
      +
        +
      • Changed Linux requirements for Android SDK Platform-tools revision 23.1.0 and later: + it now requires 64-bit Linux.
      • +
      +
      -
      + + +
      +
      + +
      +

      + SDK Tools, Revision 24.4.1 (October 2015) +

      + +
      + +
      +
      Dependencies:
      + +
      +
        +
      • Android SDK Platform-tools revision 23 or later.
      • +
      +
      + +
      General Notes:
      +
      +
        +
      • Fixed a problem where the emulator title bar was hidden off screen. + (Issue 178344)
      • +
      • Enabled the emulator to resize the user data partition by including e2fsprogs binaries. + (Issue 189030)
      • +
      • Fixed a regression on the 32-bit Windows OS where the emulator fails to boot Android 6.0 + (API level 23) through Android 5.0 (API level 21) system images. + (Issue 188326)
      • +
      +
      + +
      +
      + + +
      +

      + SDK Tools, Revision 24.4.0 (October 2015) +

      + +
      + +
      +
      Dependencies:
      + +
      +
        +
      • Android SDK Platform-tools revision 23 or later.
      • +
      +
      + +
      General Notes:
      +
      +
        +
      • Updated the emulator so it can display an upgrade notification when a new version is + available.
      • +
      • Added the ability for the emulator to send basic crash reports. You must opt-in + through Android Studio preferences to enable crash report transmission.
      • +
      +
      + +
      +
      + +

      - SDK Tools, Revision 24.3.4 (August 2015)

      @@ -78,7 +161,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.3.3 (June 2015)

      @@ -106,7 +189,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.3.2 (June 2015)

      @@ -133,7 +216,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.3.1 (June 2015)

      @@ -163,7 +246,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.3.0 (June 2015)

      @@ -194,7 +277,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.2.0 (May 2015)

      @@ -221,7 +304,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.1.2 (February 2015)

      @@ -250,7 +333,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.0.2 (December 2014)

      @@ -277,7 +360,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.0.1 (December 2014)

      @@ -304,7 +387,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 24.0.0 (December 2014)

      @@ -332,7 +415,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 23.0.5 (October 2014)

      @@ -364,7 +447,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 23.0.4 (October 2014)

      @@ -396,7 +479,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 23.0.2 (July 2014)

      @@ -430,7 +513,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 23.0.0 (June 2014)

      @@ -461,7 +544,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.6.4 (June 2014)

      @@ -494,7 +577,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.6.3 (April 2014)

      @@ -534,7 +617,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.6.2 (March 2014)

      @@ -569,7 +652,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.6.1 (March 2014)

      @@ -617,7 +700,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.6 (March 2014)

      @@ -700,7 +783,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.3 (October 2013)

      @@ -732,7 +815,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.2.1 (September 2013)

      @@ -766,7 +849,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.2 (September 2013)

      @@ -814,7 +897,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.0.5 (July 2013)

      @@ -854,7 +937,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.0.4 (July 2013)

      @@ -890,7 +973,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22.0.1 (May 2013)

      @@ -931,7 +1014,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 22 (May 2013)

      @@ -986,7 +1069,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 21.1 (February 2013)

      @@ -1021,7 +1104,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 21.0.1 (December 2012)

      @@ -1124,7 +1207,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 21 (November 2012)

      @@ -1210,7 +1293,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 20.0.3 (August 2012)

      @@ -1241,7 +1324,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues

      - SDK Tools, Revision 20.0.1 (July 2012)

      @@ -1278,7 +1361,7 @@ unprotected receivers for default Android actions.

      - SDK Tools, Revision 20 (June 2012)

      @@ -1352,7 +1435,7 @@ and tested like test applications, including code coverage information.

      - SDK Tools, Revision 19 (April 2012)

      @@ -1386,7 +1469,7 @@ acceleration.

      - SDK Tools, Revision 18 (April 2012)

      @@ -1428,7 +1511,7 @@ in some cases.

      - SDK Tools, Revision 17 (March 2012)

      @@ -1532,7 +1615,7 @@ ignore attribute. (

      - SDK Tools, Revision 16 (December 2011)

      @@ -1582,7 +1665,7 @@ ignore attribute. (

      - SDK Tools, Revision 15 (October 2011)

      @@ -1630,7 +1713,7 @@ ignore attribute. (

      - SDK Tools, Revision 14 (October 2011)

      @@ -1685,7 +1768,7 @@ site.

      - SDK Tools, Revision 13 (September 2011)

      @@ -1716,7 +1799,7 @@ Ant 1.8 or later.

      - SDK Tools, Revision 12 (July 2011)

      @@ -1744,7 +1827,7 @@ Ant 1.8 or later.

      - SDK Tools, Revision 11 (May 2011)

      @@ -1772,7 +1855,7 @@ Ant 1.8 or later.

      - SDK Tools, Revision 10 (February 2011)

      @@ -1803,7 +1886,7 @@ Ant 1.8 or later.

      - SDK Tools, Revision 9 (January 2011)

      @@ -1862,7 +1945,7 @@ resolve the performance issues and it will improve in future releases.

      - SDK Tools, Revision 8 (December 2010)

      @@ -1930,7 +2013,7 @@ documentation.

      - SDK Tools, Revision 7 (September 2010)

      @@ -1964,7 +2047,7 @@ decimal point.

      - SDK Tools, Revision 6 (May 2010)

      @@ -1995,7 +2078,7 @@ provides the equivalent library project support.

      - SDK Tools, Revision 5 (March 2010)

      @@ -2044,7 +2127,7 @@ officially supported.

      - SDK Tools, Revision 4 (December 2009)

      @@ -2094,7 +2177,7 @@ skin name specified.

      - SDK Tools, Revision 3 (October 2009)

      diff --git a/docs/html/tools/studio/code-tools.jd b/docs/html/tools/studio/code-tools.jd new file mode 100644 index 0000000000000000000000000000000000000000..4033bfcb28d8193d3b4b061475a026fcbb1b6e40 --- /dev/null +++ b/docs/html/tools/studio/code-tools.jd @@ -0,0 +1,28 @@ +page.title=Android Studio Code Tools + +@jd:body + +

      + Android Studio provides a number of coding features to assist with writing and building your + app. These tools help you write code faster and improve quality. +

      + +
      +
      Lint
      +
      The Lint tool is a static code analysis tool that checks your Android + project source files for potential bugs and optimization improvements.
      + +
      Code Annotations
      +
      Annotations help you improve code readability and improve code inspector output, + by allowing you to more clearly define method parameter requirements. +
      + +
      Deep Link and App Indexing
      +
      These features help you add deep links, app indexing, and search functionality to your + apps. These features can make it easier to find content in an installed app, drive more + traffic to your app, discover which app content users view the most, and attract new users. +
      + +
      + + diff --git a/docs/html/tools/studio/index.jd b/docs/html/tools/studio/index.jd index 43cb2fe53ed5230cbb7e81c788942fa4c42fca19..b3ed82d448166ab6f06a60194d11fceaaf490482 100644 --- a/docs/html/tools/studio/index.jd +++ b/docs/html/tools/studio/index.jd @@ -1,6 +1,6 @@ page.title=Android Studio Overview page.image=images/cards/card-android-studio-overview_16x9_2x.jpg -page.metaDescription=Learn about the official IDE for Android. +page.metaDescription=The basics of working with Android Studio, from projects to build and performance. page.tags=studio,sdk,tools,firstapp @jd:body @@ -9,139 +9,105 @@ page.tags=studio,sdk,tools,firstapp

      In this document

        -
      1. Project and File Structure
      2. -
      3. Android Build System
      4. -
      5. Debug and Performance
      6. - - +
      7. Project Structure
      8. +
      9. Gradle Build System
      10. +
      11. Debug and Profile Tools
      -

      See also

      -
        -
      1. IntelliJ FAQ on migrating to IntelliJ IDEA
      2. -
      + + + Get Android Studio +
      -

      Android Studio is the official IDE for Android application development, -based on IntelliJ IDEA. -On top of the capabilities you expect from IntelliJ, -Android Studio offers:

      +

      Android Studio is the official IDE for Android app development, based on +IntelliJ IDEA. On top of IntelliJ's powerful code editor and +developer tools, Android Studio offers even more features that enhance your +productivity when building Android apps, such as:

      +
        -
      • Flexible Gradle-based build system
      • -
      • Build variants and multiple apk file generation
      • +
      • A flexible Gradle-based build system
      • +
      • Build variants and multiple APK file generation
      • Code templates to help you build common app features
      • -
      • Rich layout editor with support for drag and drop theme editing
      • -
      • {@code lint} tools to catch performance, usability, version compatibility, and other problems
      • -
      • ProGuard and app-signing capabilities
      • +
      • A rich layout editor with support for drag and drop theme editing
      • +
      • Lint tools to catch performance, usability, version compatibility, and other problems
      • +
      • Code shrinking with ProGuard and resource shrinking with Gradle
      • Built-in support for Google Cloud Platform, making it easy to integrate Google Cloud Messaging and App Engine
      • -
      • And much more
      +

      This page provides an introduction to basic Android Studio features. For +more detailed guides to using Android Studio, start by browsing pages in the +Workflow section.

      -

      If you're new to Android Studio or the IntelliJ IDEA interface, this -page provides an introduction to some key Android -Studio features.

      - -

      For specific Android Studio how-to documentation, see the pages in the Workflow section, such as Managing Projects from Android Studio and -Building and Running from Android -Studio. For a summary of the latest changes to Android Studio, see the -Android Studio Release Notes.

      +

      For a summary of the latest changes, see the Android Studio Release Notes.

      +

      Project Structure

      -

      Project and File Structure

      +
      + +

      Figure 1. The project files in Android +view.

      +
      -

      Android project view

      -

      By default, Android Studio displays your project files in the Android project view. This -view shows a flattened version of your project's structure that provides quick access to the key -source files of Android projects and helps you work with the -Gradle-based build system. -The Android project view:

      +

      Each project in Android Studio contains one or more modules with source code +files and resource files. Different types of modules include:

        -
      • Shows the most important source directories at the top level of the module hierarchy.
      • -
      • Groups the build files for all modules in a common folder.
      • -
      • Groups all the manifest files for each module in a common folder.
      • -
      • Shows resource files from all Gradle source sets.
      • -
      • Groups resource files for different locales, orientations, and screen types in a single - group per resource type.
      • +
      • Android app modules
      • +
      • Test modules
      • +
      • Library modules
      • +
      • App Engine modules
      - -

      Figure 1. Show the Android project view.

      - -

      Figure 2. Show project build files.

      - -

      The Android project view shows all the build files at the top level of the project -hierarchy under Gradle Scripts. Each project module appears as a folder at the -top level of the project hierarchy and contains these four elements at the top level:

      +

      By default, Android Studio displays your project files in the +Android project view, as shown in figure 1. +This view is organized by modules to +provide quick access to the key source files of your +project.

      +

      All the build files are visible at the top level +under Gradle Scripts and each app module +contains the following three elements:

        -
      • java/ - Source files for the module.
      • -
      • manifests/ - Manifest files for the module.
      • -
      • res/ - Resource files for the module.
      • -
      • Gradle Scripts/ - Gradle build and property files.
      • +
      • manifests: Manifest files.
      • +
      • java: Source code files.
      • +
      • res: Resource files.
      -

      For example, Android project view groups all the instances of the -ic_launcher.png resource for different screen densities under the same element.

      - -

      Note: The project structure on disk differs from this flattened -representation. To switch to back to the segregated project view, select Project -from the Project drop-down.

      - - - -

      Other Android Studio views

      -

      When you use the Project view in Android Studio, you -should notice that the project structure appears different than you may be used to in Eclipse. Each -instance of Android Studio contains a project with one or more application modules. Each -application module folder contains the complete source sets for that module, including -{@code src/main/} and {@code src/androidTest/} directories, resources, build -file and the Android manifest. For the most part, you will need to modify the files under each -module's {@code src/main/} directory for source code updates, the gradle.build file for build -specification and the files under {@code src/androidTest/} directory for test case creation. +

      The Android project structure on disk differs +from this flattened representation. To see the actual file structure of the +project, select Project from the Project +drop-down (in figure 1, it's showing as Android).

      -

      -

      Figure 3. View Android Studio Project - structure.

      - -

      You can also customize the view of the project files to focus on specific aspects of your app -development:

      - -
        -
      • Packages
      • -
      • Project Files
      • -
      • Scratches
      • -
      • Problems
      • -
      • Production
      • -
      • Tests
      • -
      - -

      For example, selecting the Problems view of your project displays links to the -source files containing any recognized coding and syntax errors, such as missing an XML element -closing tag in a layout file.

      +

      You can also customize the view of the project files to focus on specific +aspects of your app development. For example, selecting the +Problems view of your project displays links to the source +files containing any recognized coding and syntax errors, such as missing an +XML element closing tag in a layout file.

      For more information, see IntelliJ project organization and Managing Projects.

      +

      Gradle Build System

      -

      Android Build System

      -

      The Android build system is the toolkit you use to build, test, run and package -your apps. This build system replaces the Ant system used with Eclipse ADT. It can run as an -integrated tool from the Android Studio menu and independently from the command line. You can use -the features of the build system to:

      +

      Android Studio uses Gradle as the foundation of the build system, with +more Android-specific capabilities provided by the Android Plugin for +Gradle. This build system +runs as an integrated tool from the Android Studio menu and independently from +the command line. You can use the features of the build system to:

      • Customize, configure, and extend the build process.
      • @@ -150,39 +116,30 @@ the features of the build system to:

      • Reuse code and resources across source sets.
      -

      The flexibility of the Android build system enables you to achieve all of this without +

      The flexibility of Gradle enables you to achieve all of this without modifying your app's core source files. To build an Android Studio project, see -Building and Running from Android Studio. -To configure custom build settings in an Android Studio project, see -Configuring Gradle Builds.

      +Building and Running +from Android Studio. To configure custom build settings in an Android +Studio project, see Configuring Gradle +Builds.

      -

      Debug and Performance

      -

      Android Studio provides a number of improvements to assist you in debugging and improving the -performance of your code, including an improved virtual device management, inline debugging, and -performance analysis tools.

      - -

      Android Virtual Device (AVD) Manager

      -

      AVD Manager has updated screens with links to help you select the most popular device -configurations, screen sizes and resolutions for your app previews.

      -Click the Android Virtual Device Manager - in the toolbar to open it and create -new virtual devices for running your app in the emulator.

      -

      The AVD Manager comes with emulators for Nexus 6 and Nexus 9 devices and also supports -creating custom Android device skins based on specific emulator properties and assigning those -skins to hardware profiles. Android Studio installs the Intel® x86 Hardware Accelerated Execution -Manager (HAXM) emulator accelerator and creates a default emulator for quick app prototyping.

      +

      Debug and Profile Tools

      -

      For more information, see Managing AVDs.

      +

      Android Studio assists you in debugging and improving the +performance of your code, including inline debugging and +performance analysis tools.

      Inline debugging

      +

      Use inline debugging to enhance your code walk-throughs in the debugger view -with inline verification of references, expressions, and variable values. Inline debug information -includes:

      +with inline verification of references, expressions, and variable values. +Inline debug information includes:

      +
      • Inline variable values
      • Referring objects that reference a selected object
      • @@ -203,8 +160,9 @@ device or emulator, click the Android tab in the lower left cor runtime window to launch the Android runtime window. Click the Memory or CPU tab.

        - -

        Figure 4. Monitor memory and CPU usage.

        + +

        Figure 2. Monitor memory and CPU usage.

        Heap dump

        When you're monitoring memory usage in Android Studio you can, at the same time, initiate @@ -213,7 +171,7 @@ format file. The HPROF viewer displays classes, instances of each class, and a r help you track memory usage and find memory leaks.

        -

        Figure 5. HPROF viewer with heap dump.

        +

        Figure 3. HPROF viewer with heap dump.

        To create a snapshot of the Android app heap memory, click the Dump Java Heap icon () @@ -233,21 +191,11 @@ Knowing these allocations enables you to adjust the method calls related to those actions to optimize your app's performance and memory use.

        -

        Figure 6. Allocation tracker.

        +

        Figure 4. Allocation tracker.

        -

        Perform the following steps to track and analyze allocations:

        -
          -
        1. Click the Start/Stop Allocation Tracking icon - () in the - Memory Monitor. Android Studio starts tracking memory allocations.
        2. -
        3. Perform the tasks whose mallocs you want to track.
        4. -
        5. Click the Start/Stop Allocation Tracking icon again. Android Studio stops tracking mallocs - and saves the data to a file named Allocation-yyyy.mm.dd-hh.mm.ss.alloc. The - resulting file appears in the Captures tab.
        6. -
        7. Double-click the file to open it in the allocation viewer. -

          The allocation viewer allows you to view and analyze the allocations your app made while - running.

        8. -
        +

        For information about tracking and analyzing allocations, see + Memory Monitor. +

        Data file access

        @@ -324,17 +272,6 @@ to error.

        The Inspections Scope dialog appears so you can specify the desired inspection profile and scope.

        - -

        Running inspections from the command line

        -

        You can also run {@code lint} inspections from the command line in your SDK directory.

        -
        -sdk$ lint [flags] 
        -
        - -

        Note: The {@code lint} --show and --list -flags can be used to display the available issues and explanations.

        - -

        For more information, see Improving Your Code with {@code lint} and lint tool.

        @@ -359,7 +296,7 @@ Studio. Android Studio validates the configured annotations during code inspecti Library dependency.
      • In the Choose Library Dependency dialog, select support-annotations and click Ok.
      • -
    +

    The build.gradle file is updated with the support-annotations dependency.

    @@ -432,7 +369,7 @@ and enumerated constants.

    Run Analyze > Inspect Code to validate the configured annotations.

    For a complete list of the supported annotations, either use the auto-complete feature to display -the available options for the import android.support.annotation. statement or +the available options for the import android.support.annotation statement or view the contents of the {@link android.support.annotation Support-Annotations} library.

    @@ -440,30 +377,6 @@ library.

    For more details about Android annotations, see Improving Code Inspection with Annotations. - - -

    Dynamic layout preview

    -

    Android Studio allows you to work with layouts in both a Design View

    -

    -

    -

    Figure 6. Hello World App with Design View.

    - -

    and a Text View.

    - -

    - Figure 7. Hello World App with text view.

    - -

    Easily select and preview layout changes for different device images, display -densities, UI modes, locales, and Android versions (multi-API version rendering). -

    -

    Figure 8. Multi-API version rendering.

    - - -

    From the Design View, you can drag and drop elements from the Palette to the Preview or -Component Tree. The Text View allows you to directly edit the XML settings, while previewing -the device display.

    - -

    Log messages

    When you build and run your app with Android Studio, you can view adb and device log messages (logcat) by clicking Android at the bottom of the window.

    diff --git a/docs/html/tools/studio/studio-features.jd b/docs/html/tools/studio/studio-features.jd index 44d6985dfdc6d2b134e258d86ba69e03f105da8e..080a12437e6c6a101cb99219abf0b345b0a2f90b 100644 --- a/docs/html/tools/studio/studio-features.jd +++ b/docs/html/tools/studio/studio-features.jd @@ -1,6 +1,10 @@ -page.title=Features -page.metaDescription=Learn about the Android Studio features. -page.tags=studio, features +page.title=Android Studio Features +page.image=images/cards/card-studio-modules_crop_2x.png +page.metaDescription=A quick look at Android Studio features that make your work faster. +page.tags=studio, tools, sdk +meta.tags="studio" + + @jd:body
    @@ -42,24 +46,24 @@ and

    Translations Editor

    -

    Multi-language support is enhanced with the Translations Editor plugin so you can easily add -a variety of locales to the app's translation file. With -BCP 47 support, the editor -combines language and -region codes into a single selection for targeted localizations. Color codes indicate whether a -locale is complete or still missing string translations.

    - -

    To access the Translations Editor, open a strings.xml file and click the -Open Editor link, or click the globe icon -() in the Design layout view.

    +

    If your application supports multiple languages, you need to properly manage your +translated string resources. The Translations Editor lets you view and update all your string +resources in one convenient place.

    + +

    +Use the Translations Editor to view all your translated resources, modify translations, and add +new locales. You can provide default values for your resources and mark resources as +untranslatable. The Translations Editor also marks resources with missing translations in red, +and provides a link to a page where you can upload resource files and order translation services. +

    +

    For more details on the Translations Editor, see +Translations Editor.

    - -

    Figure 1. Add locales and strings in the + +

    Figure 1. Manage locales and strings in the Translations Editor.

    - -

    Android Code Samples on GitHub

    Clicking Import Samples from the File menu or Welcome page provides seamless access to Google code samples on GitHub.

    diff --git a/docs/html/tools/studio/ui-tools.jd b/docs/html/tools/studio/ui-tools.jd new file mode 100644 index 0000000000000000000000000000000000000000..2d25de66c3743ede0d2b8f02e59c9d7ea0d6c802 --- /dev/null +++ b/docs/html/tools/studio/ui-tools.jd @@ -0,0 +1,32 @@ +page.title=Android Studio User Interface Tools + +@jd:body + +

    + Android Studio provides a number of user interface tools to assist you with creating layouts, + implementing style themes, and building graphic or text resources for your app. +

    + +
    + +
    Layout Editor
    +
    Drag and drop user interface elements to build layouts for your app. +
    + +
    Theme Editor
    +
    Build re-usable user interface styles for layouts and widgets in your app. +
    + +
    Translations Editor
    +
    View and update all your string resource translations in one convenient place. +
    + +
    Vector Asset Studio
    +
    Add material icons and import Scalable Vector Graphic (SVG) files into your + Android Studio project as a drawable resource.
    + +
    Image Asset Studio
    +
    Generate custom icons for your Android applications from existing images, + clipart, or text.
    + +
    diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd old mode 100644 new mode 100755 index f03de771bdac5090ef3fb575b499db326bdff91a..fa5a8b40918be98d3779820e2b3cf8fc26e39c26 --- a/docs/html/tools/support-library/features.jd +++ b/docs/html/tools/support-library/features.jd @@ -38,6 +38,8 @@ page.title=Support Library Features Support Library Revisions
  • Support Library Setup
  • +
  • + Testing Support Library
  • @@ -140,7 +142,7 @@ page.title=Support Library Features

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v4/} directory. The library does not contain user +{@code <sdk>/extras/android/support/v4/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources.

    @@ -151,7 +153,7 @@ numbers, can cause unexpected version updates and regression incompatibilities.<

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:support-v4:21.0.0
    +com.android.support:support-v4:23.2.1
     
    @@ -167,7 +169,7 @@ com.android.support:support-v4:21.0.0

    After you download the Android Support Libraries, this library is located in the - {@code /extras/android/support/multidex/} directory. The library does not contain + {@code <sdk>/extras/android/support/multidex/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without @@ -202,8 +204,7 @@ com.android.support:multidex:1.0.0

    Note: - This library depends on the v4 Support Library. If you are using Ant or Eclipse, make sure - you include the v4 Support Library as part of this library's classpath. + This library depends on the v4 Support Library.

    Here are a few of the key classes included in the v7 appcompat library:

    @@ -228,7 +229,7 @@ com.android.support:multidex:1.0.0

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v7/appcompat/} directory. The library contains user +{@code <sdk>/extras/android/support/v7/appcompat/} directory. The library contains user interface resources. To include it in your application project, follow the instructions for Adding libraries with resources.

    @@ -236,7 +237,7 @@ resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:appcompat-v7:21.0.0
    +com.android.support:appcompat-v7:23.2.1
     
    @@ -249,7 +250,7 @@ on any app. These cards are useful for material design implementations, and are used extensively in layouts for TV apps.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v7/cardview/} directory. The library contains user interface +{@code <sdk>/extras/android/support/v7/cardview/} directory. The library contains user interface resources. To include it in your application project, follow the instructions for Adding libraries with resources.

    @@ -257,7 +258,7 @@ libraries with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:cardview-v7:21.0.0
    +com.android.support:cardview-v7:23.2.1
     
    @@ -270,7 +271,7 @@ allows you to arrange user interface elements using a grid of rectangular cells. For detailed information about the v7 gridlayout library APIs, see the {@link android.support.v7.widget android.support.v7.widget} package in the API reference.

    -

    This library is located in the {@code /extras/android/support/v7/gridlayout/} +

    This library is located in the {@code <sdk>/extras/android/support/v7/gridlayout/} directory . The library contains user interface resources. To include it in your application project, follow the instructions for Adding libraries with @@ -279,7 +280,7 @@ For detailed information about the v7 gridlayout library APIs, see the

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:gridlayout-v7:21.0.0
    +com.android.support:gridlayout-v7:23.2.1
     
    @@ -306,16 +307,14 @@ with a dependency on the v7 appcompat library, so you'll need to include both libraries in your build path when setting up your project. For more information on how to set up your project, follow the instructions in
    Adding libraries -with resources. If you are developing in Eclipse/ADT, make sure to include -the android-support-v7-mediarouter.jar, android-support-v7-appcompat.jar, -and android-support-v7-palette.jar files.

    +with resources.

    -

    If you are using Android Studio, all you need to do is specify the Gradle build -script dependency identifier com.android.support:support-v7-mediarouter:<revision>, +

    The Gradle builder script dependency identifier is as follows: +com.android.support:support-v7-mediarouter:<revision>, where "<revision>" is the minimum revision at which the library is available. For example:

    -com.android.support:mediarouter-v7:21.0.0
    +com.android.support:mediarouter-v7:23.2.1
     

    The v7 mediarouter library APIs introduced in Support Library @@ -333,7 +332,7 @@ from an album cover, and use those colors to build a color-coordinated song title card.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v7/palette/} directory. The library does not contain +{@code <sdk>/extras/android/support/v7/palette/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources.

    @@ -341,7 +340,7 @@ resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:palette-v7:21.0.0
    +com.android.support:palette-v7:23.2.1
     
    @@ -350,12 +349,12 @@ com.android.support:palette-v7:21.0.0

    The recyclerview library adds the {@link android.support.v7.widget.RecyclerView} class. This class provides support for the -RecyclerView -widget, a view for efficiently displaying large data sets by providing a +RecyclerView +widget, a view for efficiently displaying large data sets by providing a limited window of data items.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v7/recyclerview/} directory. The library contains +{@code <sdk>/extras/android/support/v7/recyclerview/} directory. The library contains user interface resources. To include it in your application project, follow the instructions for Adding libraries with resources.

    @@ -363,7 +362,7 @@ libraries with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:recyclerview-v7:21.0.0
    +com.android.support:recyclerview-v7:23.2.1
     
    @@ -384,7 +383,7 @@ such as {@link android.support.v7.preference.CheckBoxPreference} and

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v7/preference} directory. For more information +{@code <sdk>/extras/android/support/v7/preference} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -392,7 +391,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:preference-v7:23.0.0
    +com.android.support:preference-v7:23.2.1
     
    @@ -421,7 +420,7 @@ com.android.support:preference-v7:23.0.0

    Note: Use of RenderScript with the support library is supported with Android - Studio and Gradle-based builds, as well as the Eclipse plugin and Ant build tools. The + Studio and Gradle-based builds. The renderscript library is located in the build-tools/$VERSION/renderscript/ folder.

    @@ -448,7 +447,7 @@ defaultConfig {

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v13/} directory. The library does not contain user +{@code <sdk>/extras/android/support/v13/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources.

    @@ -456,7 +455,7 @@ resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:support-v13:18.0.0
    +com.android.support:support-v13:23.2.1
     
    @@ -480,7 +479,7 @@ for preference interfaces such as

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v14/} directory. The library does not contain user +{@code <sdk>/extras/android/support/v14/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources.

    @@ -488,7 +487,7 @@ resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:preference-v14:23.0.0
    +com.android.support:preference-v14:23.2.1
     
    @@ -509,7 +508,7 @@ interface and classes, such as

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v17/} directory. The library does not contain user +{@code <sdk>/extras/android/support/v17/} directory. The library does not contain user interface resources. To include it in your application project, follow the instructions for Adding libraries without resources.

    @@ -517,7 +516,7 @@ resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:preference-v17:23.0.0
    +com.android.support:preference-leanback-v17:23.2.1
     
    @@ -551,7 +550,7 @@ com.android.support:preference-v17:23.0.0

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/v17/leanback} directory. For more information +{@code <sdk>/extras/android/support/v17/leanback} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -559,7 +558,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:leanback-v17:21.0.0
    +com.android.support:leanback-v17:23.2.1
     
    @@ -572,7 +571,7 @@ package provides APIs to support adding annotation metadata to your apps.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/annotations} directory. For more information +{@code <sdk>/extras/android/support/annotations} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -580,7 +579,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:support-annotations:22.0.0
    +com.android.support:support-annotations:23.2.1
     
    @@ -597,7 +596,7 @@ snackbars, and tabs. <

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/design} directory. For more information +{@code <sdk>/extras/android/support/design} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -605,7 +604,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:design:22.2.0
    +com.android.support:design:23.2.1
     
    @@ -625,7 +624,7 @@ Callback.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/customtabs} directory. For more information +{@code <sdk>/extras/android/support/customtabs} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -633,7 +632,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:customtabs:23.0.0
    +com.android.support:customtabs:23.2.1
     
    @@ -656,7 +655,7 @@ PercentRelativeLayout.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/customtabs} directory. For more information +{@code <sdk>/extras/android/support/percent} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -664,7 +663,7 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:percent:23.0.0
    +com.android.support:percent:23.2.1
     
    @@ -686,7 +685,7 @@ RecommendationExtender.

    After you download the Android Support Libraries, this library is located in the -{@code /extras/android/support/customtabs} directory. For more information +{@code <sdk>/extras/android/support/recommendation} directory. For more information on how to set up your project, follow the instructions in Adding libraries with resources.

    @@ -694,5 +693,5 @@ with resources.

    The Gradle build script dependency identifier for this library is as follows:

    -com.android.support:app.recommendation-app:23.0.0
    +com.android.support:recommendation:23.2.1
     
    diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd index 60620e79c4c3e2d0f815663ae7d7eb5112a6ac96..1152eac4dbd85b14f691996f74e4a0dcda6bd7ff 100644 --- a/docs/html/tools/support-library/index.jd +++ b/docs/html/tools/support-library/index.jd @@ -58,11 +58,945 @@ page.title=Support Library

    This section provides details about the Support Library package releases.

    +
    +

    + Android Support Library, revision 23.2.1 (March + 2016) +

    +
    +
    +
    + Changes for v4 Support + Library: +
    -
    -

    - +

    + + +
    + Changes for v7 + appcompat library: +
    + +
    +
      +
    • Reverted dependency on vector assets so that developers using the + appcompat + library are not forced to use {@link + android.graphics.drawable.VectorDrawable} and its associated build flags. +
    • + +
    • Fixed a compatibility issue with Night Mode and API level 23. + (Issue + 201910) +
    • + +
    • Fixed a compatibility issue with {@link + android.support.v7.widget.SwitchCompat} and API level 7. (Issue 201942) +
    • + +
    • Fixed an issue with propagating configuration values in Resources objects + Issue 201928 +
    • + +
    • Fixed a compatibility issue where the {@link + android.support.v7.app.NotificationCompat.MediaStyle + NotificationCompat.MediaStyle} cancel button becomes invisible on API level + 21 and below. (Issue 202156) +
    • + +
    • Fixed a compatibility crash with {@link + android.support.v7.widget.AppCompatSpinner} on API level 21 and below. + (Issue 202246) +
    • + +
    • Fixed an issue where the {@code app:textAllCaps = "false"} style did not + work. (Issue 202117) +
    • + +
    • Fixed a crash when restoring {@link android.widget.SearchView}. + (Issue + 201836) +
    • + +
    • Fixed a memory leak that occurs when tinting drawable resources using + AppCompat. (Issue 202379) +
    • + +
    • Fixed an issue with {@link android.view.KeyEvent} on API level 11 and + lower. (Issue 202939) +
    • +
    +
    + +
    + Changes for v7 cardview + library: +
    + +
    +
      +
    • Added Night Mode support for {@link + android.support.v7.widget.CardView}. (Issue + 194497) +
    • +
    +
    + +
    + Changes for v7 + recyclerview library: +
    + +
    +
      +
    • Fixed bugs related to various measure-spec methods. (Issue + 201856) +
    • + +
    • Reduced the lockdown period in which {@link + android.support.v7.widget.RecyclerView} does not allow adapter + changes while calculating a layout or scroll. (Issue + 202046) +
    • + +
    • Fixed a crash when calling {@link + android.support.v7.widget.RecyclerView.Adapter#notifyItemChanged + notifyItemChanged()} on an item that is out of view. (Issue + 202136) +
    • + +
    • Fixed a crash that occurs when {@link + android.support.v7.widget.RecyclerView.LayoutManager + RecyclerView.LayoutManager} adds and removes a view in the same + measurement pass. (Issue + 193958) +
    • +
    +
    + +
    + Changes for v7 + mediarouter library: +
    + +
    +
      +
    • Fixed a crash that occurs when calling {@link + android.support.v7.media.MediaRouter#getInstance + MediaRouter.getInstance()} on API level 17. (Issue + 180654) +
    • +
    +
    + +
    + Changes for v17 + Leanback Library: +
    + +
    +
      +
    • Fixed an issue with {@code GridLayout.onAddFocusables()} that + caused the wrong item to be selected. +
    • + +
    • Fixed issue with {@link + android.support.v17.leanback.app.GuidedStepFragment} actions + disappearing after an action was collapsed. +
    • +
    +
    + +
    + Changes for Design Support + Library: +
    + +
    +
      +
    • Fixed a {@link android.support.design.widget.TabLayout} crash + caused by tab-pooling. (Issue + 201827) +
    • + +
    • Fixed a bug in {@link + android.support.design.widget.NavigationView} that caused the wrong + color to be selected. (Issue + 201951) +
    • + +
    • Fixed a bug where {@link + android.support.design.widget.FloatingActionButton#setBackgroundTintList + setBackgroundTintList()} was no longer able to change the background + color. (Issue + 201873) +
    • + +
    • Fixed an issue where {@link + android.support.design.widget.AppBarLayout} did not completely scroll + out of view when used with {@code android:fitsSystemWindows = “true”}. + (Issue + 201822) +
    • + +
    • Fixed an issue where {@code BottomSheetDialog} did not display + short content views correctly. (Issue + 201793) +
    • + +
    • Fixed an issue where {@code BottomSheetDialogFragment} moved + sporadically when content inside was changed. (Issue + 202125) +
    • + +
    • Fixed a crash in TextInputLayout counter link +
    • + +
    • Fixed a crash that occured when {@link + android.support.design.widget.TextInputLayout#getCounterMaxLength + TextInputLayout.getCounterMaxLength()} restored a saved state. + (Issue + 202375) +
    • + +
    • Fixed a {@code ClassCastException} that occurred when restoring a + {@link android.support.design.widget.CoordinatorLayout} using the + saved state of a view that was not a {@link + android.support.design.widget.CoordinatorLayout}. +
    • +
    +
    + +
    + Changes for VectorDrawableCompat: +
    + +
    +
      +
    • Fixed a bug where the wrong variable was read for {@code + android:tintMode}. (Issue + 201907) +
    • +
    +
    +
    +
    +
    + + +
    +

    + Android Support Library, revision 23.2.0 (February + 2016) +

    + +
    +
    +
    + Changes for v4 Support library: +
    + +
    +
      +
    • Added {@code MediaBrowserCompat} for {@link + android.media.browse.MediaBrowser} support, and {@code + MediaBrowserServiceCompat} for {@link + android.service.media.MediaBrowserService} support. This is useful + when connecting a media app’s background service with UI components, + and integrating with Android Auto and Android Wear without requiring + API level 21 or higher. +
    • + +
    • The system now calls {@link + android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} for a + nested {@link android.support.v4.app.FragmentActivity}. +
    • +
    +
    + +
    + Changes for v7 AppCompat + library: +
    + +
    +
      +
    • Added Night Mode functionality to API level 14 and higher. Switch + between Material + Light and Material Dark Themes based on the time of day or + app-specific setting. +
    • + +
    • +
        +
      • Day and night themes can be found here: {@code + <sdk>/extras/android/support/v7/appcompat/res/values/themes_daynight.xml} +
      • + +
      • {@code AppCompatDelegate.setDefaultNightMode()}: sets the + app’s default mode by passing one of the following constants: +
      • + +
      • +
          +
        • {@code MODE_NIGHT_AUTO} +
        • + +
        • {@code MODE_NIGHT_NO} +
        • + +
        • {@code MODE_NIGHT_YES} +
        • + +
        • {@code MODE_NIGHT_FOLLOW_SYSTEM} +
        • +
        +
      • + +
      • {@code AppCompatDelegate.setLocalNightMode()}: overrides + the night mode setting for the local app component. +
      • + +
      • {@code AppCompatDelegate.getDefaultNightMode()}: returns + the default night mode. +
      • +
      +
    • +
    +
    + +
    + Changes for v7 mediarouter + library: +
    + +
    +
      +
    • {@link android.support.v7.app.MediaRouteControllerDialog} now + correctly applies custom app theme colors. +
    • +
    +
    + +
    + Changes for Design support library: +
    + +
    +
      +
    • Added support for bottom + sheets. An interaction plugin, {@code + BottomSheetBehavior}, allows a child view of a {@link + android.support.design.widget.CoordinatorLayout} to act as + a bottom sheet. The base class, {@code BottomSheetCallback}, provides + callbacks to monitor bottom sheet events. +
    • +
    +
    + +
    + Changes for the CustomTabs support + library: +
    + +
    +
      +
    • + Chrome + Custom Tabs now allows apps to include a bottom bar with action + buttons in addition to the existing top action button. +
    • + +
    • {@code CustomTabsIntent.Builder.addToolBarItem()}: adds an action + button to a custom tab. You can use this to add multiple buttons. +
    • + +
    • {@code CustomTabsSession.setToolBarItem()}: updates the visuals + for toolbar items. This method will only succeed if it is given a + valid id and the browser session is in the foreground. +
    • +
    +
    + +
    + Added VectorDrawable support library: +
    + +
    +
      +
    • Added Classes: +
    • + +
    • +
        +
      • {@code VectorDrawableCompat} +
      • + +
      • {@code AnimatedVectorDrawableCompat} +
      • +
      +
    • + +
    • Adds support for {@link + android.graphics.drawable.VectorDrawable} assets to apps + running on API level 7 or higher. {@link + android.graphics.drawable.AnimatedVectorDrawable} assets + are also supported on API level 11 or higher. Vector assets can be + considerably smaller than image assets and should help reduce app + size by reducing the amount of assets required to support multiple + device screens. +
    • + +
    • This library is now a dependency of the v7 AppCompat library, allowing + developers and AppCompat + to easily use vector drawables. To use {@code + VectorDrawableCompat} within an {@link android.widget.ImageButton} or + {@link android.widget.ImageView}, use the {@code app:srcCompat} XML + attribute or {@code setImageResource()} + method. +
    • + +
    • To keep referencing attribute IDs on API level 20 or + lower, add the following {@code appt} flag to your {@code + build,gradle} file: +
    • + +
    • +
        +
      • If you are building with Android Plugin for Gradle 1.5.0 or + lower, add the following to your {@code build.gradle} file: +
      • + +
      • +
        +android {
        +  defaultConfig {
        +    // Stops the Gradle’s automatic rasterization of vectors
        +    generatedDensities = []
        +  }
        +   // Flag that tells aapt to keep the attribute ids
        +  aaptOptions {
        +    additionalParameters "--no-version-vectors"
        +  }
        +}
        +
        +
      • + +
      • If you are building with Android Plugin for Gradle 2.0.0 or + higher, add the following to your {@code build.gradle} file: +
      • + +
      • +
        +android {
        +  defaultConfig {
        +    vectorDrawables.useSupportLibrary = true
        +  }
        +}
        +
        +
      • +
      +
    • +
    +
    + +
    + Changes for v17 Leanback + Library: +
    + +
    +
      +
    • Added new capabilities to {@link android.support.v17.leanback.app.GuidedStepFragment}, + which is a component that guides users through a decision or series of decisions: +
    • + +
    • +
        +
      • Added button actions to {@link + android.support.v17.leanback.widget.GuidedAction}: +
      • + +
          +
        • {@code GuidedStepFragment.setButtonActions()}: sets a list of + {@link android.support.v17.leanback.widget.GuidedAction} buttons that + the user may select from the Actions view. +
        • +
        + + +
      • Description fields are now editable: +
      • + +
      • +
          +
        • {@code GuidedAction.Builder.descriptionEditable()}: when passing + {@code true}, sets the action’s description to be editable. +
        • + +
        • {@code GuidedAction.getEditDescription()}: returns the editable + description as a {@code CharSequence}. +
        • +
        +
      • + +
      • Added drop-down lists of sub-actions: +
      • + +
      • +
          +
        • {@code GuidedAction.setSubActions()}: sets a {@link + android.support.v17.leanback.widget.GuidedAction} list as a drop-down + menu of sub-actions. +
        • +
        +
      • +
      + +
    • Added the {@code GuidedDatePickerAction} widget for {@link + android.widget.DatePicker} functionality: +
    • + +
    • +
        +
      • The date is selected using year, month, and day columns and has a + customizable range. +
      • + +
      • {@code GuidedDatePickerAction.Builder}: builder class for the {@code + GuidedDatePickerAction} object. +
      • + +
      • {@code GuidedDatePickerAction.Builder.datePickerFormat(String + datePickerFormat)}: set the desired date format by passing the + appropriate three-character {@code String}, e.g. {@code “YMD”} or {@code + “MDY”}. Alternatively, use the {@code datePickerFormat} XML attribute. +
      • +
      +
    • +
    +
    +
    + +
    +
    + Changes for v7 RecyclerView + library: +
    + +
    +
      +
    • {@link android.support.v7.widget.RecyclerView} now has an opt-in + feature called AutoMeasure which allows {@link + android.support.v7.widget.RecyclerView.LayoutManager} to easily wrap + content or handle various measurement specifications provided by the + parent of the {@link android.support.v7.widget.RecyclerView}. It + supports all existing animation capabilities of the {@link + android.support.v7.widget.RecyclerView}. +
    • + +
    • +
        +
      • If you have a custom {@link + android.support.v7.widget.RecyclerView.LayoutManager}, + call {@code setAutoMeasureEnabled(true)} to start using the new + AutoMeasure API. All built-in {@link + android.support.v7.widget.RecyclerView.LayoutManager} objects + enable auto-measure by default. +
      • + +
      • {@link android.support.v7.widget.RecyclerView.LayoutManager} + no longer ignores some {@link + android.support.v7.widget.RecyclerView.LayoutParams} settings, + such as {@code MATCH_PARENT} in the scroll direction. +

        + Note: These lifted restrictions may cause + unexpected behavior in your layouts. Make sure you specify + the correct layout parameters. +

        +
      • +
      +
    • + +
    • When updating a {@link + android.support.v7.widget.RecyclerView.ViewHolder} with payload + information, {@link android.support.v7.widget.DefaultItemAnimator} + now disables change animations. +
    • + +
    • You can now modify the {@link + android.support.v7.widget.helper.ItemTouchHelper} escape velocity to + control swipe sensitivity. To make it easier or harder to swipe, + override {@code getSwipeEscapeVelocity(float defaultValue)} and modify {@code defaultValue}. +
    • +
    +
    +
    +
    +
    + + +
    +

    + Android Support Library, revision 23.1.1 (November 2015) +

    +
    +
    +
    Changes for v7 recyclerview library:
    +
    +
      +
    • Fixed a crash that occurs when you perform a swipe-to-dismiss action that the + {@code ItemTouchHelper} utility class provides, and then add an item. + (Issue 190500)
    • +
    +
    +
    Changes for v7 preference library:
    +
    + +
    + +
    Changes for v17 Leanback Support library:
    +
    +
      +
    • Fixed a number of internal issues in this library.
    • +
    +
    +
    Changes for Design Support library:
    +
    +
      +
    • Added the getHeaderView method to the NavigationView class.
    • +
    • Fixed a transparent background issue for a FloatingActionButton object on + devices running Android 4.0 (API level 15) and lower. + (Issue 183315)
    • +
    +
    +
    + +
    +
    + + + +
    +

    + Android Support Library, revision 23.1.0 (October 2015) +

    +
    +
    +
    Changes for v4 Support library:
    +
    +
      +
    • Added OnScrollChangedListener interface support to the + NestedScrollView widget. It + allows you to receive callbacks when the scroll X or Y positions change.
    • +
    • Added a MediaButtonReceiver class to forward received playback controls + to a service that’s managing the MediaSessionCompat class. The + MediaSessionCompat class has a + constructor that can automatically find a media button receiver in the manifest. A + media button receiver is a key part to handling + playback controls from hardware or bluetooth controls.
    • +
    +
    + + +
    Changes for v7 appcompat library:
    +
    +
      +
    • Added material design Seekbar and ImageButton widgets.
    • +
    • Updated the ImageView widget to support the tint feature.
    • +
    • Updated the look-and-feel of the SwitchCompat widget.
    • +
    +
    + +
    Changes for v7 mediarouter library:
    +
    +
      +
    • Added the following features to the MediaRouteChooserDialog class:
    • +
        +
      • Displays a loading page while discovering media route providers.
      • +
      • Includes a device type icon for easier device identification.
      • +
      • Sorts the routes according to frequency of use in the current app.
      • +
      • Supports landscape mode.
      • +
      +
    +
      +
    • Added the following features to the MediaRouteControllerDialog class:
    • +
        +
      • Recognizes screen casting and provides a proper description.
      • +
      • Supports various album art sizes and aspect ratios, and loads the art asynchronously.
      • +
      • Automatically selects the content color based on the primary color of the app.
      • +
      • Adjusts the dialog layout based on available screen space on the device.
      • +
      • Supports landscape mode.
      • +
      +
    +
    + +
    Changes for v7 palette library:
    +
    +
      +
    • Added the setRegion() method to support extracting color from a specific + region of a Bitmap object.
    • +
    +
    + +
    Changes for v7 recyclerview library:
    +
    +
      +
    • Added an improved animation API to the ItemAnimator class for better + customizations:
    • +
        +
      • Change animations no longer enforce two copies of the ViewHolder object, + which enables item content animations. Also, the ItemAnimator object + decides whether it wants to reuse the same ViewHolder object or create a + new one.
      • +
      • The new information record API gives the ItemAnimator class the flexibility + to collect data at the correct point in the layout lifecycle. This information is later + passed into the animate callbacks.
      • +
      +
    +
      +
    • Provided an easy transition plan for this backward-incompatible API change:
    • +
        +
      • If you’ve previously extended the ItemAnimator class, you can change + your base class to SimpleItemAnimator and your code should work as before. + The SimpleItemAnimator class provides the old API by wrapping the new API.
      • +
      • Some methods were removed from the ItemAnimator class. The following + code will no longer compile:
      • +
        recyclerView.getItemAnimator().setSupportsChangeAnimations(false)
        +

        You can replace it with:

        +
        +ItemAnimator animator = recyclerView.getItemAnimator();
        +if (animator instanceof SimpleItemAnimator) {
        +   ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
        +}
        +           
        +
      +
    +
    + + +
    Changes for v7, + v14, and + v17 Preference Support library:
    +
    +
      +
    • Removed APIs for controlling EditText dialogs.
    • +
    +
    + +
    Changes for v17 Leanback Support library:
    +
    +
      +
    • Added a version of the GuidedStepFragment class for the Support library + (extends android.support.v4.app.Fragment), and improved animations and + transitions.
    • +
    • Updated the GuidedStepFragment class so it can be placed on top of + existing content.
    • +
    • Added the ability to annotate different types of search completions to the + SearchFragment class.
    • +
    • Added staggered slide transition support to the VerticalGridFragment + class.
    • +
    +
    + + +
    Changes for Design Support library:
    +
    +
      +
    • Added + character counting support to the TextInputLayout widget.
    • +
    • Added edge snapping support to the AppBarLayout class by adding the + SCROLL_FLAG_SNAP constant. When scrolling ends, if the view is only + partially visible, the view is snapped and scrolled to its closest edge.
    • +
    • Added support for custom views to the NavigationView class by using the + app:actionLayout attribute or MenuItemCompat.setActionView() + method.
    • +
    +
    + + +
    Changes for Custom Tabs Support library:
    +
    +
      +
    • Added the enableUrlBarHiding() method to the CustomTabsIntent + class. It lets the client customize whether the URL bar should be hidden automatically + on scroll down.
    • +
    • Added the setActionButton() method to the CustomTabsSession + class. It lets the client + change the icon for a custom action button in an already launched custom tab.
    • +
    • Added the TAB_SHOWN and TAB_HIDDEN constants as new events + for the onNavigationEvent method of the CustomTabsCallback + class.
    • +
    +
    + +
    + +
    +
    + + +
    +

    + Android Support Library, revision 23.0.1 (September 2015) +

    +
    +
    +
    Changes for v7 and + v14 Preference Support library:
    +
    +
      +
    • Added the material design layout and style files. + (Issue 183376)
    • +
    +
    + + +
    Changes for v7 appcompat library:
    +
    +
      +
    • Fixed crash issues for the {@link android.app.Fragment} class by limiting the use of + hardware layers to Android 4.1 (API level 16) and higher. + (Issue 183896)
    • +
    • Fixed an issue where hardware buttons did not work when an activity had set the + {@link android.widget.Toolbar} class to act as the + {@link android.app.ActionBar} by using the setSupportActionBar() method. + (Issue 183334)
    • +
    • Updated the {@link android.support.v7.app.AppCompatDialogFragment} class so it + no longer throws the {@code Windows feature must be requested before adding content} + error. + (Issue 183186)
    • +
    +
    + + +
    Changes for Design Support library:
    +
    +
      +
    • Fixed the + {@link android.support.design.widget.AppBarLayout} class so it now draws correctly after + rotation. + (Issue 183109)
    • +
    • Fixed the + {@link android.support.design.widget.TabLayout} class so it now behaves correctly when a + user clicks after a swipe. + (Issue 183123)
    • +
    +
    + + +
    Changes for Custom Tabs Support library:
    +
    +
      +
    • Lowered the + minSdkVersion + value from 16 to 15 for version support.
    • +
    • Added a way to generate a + {@link android.support.customtabs.CustomTabsSessionToken} from an intent.
    • +
    +
    + +
    + +
    +
    + + +
    +

    + Android Support Library, revision 23 (August 2015)

    @@ -91,8 +1025,8 @@ page.title=Support Library
    -

    - + Android Support Library, revision 22.2.1 (July 2015)

    @@ -132,7 +1066,7 @@ page.title=Support Library

    - Android Support Library, revision 22.2.0 (May 2015)

    @@ -288,7 +1222,7 @@ page.title=Support Library

    - Android Support Library, revision 22.1.0 (April 2015)

    @@ -512,7 +1446,7 @@ page.title=Support Library

    - Android Support Library, revision 22 (March 2015)

    @@ -618,7 +1552,7 @@ page.title=Support Library

    - Android Support Library, revision 21.0.3 (December 2014)

    @@ -645,7 +1579,7 @@ page.title=Support Library

    - Android Support Library, revision 21.0.2 (November 2014)

    @@ -704,7 +1638,7 @@ page.title=Support Library

    - Android Support Library, revision 21.0.1 (November 2014)

    @@ -723,7 +1657,7 @@ page.title=Support Library

    - Android Support Library, revision 21 (October 2014)

    @@ -806,7 +1740,7 @@ page.title=Support Library

    - Android Support Library, revision 20 (July 2014)

    @@ -833,7 +1767,7 @@ page.title=Support Library

    - Android Support Library, revision 19.1.0 (March 2014)

    @@ -860,7 +1794,7 @@ page.title=Support Library

    - Android Support Library, revision 19.0.1 (December 2013)

    @@ -903,7 +1837,7 @@ page.title=Support Library

    - Android Support Library, revision 19 (October 2013)

    @@ -955,7 +1889,7 @@ page.title=Support Library

    - Android Support Library, revision 18 (July 2013)

    @@ -1056,7 +1990,7 @@ page.title=Support Library

    - Android Support Library, revision 13 (May 2013)

    @@ -1107,7 +2041,7 @@ page.title=Support Library

    - Android Support Library, revision 12 (February 2013)

    @@ -1132,7 +2066,7 @@ page.title=Support Library

    - Android Support Library, revision 11 (November 2012)

    @@ -1194,7 +2128,7 @@ page.title=Support Library

    - Android Support Library, revision 10 (August 2012)

    @@ -1213,7 +2147,7 @@ page.title=Support Library

    - Android Support Library, revision 9 (June 2012)

    @@ -1283,7 +2217,7 @@ isActiveNetworkMetered()} method.

    - Android Support Library, revision 8 (April 2012)

    @@ -1306,7 +2240,7 @@ isActiveNetworkMetered()} method.

    - Android Support Library, revision 7 (March 2012)

    @@ -1336,7 +2270,7 @@ for creating standardized system notifications.

    - Android Support Library, revision 6 (December 2011)

    @@ -1389,7 +2323,7 @@ for creating standardized system notifications.

    - Android Support Library, revision 5 (December 2011)

    @@ -1448,7 +2382,7 @@ for creating standardized system notifications.

    - Android Support Library, revision 4 (October 2011)

    @@ -1490,7 +2424,7 @@ for creating standardized system notifications.

    - Android Support Library, revision 3 (July 2011)

    @@ -1541,7 +2475,7 @@ Android 3.2 and higher (all other APIs in the v4 library are already available w

    - Android Support Library, revision 2 (May 2011)

    @@ -1561,7 +2495,7 @@ Android 3.2 and higher (all other APIs in the v4 library are already available w

    - Android Support Library, revision 1 (March 2011)

    diff --git a/docs/html/tools/support-library/setup.jd b/docs/html/tools/support-library/setup.jd old mode 100644 new mode 100755 index a39e4b0130bbe6e5875ee2d29a552df971a2f594..8fbcee156d32a8e79bad9680d440274ce0162a82 --- a/docs/html/tools/support-library/setup.jd +++ b/docs/html/tools/support-library/setup.jd @@ -72,7 +72,7 @@ Android Support Library selected.

    After downloading, the tool installs the Support Library files to your existing Android SDK directory. The library files are located in the following subdirectory of your SDK: - {@code /extras/android/support/} directory.

    + {@code <sdk>/extras/android/support/} directory.

    Choosing Support Libraries

    @@ -104,31 +104,6 @@ Android Support Library selected.

    To add a Support Library without resources to your application project:

    -
    -

    - Using Eclipse

    - -
    -
      -
    1. Make sure you have downloaded the Android Support Library - using the SDK Manager.
    2. -
    3. Create a {@code libs/} directory in the root of your application project.
    4. -
    5. Copy the JAR file from your Android SDK installation directory (e.g., - {@code /extras/android/support/v4/android-support-v4.jar}) into your - application's project {@code libs/} directory. -
    6. Right click the JAR file and select Build Path > Add to Build Path. -
    7. -
    -
    -
    - -
    -

    - Using Android Studio

    - -
    1. Make sure you have downloaded the Android Support Repository using the SDK Manager.
    2. @@ -143,8 +118,6 @@ dependencies {
    -
    -

    Adding libraries with resources

    @@ -153,77 +126,6 @@ dependencies { v7 appcompat for action bar) to your application project:

    -
    -

    - Using Eclipse

    -
    - -

    Create a library -project based on the support library code:

    - -
      -
    1. Make sure you have downloaded the Android Support Library - using the SDK Manager.
    2. -
    3. Create a library project and ensure the required JAR files are included in the project's - build path: -
        -
      1. Select File > Import.
      2. -
      3. Select Existing Android Code Into Workspace and click - Next.
      4. -
      5. Browse to the SDK installation directory and then to the Support Library folder. - For example, if you are adding the {@code appcompat} project, browse to - <sdk>/extras/android/support/v7/appcompat/.
      6. -
      7. Click Finish to import the project. For the v7 appcompat project, you - should now see a new project titled android-support-v7-appcompat.
      8. -
      9. In the new library project, expand the {@code libs/} folder, right-click each {@code .jar} - file and select Build Path > Add to Build Path. For example, when - creating the the v7 appcompat project, add both the {@code android-support-v4.jar} and - {@code android-support-v7-appcompat.jar} files to the build path.
      10. -
      11. Right-click the library project folder and select Build Path > Configure - Build Path.
      12. -
      13. In the Order and Export tab, check the .jar files you just - added to the build path, so they are available to projects that depend on this library - project. For example, the {@code appcompat} project requires you to export both the - {@code android-support-v4.jar} and {@code android-support-v7-appcompat.jar} files.
      14. -
      15. Uncheck Android Dependencies.
      16. -
      17. Click OK to complete the changes.
      18. -
      -
    4. -
    - -

    You now have a library project for your selected Support Library that you can use with one or - more application projects.

    - -

    Add the library to your application project:

    -
      -
    1. In the Project Explorer, right-click your project and select Properties.
    2. -
    3. In the category panel on the left side of the dialog, select Android.
    4. -
    5. In the Library pane, click the Add button.
    6. -
    7. Select the library project and click OK. For example, the - {@code appcompat} project should be listed as android-support-v7-appcompat. -
    8. -
    9. In the properties window, click OK.
    10. -
    - -

    - Note: If you are using the {@code android-support-v7-mediarouter} support - library, you should note that it depends on the {@code android-support-v7-appcompat} library. - In order for the v7 mediarouter library to compile, you must import both library projects into - your development workspace. Then follow the procedure above to add the v7 appcompat project as a - library to the v7 mediarouter library project. -

    - -
    -
    - - -
    -

    - Using Android Studio

    - -
    1. Make sure you have downloaded the Android Support Repository using the SDK Manager.
    2. @@ -240,8 +142,6 @@ dependencies {
    -
    -

    Using Support Library APIs

    @@ -333,9 +233,9 @@ APIs. The code is included in the download from the SDK Manager and is placed in SDK installation directory, as listed below:

      -
    • 4v Samples: {@code /extras/android/support/samples/Support4Demos/}
    • -
    • 7v Samples: {@code /extras/android/support/samples/Support7Demos/}
    • -
    • 13v Samples: {@code /extras/android/support/samples/Support13Demos/}
    • -
    • App Navigation: {@code /extras/android/support/samples/SupportAppNavigation/}
    • +
    • 4v Samples: {@code <sdk>/extras/android/support/samples/Support4Demos/}
    • +
    • 7v Samples: {@code <sdk>/extras/android/support/samples/Support7Demos/}
    • +
    • 13v Samples: {@code <sdk>/extras/android/support/samples/Support13Demos/}
    • +
    • App Navigation: {@code <sdk>/extras/android/support/samples/SupportAppNavigation/}
    diff --git a/docs/html/tools/testing-support-library/index.jd b/docs/html/tools/testing-support-library/index.jd index 97c2dcf5458f255b60792ce1eef39e3287a8f84f..35a3c7db8f2ced09763e11a334588922678a8eef 100644 --- a/docs/html/tools/testing-support-library/index.jd +++ b/docs/html/tools/testing-support-library/index.jd @@ -1,4 +1,5 @@ page.title=Testing Support Library +page.image=images/tools/studio-test-module.png @jd:body @@ -585,13 +586,13 @@ allAppsButton.clickAndWaitForNewWindow();
     dependencies {
    -  androidTestCompile 'com.android.support.test:runner:0.3'
    +  androidTestCompile 'com.android.support.test:runner:0.4'
       // Set this dependency to use JUnit 4 rules
    -  androidTestCompile 'com.android.support.test:rules:0.3'
    +  androidTestCompile 'com.android.support.test:rules:0.4'
       // Set this dependency to build and run Espresso tests
    -  androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2'
    +  androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
       // Set this dependency to build and run UI Automator tests
    -  androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
    +  androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
     }

    To set @@ -628,4 +629,4 @@ android {

    For more information about Android Studio and to download it, see Download Android Studio and SDK Tools. -

    \ No newline at end of file +

    diff --git a/docs/html/tools/testing/activity_test.jd b/docs/html/tools/testing/activity_test.jd deleted file mode 100644 index 036407a02eff25b4d0e5e78e1fa474233b95d09d..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/activity_test.jd +++ /dev/null @@ -1,1321 +0,0 @@ -page.title=Activity Testing Tutorial -parent.title=Testing -parent.link=index.html -@jd:body -
    -
    -

    In this document

    -
      -
    1. - Prerequisites -
    2. -
    3. - Installing the Tutorial Sample Code -
    4. -
    5. - Setting Up the Emulator -
    6. -
    7. - Setting Up the Projects -
    8. -
    9. - Creating the Test Case Class -
        -
      1. - Adding the test case class file -
      2. -
      3. - Adding the test case constructor -
      4. -
      5. - Adding the setup method -
      6. -
      7. - Adding an initial conditions test -
      8. -
      9. - Adding a UI test -
      10. -
      11. - Adding state management tests -
      12. -
      -
    10. -
    11. - Running the Tests and Seeing the Results -
    12. -
    13. - Forcing Some Tests to Fail -
    14. -
    15. - Next Steps -
    16. -
    -

    Appendix

    -
      -
    1. - Installing the Completed Test Application File -
    2. -
    3. - For Users Not Developing In Eclipse -
    4. -
    -

    See Also

    -
      -
    1. - Testing Fundamentals -
    2. -
    3. - {@link android.test.ActivityInstrumentationTestCase2} -
    4. -
    5. - {@link junit.framework.Assert} -
    6. -
    7. - {@link android.test.InstrumentationTestRunner} -
    8. -
    -
    -
    -

    - Android includes powerful tools for testing applications. The tools extend JUnit with additional features, provide convenience classes for mock Android system objects, and use - instrumentation to give you control over your main application while you are testing it. The entire Android testing environment is discussed in the document - Testing Fundamentals. -

    -

    - This tutorial demonstrates the Android testing tools by presenting a simple Android application and then leading you step-by-step through the creation of a test application for it. - The test application demonstrates these key points: -

    -
      -
    • - An Android test is itself an Android application that is linked to the application under test by entries in its AndroidManifest.xml file. -
    • -
    • - Instead of Android components, an Android test application contains one or more test cases. Each of these is a separate class definition. -
    • -
    • - Android test case classes extend the JUnit {@link junit.framework.TestCase} class. -
    • -
    • - Android test case classes for activities extend JUnit and also connect you to the application under test with instrumentation. You can send keystroke or touch events directly to the UI. -
    • -
    • - You choose an Android test case class based on the type of component (application, activity, content provider, or service) you are testing. -
    • -
    • - Additional test tools in Eclipse/ADT provide integrated support for creating test applications, running them, and viewing the results. -
    • -
    -

    - The test application contains methods that perform the following tests: -

    -
      -
    • - Initial conditions test. Tests that the application under test initializes correctly. This is also a unit test of the application's - {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method. Testing initial conditions also provides a confidence measure for subsequent tests. -
    • -
    • - UI test. Tests that the main UI operation works correctly. This test demonstrates the instrumentation features available in activity testing. - It shows that you can automate UI tests by sending key events from the test application to the main application. -
    • -
    • - State management tests. Test the application's code for saving state. This test demonstrates the instrumentation features of the test runner, which - are available for testing any component. -
    • -
    -

    Prerequisites

    -

    - The instructions and code in this tutorial depend on the following: -

    -
      -
    • - Basic knowledge of Android programming. If you haven't yet written an Android application, - do the class - Building Your First App. - If you want to learn more about Spinner, the application under test, then you - might want to review the "Spinner" sample app. -
    • -
    • - Some familiarity with the Android testing framework and concepts. If you haven't explored - Android testing yet, start by reading the - Testing Fundamentals - guide. -
    • -
    • - Eclipse with ADT. This tutorial describes how to set up and run a test application using - Eclipse with ADT. If you haven't yet installed Eclipse and the ADT plugin, - follow the steps in Installing the SDK - to install them before continuing. If you are not developing in Eclipse, you will - find instructions for setting up and running the test application in the - appendix of this document. -
    • -
    -

    Installing the Tutorial Sample Code

    -

    - During this tutorial, you will be working with sample code that is provided as part - of the downloadable Samples component of the SDK. Specifically, you will be working - with a pair of related sample applications — an application under test and a test - application: -

    -
      -
    • - Spinner is the application under test. This tutorial focuses on the - common situation of writing tests for an application that already exists, so the main - application is provided to you. -
    • -
    • - SpinnerTest is the test application. In the tutorial, you create this application - step-by-step. If you want to run quickly through the tutorial, - you can install the completed SpinnerTest application first, and then follow the - text. You may get more from the tutorial, however, if you create the test application - as you go. The instructions for installing the completed test application are in the - section - Installing the Completed Test Application File. -
    • -
    -

    - The sample applications are described in more detail in the - Samples topic. Follow the instructions to - download the version of the samples that's appropriate for the platform you're working with. -

    -

    Setting Up the Emulator

    -

    - In this tutorial, you will use the Android emulator to run applications. The emulator needs - an Android Virtual Device (AVD) with an API level equal to or higher than the one you set for the projects in the previous step. - To find out how to check this and create the right AVD if necessary, - see Creating an AVD. -

    -

    - As a test of the AVD and emulator, run the SpinnerActivity application in Eclipse with ADT. When it starts, - click the large downward-pointing arrow to the right of the spinner text. You see the spinner expand and display the title "Select a planet" at the top. - Click one of the other planets. The spinner closes, and your selection appears below it on the screen. -

    -

    Setting Up the Projects

    -

    - When you are ready to get started with the tutorial, begin by setting up Eclipse projects for - both Spinner (the application under test) and SpinnerTest (the test application). -

    -

    - You'll be using the Spinner application as-is, without modification, so you'll be loading it - into Eclipse as a new Android project from existing source. In the process, you'll be - creating a new test project associated with Spinner that will contain the SpinnerTest - application. The SpinnerTest application will be completely new and you'll be - using the code examples in this tutorial to add test classes and tests to it. -

    -

    - To install the Spinner app in a new Android project from existing source, following these steps: -

    -
      -
    1. - In Eclipse, select File > New > Project > Android > Android Project, - then click Next. The New Android Project dialog appears. -
    2. -
    3. - In the Project name text box, enter "SpinnerActivity". The Properties area is filled in automatically. -
    4. -
    5. - In the Contents area, set "Create project from existing source". -
    6. -
    7. - For Location, click Browse, navigate to the directory <SDK_path>/samples/android-8/Spinner, - then click Open. The directory name <SDK_path>/samples/android-8/Spinner now appears in the Location text box. -
    8. -
    9. - In the Build Target area, set a API level of 3 or higher. If you are already developing with a particular target, and it is API level 3 or higher, then use that target. -
    10. -
    11. - In the Properties area, in the Min SDK Version:, enter "3". -
    12. -
    13. - You should now see these values: -
        -
      • Project Name: "SpinnerActivity"
      • -
      • Create project from existing source: set
      • -
      • Location: "<SDK_path>/samples/android-8/Spinner"
      • -
      • Build Target: "API level of 3 or higher" (Target Name "Android 1.5 or higher")
      • -
      • Package name: (disabled, set to "com.android.example.spinner")
      • -
      • Create Activity: (disabled, set to ".SpinnerActivity")
      • -
      • Min SDK Version: "3"
      • -
      -

      - The following screenshot summarizes these values: -

      - - New Android Project dialog with filled-in values - - -
    14. -
    -

    - To create a new test project for the SpinnerTest application, follow these steps: -

    -
      -
    1. - Click Next. The New Android Test Project dialog appears. -
    2. -
    3. - Set "Create a Test Project". -
    4. -
    5. - Leave the other values unchanged. The result should be: -
        -
      • Create a Test Project: checked
      • -
      • Test Project Name: "SpinnerActivityTest"
      • -
      • Use default location: checked (this should contain the directory name "workspace/SpinnerActivityTest").
      • -
      • Build Target: Use the same API level you used in the previous step.
      • -
      • Application name: "SpinnerActivityTest"
      • -
      • Package name: "com.android.example.spinner.test"
      • -
      • Min SDK Version: "3"
      • -
      -

      - The following screenshot summarizes these values: -

      - - New Android Test Project dialog with filled-in values - -
    6. -
    7. - Click Finish. Entries for SpinnerActivity and SpinnerActivityTest should appear in the - Package Explorer. -

      - Note: If you set Build Target to an API level higher than "3", you will see the warning - "The API level for the selected SDK target does not match the Min SDK version". You do not need to change the API level or the Min SDK version. - The message tells you that you are building the projects with one particular API level, but specifying that a lower API level is required. This may - occur if you have chosen not to install the optional earlier API levels. -

      -

      - If you see errors listed in the Problems pane at the bottom of the Eclipse window, or if a red error marker appears next to - the entry for SpinnerActivity in the Package Explorer, highlight the SpinnerActivity entry and then select - Project > Clean. This should fix any errors. -

      -
    8. -
    -

    - You now have the application under test in the SpinnerActivity project, - and an empty test project in SpinnerActivityTest. You may - notice that the two projects are in different directories, but Eclipse with - ADT handles this automatically. You should have no problem in either building or running them. -

    -

    - Notice that Eclipse and ADT have already done some initial setup for your test application. - Expand the SpinnerActivityTest project, and notice that it already has an - Android manifest file AndroidManifest.xml. - Eclipse with ADT created this when you added the test project. - Also, the test application is already set up to use instrumentation. You can see this - by examining AndroidManifest.xml. - Open it, then at the bottom of the center pane click AndroidManifest.xml - to display the XML contents: -

    -
    -<?xml version="1.0" encoding="utf-8"?>
    -<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    -      package="com.android.example.spinner.test"
    -      android:versionCode="1"
    -      android:versionName="1.0">
    -    <uses-sdk android:minSdkVersion="3" />
    -    <instrumentation
    -        android:targetPackage="com.android.example.spinner"
    -        android:name="android.test.InstrumentationTestRunner" />
    -    <application android:icon="@drawable/icon" android:label="@string/app_name">
    -        <uses-library android:name="android.test.runner" />
    -        ...
    -    </application>
    -</manifest>
    -
    -

    - Notice the <instrumentation> element. The attribute - android:targetPackage="com.android.example.spinner" tells Android that the - application under test is defined in the Android package - com.android.example.spinner. Android now knows to use that - package's AndroidManifest.xml file to launch the application under test. - The <instrumentation> element also contains the attribute - android:name="android.test.InstrumentationTestRunner", which tells Android - instrumentation to run the test application with Android's instrumentation-enabled test runner. -

    -

    Creating the Test Case Class

    - -

    - You now have a test project SpinnerActivityTest, and the basic structure of a test - application also called SpinnerActivityTest. The basic structure includes all the files and - directories you need to build and run a test application, except for the class that - contains your tests (the test case class). -

    -

    - The next step is to define the test case class. In this tutorial, you'll be creating a - test case class that includes: -

    -
      -
    • - Test setup. This use of the JUnit {@link junit.framework.TestCase#setUp() setUp()} - method demonstrates some of the tasks you might perform before running an Android test. -
    • -
    • - Testing initial conditions. This test demonstrates a good testing technique. - It also demonstrates that with Android instrumentation you can look at the application - under test before the main activity starts. The test checks that the application's - important objects have been initialized. - If the test fails, you then know that any other tests against the application are - unreliable, since the application was running in an incorrect state. -

      - Note: The purpose of testing initial conditions is not the same as - using setUp(). The JUnit {@link junit.framework.TestCase#setUp()} runs once - before each test method, and its purpose is to create a clean test - environment. The initial conditions test runs once, and its purpose is to verify that the - application under test is ready to be tested. -

      -
    • -
    • - Testing the UI. This test shows how to control the main application's UI - with instrumentation, a powerful automation feature of Android testing. -
    • -
    • - Testing state management. This test shows some techniques for testing how - well the application maintains state in the Android environment. Remember that to - provide a satisfactory user experience, your application must never lose its current state, - even if it's interrupted by a phone call or destroyed because of memory constraints. - The Android activity lifecycle provides ways to maintain state, and the - SpinnerActivity application uses them. The test shows the techniques for - verifying that they work. -
    • -
    -

    - Android tests are contained in a special type of Android application that contains one or more test class definitions. Each of these contains - one or more test methods that do the actual tests. In this tutorial, you will first add a test case class, and then add tests to it. -

    -

    - You first choose an Android test case class to extend. You choose from the base test case classes according to the Android component you are testing and the types of tests you are doing. - In this tutorial, the application under test has a single simple activity, so the test case class will be for an Activity component. Android offers several, but the one that tests in - the most realistic environment is {@link android.test.ActivityInstrumentationTestCase2}, so you will use it as the base class. Like all activity test case classes, - ActivityInstrumentationTestCase2 offers convenience methods for interacting directly with the UI of the application under test. -

    -

    Adding the test case class file

    -

    - To add ActivityInstrumentationTestCase2 as the base test case class, follow these steps: -

    -
      -
    1. - In the Package Explorer, expand the test project SpinnerActivityTest if it is not open already. -
    2. -
    3. - Within SpinnerActivityTest, expand the src/ folder and then the package marker for - com.android.example.spinner.test. Right-click on the package name and select New > Class:
      - - Menu for creating a new class in the test application - -

      - The New Java Class wizard appears: -

      - - New Java Class wizard dialog - -
    4. -
    5. - In the wizard, enter the following: -
        -
      • - Name: "SpinnerActivityTest". This becomes the name of your test class. -
      • -
      • - Superclass: "android.test.ActivityInstrumentationTestCase2<SpinnerActivity>". The superclass is parameterized, so - you have to provide it your main application's class name. -
      • -
      -

      - Do not change any of the other settings. Click Finish. -

      -
    6. -
    7. - You now have a new file SpinnerActivityTest.java in the project. -
    8. -
    9. - To resolve the reference to SpinnerActivity, add the following import: -
      -import com.android.example.spinner.SpinnerActivity;
      -
      -
    10. -
    -

    Adding the test case constructor

    -

    - To ensure that the test application is instantiated correctly, you must set up a constructor that the test - runner will call when it instantiates your test class. This constructor has no parameters, and its sole - purpose is to pass information to the superclass's default constructor. To set up this constructor, enter the - following code in the class: -

    -
    -  public SpinnerActivityTest() {
    -    super(SpinnerActivity.class);
    -  } // end of SpinnerActivityTest constructor definition
    -
    -

    - This calls the superclass constructor with the main activity's class (SpinnerActivity.class) for the application under test. Android uses this information to find the application and activity to test. -

    -

    - You are now ready to add tests, by adding test methods to the class. -

    -

    Adding the setup method

    -

    - The setUp() method is invoked before every test. You use it to initialize variables and clean up from previous tests. You can also use - the JUnit {@link junit.framework.TestCase#tearDown() tearDown()} method, which runs after every test method. The tutorial does not use it. -

    -

    - The method you are going to add does the following: -

    -
      -
    • - super.setUp(). Invokes the superclass constructor for setUp(), which is required by JUnit. -
    • -
    • - Calls {@link android.test.ActivityInstrumentationTestCase2#setActivityInitialTouchMode(boolean) setActivityInitialTouchMode(false)}. - This turns off touch mode in the device or emulator. If any of your test methods send key events to the application, - you must turn off touch mode before you start any activities; otherwise, the call is ignored. -
    • -
    • - Stores references to system objects. Retrieves and stores a reference to the activity under test, the Spinner - widget used by the activity, the SpinnerAdapter that backs the widget, and the string value of the selection that is - set when the application is first installed. These objects are used in the state management test. The methods invoked are: -
        -
      • - {@link android.test.ActivityInstrumentationTestCase2#getActivity()}. Gets a reference to the activity under test (SpinnerActivity). - This call also starts the activity if it is not already running. -
      • -
      • - {@link android.app.Activity#findViewById(int)}. Gets a reference to the Spinner widget of the application under test. -
      • -
      • - {@link android.widget.AbsSpinner#getAdapter()}. Gets a reference to the adapter (an array of strings) backing the spinner. -
      • -
      -
    • -
    -

    - Add this code to the definition of SpinnerActivityTest, after the constructor definition: -

    -
    -  @Override
    -  protected void setUp() throws Exception {
    -    super.setUp();
    -
    -    setActivityInitialTouchMode(false);
    -
    -    mActivity = getActivity();
    -
    -    mSpinner =
    -      (Spinner) mActivity.findViewById(
    -        com.android.example.spinner.R.id.Spinner01
    -      );
    -
    -      mPlanetData = mSpinner.getAdapter();
    -
    -  } // end of setUp() method definition
    -
    -

    - Add these members to the test case class: -

    -
    -  private SpinnerActivity mActivity;
    -  private Spinner mSpinner;
    -  private SpinnerAdapter mPlanetData;
    -
    -

    - Add these imports: -

    -
    -import android.widget.Spinner;
    -import android.widget.SpinnerAdapter;
    -
    -

    - You now have the complete setUp() method. -

    -

    Adding an initial conditions test

    -

    - The initial conditions test verifies that the application under test is initialized correctly. It is an illustration of the types of tests you can run, so it is not comprehensive. - It verifies the following: -

    -
      -
    • - The item select listener is initialized. This listener is called when a selection is made from the spinner. -
    • -
    • - The adapter that provides values to the spinner is initialized. -
    • -
    • - The adapter contains the right number of entries. -
    • -
    -

    - The actual initialization of the application under test is done in setUp(), which the test runner calls automatically before every test. The verifications are - done with JUnit {@link junit.framework.Assert} calls. As a useful convention, the method name is testPreConditions(): -

    -
    -  public void testPreConditions() {
    -    assertTrue(mSpinner.getOnItemSelectedListener() != null);
    -    assertTrue(mPlanetData != null);
    -    assertEquals(mPlanetData.getCount(),ADAPTER_COUNT);
    -  } // end of testPreConditions() method definition
    -
    -

    - Add this member: -

    -
    -  public static final int ADAPTER_COUNT = 9;
    -
    -

    Adding a UI test

    -

    - Now create a UI test that selects an item from the Spinner widget. The test sends key events to the UI with key events. - The test confirms that the selection matches the result you expect. -

    -

    - This test demonstrates the power of using instrumentation in Android testing. Only an instrumentation-based test class allows you to send key events (or touch events) - to the application under test. With instrumentation, you can test your UI without having to take screenshots, record the screen, or do human-controlled testing. -

    -

    - To work with the spinner, the test has to request focus for it and then set it to a known position. The test uses {@link android.view.View#requestFocus() requestFocus()} and - {@link android.widget.AbsSpinner#setSelection(int) setSelection()} to do this. Both of these methods interact with a View in the application under test, so you have to call them - in a special way. -

    -

    - Code in a test application that interacts with a View of the application under test must run in the main application's thread, also - known as the UI thread. To do this, you use the {@link android.app.Activity#runOnUiThread(java.lang.Runnable) Activity.runOnUiThread()} - method. You pass the code to runOnUiThread()in an anonymous {@link java.lang.Runnable Runnable} object. To set - the statements in the Runnable object, you override the object's {@link java.lang.Runnable#run()} method. -

    -

    - To send key events to the UI of the application under test, you use the sendKeys() method. - This method does not have to run on the UI thread, since Android uses instrumentation to pass the key events to the application under test. -

    -

    - The last part of the test compares the selection made by sending the key events to a pre-determined value. This tests that the spinner is working as intended. -

    -

    - The following sections show you how to add the code for this test. -

    -
      -
    1. - Get focus and set selection. Create a new method public void testSpinnerUI(). Add - code to to request focus for the spinner and set its position to default or initial position, "Earth". This code is run on the UI thread of - the application under test: -
      -  public void testSpinnerUI() {
      -
      -    mActivity.runOnUiThread(
      -      new Runnable() {
      -        public void run() {
      -          mSpinner.requestFocus();
      -          mSpinner.setSelection(INITIAL_POSITION);
      -        } // end of run() method definition
      -      } // end of anonymous Runnable object instantiation
      -    ); // end of invocation of runOnUiThread
      -
      -

      - Add the following member to the test case class. -

      -
      -  public static final int INITIAL_POSITION = 0;
      -
      -
    2. -
    3. - Make a selection. Send key events to the spinner to select one of the items. To do this, open the spinner by - "clicking" the center keypad button (sending a DPAD_CENTER key event) and then clicking (sending) the down arrow keypad button five times. Finally, - click the center keypad button again to highlight the desired item. Add the following code: -
      -    this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
      -    for (int i = 1; i <= TEST_POSITION; i++) {
      -      this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
      -    } // end of for loop
      -
      -    this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
      -
      -

      - Add the following member to the test case class: -

      -
      -  public static final int TEST_POSITION = 5;
      -
      -

      - This sets the final position of the spinner to "Saturn" (the spinner's backing adapter is 0-based). -

      -
    4. -
    5. - Check the result. Query the current state of the spinner, and compare its current selection to the expected value. - Call the method {@link android.widget.AdapterView#getSelectedItemPosition() getSelectedItemPosition()} to find out the current selection position, and then - {@link android.widget.AdapterView#getItemAtPosition(int) getItemAtPosition()} to get the object corresponding to that position (casting it to a String). Assert that - this string value matches the expected value of "Saturn": -
      -    mPos = mSpinner.getSelectedItemPosition();
      -    mSelection = (String)mSpinner.getItemAtPosition(mPos);
      -    TextView resultView =
      -      (TextView) mActivity.findViewById(
      -        com.android.example.spinner.R.id.SpinnerResult
      -      );
      -
      -    String resultText = (String) resultView.getText();
      -
      -    assertEquals(resultText,mSelection);
      -
      -  } // end of testSpinnerUI() method definition
      -
      -

      - Add the following members to the test case class: -

      -
      -  private String mSelection;
      -  private int mPos;
      -
      -

      - Add the following imports to the test case class: -

      -
      -  import android.view.KeyEvent;
      -  import android.widget.TextView;
      -
      -
    6. -
    -

    - Pause here to run the tests you have. The procedure for running a test application is different - from running a regular Android application. You run a test application as an Android JUnit - application. To see how to do this, see Running the Tests and Seeing the Results. -

    -

    - Eventually, you will see the SpinnerActivity application start, and the test - application controlling it by sending it key events. You will also see a new - JUnit view in the Explorer pane, showing the results of the - test. The JUnit view is documented in a following section, - Running the Test and Seeing the Results. -

    -

    Adding state management tests

    -

    - You now write two tests that verify that SpinnerActivity maintains its state when it is paused or terminated. - The state, in this case, is the current selection in the spinner. When users make a selection, - pause or terminate the application, and then resume or restart it, they should see - the same selection. -

    -

    - Maintaining state is an important feature of an application. Users may switch from the current - application temporarily to answer the phone, and then switch back. Android may decide to - terminate and restart an activity to change the screen orientation, or terminate an unused - activity to regain storage. In each case, users are best served by having the UI return to its - previous state (except where the logic of the application dictates otherwise). -

    -

    - SpinnerActivity manages its state in these ways: -

    -
      -
    • - Activity is hidden. When the spinner screen (the activity) is running but hidden by some other screen, it - stores the spinner's position and value in a form that persists while the application is running. -
    • -
    • - Application is terminated. When the activity is terminated, it stores the spinner's position and value in - a permanent form. The activity can read the position and value when it restarts, and restore the spinner to its previous state. -
    • -
    • - Activity re-appears. When the user returns to the spinner screen, the previous selection is restored. -
    • -
    • - Application is restarted. When the user starts the application again, the previous selection is restored. -
    • -
    -

    - Note: An application can manage its state in other ways as well, but these are - not covered in this tutorial. -

    -

    - When an activity is hidden, it is paused. When it re-appears, it - resumes. Recognizing that these are key points in an activity's life cycle, - the Activity class provides two callback methods {@link android.app.Activity#onPause()} and - {@link android.app.Activity#onResume()} for handling pauses and resumes. - SpinnerActivity uses them for code that saves and restores state. -

    -

    - Note: If you would like to learn more about the difference between losing - focus/pausing and killing an application, - read about the activity -lifecycle. -

    -

    - The first test verifies that the spinner selection is maintained after the entire application is shut down and then restarted. The test uses instrumentation to - set the spinner's variables outside of the UI. It then terminates the activity by calling {@link android.app.Activity#finish() Activity.finish()}, and restarts it - using the instrumentation method {@link android.test.ActivityInstrumentationTestCase2#getActivity()}. The test then asserts that the current spinner state matches - the test values. -

    -

    - The second test verifies that the spinner selection is maintained after the activity is paused and then resumed. The test uses instrumentation to - set the spinner's variables outside of the UI and then force calls to the onPause() and onResume() methods. The test then - asserts that the current spinner state matches the test values. -

    -

    - Notice that these tests make limited assumptions about the mechanism by which the activity manages state. The tests use the activity's getters and - setters to control the spinner. The first test also knows that hiding an activity calls onPause(), and bringing it back to the foreground - calls onResume(). Other than this, the tests treat the activity as a "black box". -

    -

    - To add the code for testing state management across shutdown and restart, follow these steps: -

    -
      -
    1. - Add the test method testStateDestroy(), then - set the spinner selection to a test value: -
      -  public void testStateDestroy() {
      -    mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
      -    mActivity.setSpinnerSelection(TEST_STATE_DESTROY_SELECTION);
      -
      -
    2. -
    3. - Terminate the activity and restart it: -
      -    mActivity.finish();
      -    mActivity = this.getActivity();
      -
      -
    4. -
    5. - Get the current spinner settings from the activity: -
      -    int currentPosition = mActivity.getSpinnerPosition();
      -    String currentSelection = mActivity.getSpinnerSelection();
      -
      -
    6. -
    7. - Test the current settings against the test values: -
      -    assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
      -    assertEquals(TEST_STATE_DESTROY_SELECTION, currentSelection);
      -  } // end of testStateDestroy() method definition
      -
      -

      - Add the following members to the test case class: -

      -  public static final int TEST_STATE_DESTROY_POSITION = 2;
      -  public static final String TEST_STATE_DESTROY_SELECTION = "Earth";
      -
      -
    8. -
    -

    - To add the code for testing state management across a pause and resume, follow these steps: -

    -
      -
    1. - Add the test method testStatePause(): -
      -    @UiThreadTest
      -    public void testStatePause() {
      -
      -

      - The @UiThreadTest annotation tells Android to build this method so that it runs - on the UI thread. This allows the method to change the state of the spinner widget in the - application under test. This use of @UiThreadTest shows that, if necessary, you - can run an entire method on the UI thread. -

      -
    2. -
    3. - Set up instrumentation. Get the instrumentation object - that is controlling the application under test. This is used later to - invoke the onPause() and onResume() methods: -
      -    Instrumentation mInstr = this.getInstrumentation();
      -
      -
    4. -
    5. - Set the spinner selection to a test value: -
      -    mActivity.setSpinnerPosition(TEST_STATE_PAUSE_POSITION);
      -    mActivity.setSpinnerSelection(TEST_STATE_PAUSE_SELECTION);
      -
      -
    6. -
    7. - Use instrumentation to call the Activity's onPause(): -
      -    mInstr.callActivityOnPause(mActivity);
      -
      -

      - Under test, the activity is waiting for input. The invocation of - {@link android.app.Instrumentation#callActivityOnPause(android.app.Activity)} - performs a call directly to the activity's onPause() instead - of manipulating the activity's UI to force it into a paused state. -

      -
    8. -
    9. - Force the spinner to a different selection: -
      -    mActivity.setSpinnerPosition(0);
      -    mActivity.setSpinnerSelection("");
      -
      -

      - This ensures that resuming the activity actually restores the - spinner's state rather than simply leaving it as it was. -

      -
    10. -
    11. - Use instrumentation to call the Activity's onResume(): -
      -    mInstr.callActivityOnResume(mActivity);
      -
      -

      - Invoking {@link android.app.Instrumentation#callActivityOnResume(android.app.Activity)} - affects the activity in a way similar to callActivityOnPause. The - activity's onResume() method is invoked instead of manipulating the - activity's UI to force it to resume. -

      -
    12. -
    13. - Get the current state of the spinner: -
      -    int currentPosition = mActivity.getSpinnerPosition();
      -    String currentSelection = mActivity.getSpinnerSelection();
      -
      -
    14. -
    15. - Test the current spinner state against the test values: -
      -    assertEquals(TEST_STATE_PAUSE_POSITION,currentPosition);
      -    assertEquals(TEST_STATE_PAUSE_SELECTION,currentSelection);
      -  } // end of testStatePause() method definition
      -
      -

      - Add the following members to the test case class: -

      -
      -  public static final int TEST_STATE_PAUSE_POSITION = 4;
      -  public static final String TEST_STATE_PAUSE_SELECTION = "Jupiter";
      -
      -
    16. -
    17. - Add the following imports: -
      -  import android.app.Instrumentation;
      -  import android.test.UiThreadTest;
      -
      -
    18. -
    -

    Running the Tests and Seeing the Results

    -

    - The most simple way to run the SpinnerActivityTest test case is to run it directly from the Package Explorer. -

    -

    - To run the SpinnerActivityTest test, follow these steps: -

    -
      -
    1. - In the Package Explorer, right-click the project SpinnerActivityTest at the top level, and then - select Run As > Android JUnit Test:
      - - Menu to run a test as an Android JUnit test - -
    2. -
    3. - You will see the emulator start. When the unlock option is displayed (its appearance depends on the API level you specified for the AVD), - unlock the home screen. -
    4. -
    5. - The test application starts. You see a new tab for the JUnit view, next to the Package Explorer tab:
      - - The JUnit window - -
    6. -
    -

    - This view contains two sub-panes. The top pane summarizes the tests that were run, and the bottom pane shows failure traces for - highlighted tests. -

    -

    - At the conclusion of a successful test run, this is the view's appearance:
    - - JUnit test run success - -

    -

    - The upper pane summarizes the test: -

    -
      -
    • - Total time elapsed for the test application(labeled Finished after <x> seconds). -
    • -
    • - Number of runs (Runs:) - the number of tests in the entire test class. -
    • -
    • - Number of errors (Errors:) - the number of program errors and exceptions encountered during - the test run. -
    • -
    • - Number of failures (Failures:) - the number of test failures encountered during the test - run. This is the number of assertion failures. A test can fail even if the program does not encounter an error. -
    • -
    • - A progress bar. The progress bar extends from left to right as the tests run. -

      - If all the tests succeed, the bar remains green. If a test fails, the bar turns from green to red. -

      -
    • -
    • - A test method summary. Below the bar, you see a line for each class in the test application. To look at the results for the individual - methods in a test, click the arrow at the left to expand the line. You see the name of each test method. To the - right of the name, you see the time taken by the test. You can look at the test's code - by double-clicking its name. -
    • -
    -

    - The lower pane contains the failure trace. If all the tests are successful, this pane is empty. If some tests fail, - then if you highlight a failed test in the upper pane, the lower view contains a stack trace for the test. This is - demonstrated in the next section. -

    -

    - Note: If you run the test application and nothing seems to happen, look for - the JUnit view. If you do not see it, you may have run the test application - as a regular Android application. - Remember that you need to run it as an Android JUnit - application. -

    -

    Forcing Some Tests to Fail

    -

    - A test is as useful when it fails as when it succeeds. This section shows what happens in Eclipse with ADT when a test fails. You - can quickly see that a test class has failed, find the method or methods that failed, and then use a failure trace to find - the exact problem. -

    -

    - The example application SpinnerActivity that you downloaded passes all the tests in the test application SpinnerActivityTest. - To force the test to fail, you must modify the example application. You change a line of setup code in the application under test. This - causes the testPreConditions() and testTextView() test methods to fail. -

    -

    - To force the tests to fail, follow these steps: -

    -
      -
    1. - In Eclipse with ADT, go to the SpinnerActivity project and open the file SpinnerActivity.java. -
    2. -
    3. - At the top of SpinnerActivity.java, at the end of the onCreate() method, find the following line: -
      -    // mySpinner.setOnItemSelectedListener(null);
      -
      -

      Remove the forward slash characters at the beginning of the line to - uncomment the line. This sets the listener callback to null: -

      -
      -    mySpinner.setOnItemSelectedListener(null);
      -
      -
    4. -
    5. - The testPreConditions() method in SpinnerActivityTest contains the following test: - assertTrue(mSpinner.getOnItemSelectedListener() != null);. This test asserts that the listener callback is not null. - Since you have modified the application under test, this assertion now fails. -
    6. -
    7. - Run the test, as described in the previous section Running the Tests and Seeing the Results. -
    8. -
    -

    - The JUnit view is either created or updated with the results of the test. Now, however, the progress bar is red, - the number of failures is 2, and small "x" icons appear in the list icons next to the testPreConditions and - TestSpinnerUI tests. This indicates that the tests have failed. The display is similar to this:
    - - The JUnit Failure window - -

    -

    - You now want to look at the failures to see exactly where they occurred. -

    -

    - To examine the failures, follow these steps: -

    -
      -
    1. - Click the testPreconditions entry. In the lower pane entitled Failure Trace, - you see a stack trace of the calls that led to the failure. This trace is similar to the following screenshot:
      - - The JUnit failure trace - -
    2. -
    3. - The first line of the trace tells you the error. In this case, a JUnit assertion failed. To look at the - assertion in the test code, double-click the next line (the first line of the trace). In the center pane - a new tabbed window opens, containing the code for the test application SpinnerActivityTest. The failed assertion - is highlighted in the middle of the window. -
    4. -
    -

    - The assertion failed because you modified the main application to set the getOnItemSelectedListener callback to null. -

    -

    - You can look at the failure in testTextView if you want. Remember, though, that testPreConditions is meant to verify the - initial setup of the application under test. If testPreConditions() fails, then succeeding tests can't be trusted. The best strategy to follow is to - fix the problem and re-run all the tests. -

    -

    - Remember to go back to SpinnerActivity.java and re-comment the line you uncommented in an earlier step. -

    -

    - You have now completed the tutorial. -

    -

    Next Steps

    -

    - This example test application has shown you how to create a test project and link it to - the application you want to test, how to choose and add a test case class, how to write - UI and state management tests, and how to run the tests against the application under - test. Now that you are familiar with the basics of testing Android applications, here - are some suggested next steps: -

    -

    - Learn more about testing on Android -

    -
      -
    • - If you haven't done so already, read the - Testing Fundamentals - document in the Dev Guide. It provides an overview of how testing on Android - works. If you are just getting started with Android testing, reading that document will - help you understand the tools available to you, so that you can develop effective - tests. -
    • -
    -

    - Review the main Android test case classes -

    -
      -
    • - {@link android.test.ActivityInstrumentationTestCase2} -
    • -
    • - {@link android.test.ActivityUnitTestCase} -
    • -
    • - {@link android.test.ProviderTestCase2} -
    • -
    • - {@link android.test.ServiceTestCase} -
    • -
    -

    - Learn more about the assert and utility classes -

    -
      -
    • - {@link junit.framework.Assert}, the JUnit Assert class. -
    • -
    • - {@link android.test.MoreAsserts}, additional Android assert methods. -
    • -
    • - {@link android.test.ViewAsserts}, useful assertion methods for testing Views. -
    • -
    • - {@link android.test.TouchUtils}, utility methods for simulating touch events in an Activity. -
    • -
    -

    - Learn about instrumentation and the instrumented test runner -

    -
      -
    • - {@link android.app.Instrumentation}, the base instrumentation class. -
    • -
    • - {@link android.test.InstrumentationTestCase}, the base instrumentation test case. -
    • -
    • - {@link android.test.InstrumentationTestRunner}, the standard Android test runner. -
    • -
    -

    Appendix

    -

    Installing the Completed Test Application File

    -

    - The recommended approach to this tutorial is to follow the instructions step-by-step and - write the test code as you go. However, if you want to do this tutorial quickly, - you can install the entire file for the test application into the test project. -

    -

    - To do this, you first create a test project with the necessary structure and files by using - the automated tools in Eclipse. Then you exit Eclipse and copy the test application's file - from the SpinnerTest sample project into your test project. The SpinnerTest sample project is - part of the Samples component of the SDK. -

    -

    - The result is a complete test application, ready to run against the Spinner sample application. -

    -

    - To install the test application file, follow these steps: -

    -
      -
    1. - Set up the projects for the application under test and the test application, as described - in the section section Setting Up the Projects. -
    2. -
    3. - Set up the emulator, as described in the section Setting Up the Emulator. -
    4. -
    5. - Add the test case class, as described in the section Adding the test case class file. -
    6. -
    7. - Close Eclipse with ADT. -
    8. -
    9. - Copy the file <SDK_path>/samples/android-8/SpinnerTest/src/com/android/example/spinner/test/SpinnerActivityTest.java - to the directory workspace/SpinnerActivityTest/src/com/android/example/spinner/test/. -
    10. -
    11. - Restart Eclipse with ADT. -
    12. -
    13. - In Eclipse with ADT, re-build the project SpinnerActivityTest by selecting it in the Package Explorer, right-clicking, - and selecting Project > Clean. -
    14. -
    15. - The complete, working test application should now be in the SpinnerActivityTest project. -
    16. -
    -

    - You can now continue with the tutorial, starting at the section Adding the test case constructor and - following along in the text. -

    -

    For Users Not Developing In Eclipse

    -

    - If you are not developing in Eclipse, you can still do this tutorial. Android provides tools for - creating test applications using a code editor and command-line tools. You use the following tools: -

    -
      -
    • - adb - Installs and uninstalls applications and test applications to a device or the emulator. You - also use this tool to run the test application from the command line. -
    • -
    • - android - Manages projects and test projects. This tool also manages AVDs and Android platforms. -
    • -
    -

    - You use the emulator tool to run the emulator from the command line. -

    -

    - Here are the general steps for doing this tutorial using an editor and the command line: -

    -
      -
    1. - As described in the section Installing the Tutorial Sample Code, get the sample code. You will then - have a directory <SDK_path>/samples/android-8, containing (among others) the directories Spinner - and SpinnerTest: -
        -
      • - Spinner contains the main application, also known as the application under test. This tutorial focuses on the - common situation of writing tests for an application that already exists, so the main application is provided to you. -
      • -
      • - SpinnerTest contains all the code for the test application. If you want to run quickly through the tutorial, you can - install the test code and then follow the text. You may get more from the tutorial, however, if you write the code as you go. The instructions - for installing the test code are in the section Appendix: Installing the Completed Test Application File. -
      • -
      -
    2. -
    3. - Navigate to the directory <SDK_path>/samples/android-8. -
    4. -
    5. - Create a new Android application project using android create project: -
      -$ android create project -t <APItarget> -k com.android.example.spinner -a SpinnerActivity -n SpinnerActivity -p Spinner
      -
      -

      - The value of <APItarget> should be "3" (API level 3) or higher. If you are already developing with a particular API level, and it is - higher than 3, then use that API level. -

      -

      - This a new Android project SpinnerActivity in the existing Spinner directory. The existing source and - resource files are not touched, but the android tool adds the necessary build files. -

      -
    6. -
    7. - Create a new Android test project using android create test-project: -
      -$ android create test-project -m ../Spinner -n SpinnerActivityTest -p SpinnerActivityTest
      -
      -

      - This will create a new Android test project in the new directory SpinnerActivityTest. You do this - so that the solution to the tutorial that is in SpinnerTest is left untouched. If you want to use the solution - code instead of entering it as you read through the tutorial, refer to the section - Appendix: Installing the Completed Test Application File. -

      -

      - Note: Running android create test-project will automatically create - the file AndroidManifest.xml with the correct <instrumentation> element. -

      -
    8. -
    9. - Build the sample application. If you are building with Ant, then it is easiest to use the command ant debug to build a debug version, since the SDK comes - with a debug signing key. The result will be the file Spinner/bin/SpinnerActivity-debug.apk. - You can install this to your device or emulator. Attach your device or start the emulator if you haven't already, and run the command: -
      -$ adb install Spinner/bin/SpinnerActivity-debug.apk
      -
      -
    10. -
    11. - To create the test application, create a file SpinnerActivityTest.java in the directory - SpinnerActivityTest/src/com/android/example/spinner/test/. -
    12. -
    13. - Follow the tutorial, starting with the section Creating the Test Case Class. When you are prompted to - run the sample application, go to the Launcher screen in your device or emulator and select SpinnerActivity. - When you are prompted to run the test application, return here to continue with the following instructions. -
    14. -
    15. - Build the test application. If you are building with Ant, then it is easiest to use the command ant debug to build a - debug version, since the SDK comes with a debug signing key. The result will be the Android file - SpinnerActivityTest/bin/SpinnerActivityTest-debug.apk. You can install this to your device or emulator. - Attach your device or start the emulator if you haven't already, and run the command: -
      -$ adb install SpinnerActivityTest/bin/SpinnerActivityTest-debug.apk
      -
      -
    16. -
    17. - In your device or emulator, check that both the main application SpinnerActivity and the test application - SpinnerActivityTest are installed. -
    18. -
    19. - To run the test application, enter the following at the command line: -
      -$ adb shell am instrument -w com.android.example.spinner.test/android.test.InstrumentationTestRunner
      - 
      -
    20. -
    -

    - The result of a successful test looks like this: -

    -
    -com.android.example.spinner.test.SpinnerActivityTest:....
    -Test results for InstrumentationTestRunner=....
    -Time: 10.098
    -OK (4 tests)
    -
    -

    - If you force the test to fail, as described in the previous section Forcing Some Tests to Fail, then - the output looks like this: -

    -
    -com.android.example.spinner.test.SpinnerActivityTest:
    -Failure in testPreConditions:
    -junit.framework.AssertionFailedError
    -  at com.android.example.spinner.test.SpinnerActivityTest.testPreConditions(SpinnerActivityTest.java:104)
    -  at java.lang.reflect.Method.invokeNative(Native Method)
    -  at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:205)
    -  at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:195)
    -  at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:175)
    -  at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    -  at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    -  at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:430)
    -  at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
    -Failure in testSpinnerUI:
    -junit.framework.ComparisonFailure: expected:<Result> but was:<Saturn>
    -  at com.android.example.spinner.test.SpinnerActivityTest.testSpinnerUI(SpinnerActivityTest.java:153)
    -  at java.lang.reflect.Method.invokeNative(Native Method)
    -  at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:205)
    -  at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:195)
    -  at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:175)
    -  at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
    -  at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
    -  at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:430)
    -  at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1447)
    -..
    -Test results for InstrumentationTestRunner=.F.F..
    -Time: 9.377
    -FAILURES!!!
    -Tests run: 4,  Failures: 2,  Errors: 0
    -
    diff --git a/docs/html/tools/testing/activity_testing.jd b/docs/html/tools/testing/activity_testing.jd deleted file mode 100644 index 8baa35de88e8ecb15ebfbb7aa6e8166157e0becf..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/activity_testing.jd +++ /dev/null @@ -1,375 +0,0 @@ -page.title=Activity Testing -parent.title=Testing -parent.link=index.html -@jd:body - -
    -
    -

    In this document

    -
      -
    1. - The Activity Testing API -
        -
      1. - ActivityInstrumentationTestCase2 -
      2. -
      3. - ActivityUnitTestCase -
      4. -
      5. - SingleLaunchActivityTestCase -
      6. -
      7. - Mock objects and activity testing -
      8. -
      9. - Assertions for activity testing -
      10. -
      -
    2. -
    3. - What to Test -
    4. -
    5. - Next Steps -
    6. -
    7. - Appendix: UI Testing Notes -
        -
      1. - Testing on the UI thread -
      2. -
      3. - Turning off touch mode -
      4. -
      5. - Unlocking the Emulator or Device -
      6. -
      7. - Troubleshooting UI tests -
      8. -
      -
    8. -
    -

    Key Classes

    -
      -
    1. {@link android.test.InstrumentationTestRunner}
    2. -
    3. {@link android.test.ActivityInstrumentationTestCase2}
    4. -
    5. {@link android.test.ActivityUnitTestCase}
    6. -
    -

    Related Tutorials

    -
      -
    1. - Activity Testing Tutorial -
    2. -
    -

    See Also

    -
      -
    1. - - Testing from Eclipse with ADT -
    2. -
    3. - - Testing from Other IDEs -
    4. -
    -
    -
    -

    - Activity testing is particularly dependent on the Android instrumentation framework. - Unlike other components, activities have a complex lifecycle based on callback methods; these - can't be invoked directly except by instrumentation. Also, the only way to send events to the - user interface from a program is through instrumentation. -

    -

    - This document describes how to test activities using instrumentation and other test - facilities. The document assumes you have already read - Testing Fundamentals, - the introduction to the Android testing and instrumentation framework. -

    -

    The Activity Testing API

    -

    - The activity testing API base class is {@link android.test.InstrumentationTestCase}, - which provides instrumentation to the test case subclasses you use for Activities. -

    -

    - For activity testing, this base class provides these functions: -

    -
      -
    • - Lifecycle control: With instrumentation, you can start the activity under test, pause it, - and destroy it, using methods provided by the test case classes. -
    • -
    • - Dependency injection: Instrumentation allows you to create mock system objects such as - Contexts or Applications and use them to run the activity under test. This - helps you control the test environment and isolate it from production systems. You can - also set up customized Intents and start an activity with them. -
    • -
    • - User interface interaction: You use instrumentation to send keystrokes or touch events - directly to the UI of the activity under test. -
    • -
    -

    - The activity testing classes also provide the JUnit framework by extending - {@link junit.framework.TestCase} and {@link junit.framework.Assert}. -

    -

    - The two main testing subclasses are {@link android.test.ActivityInstrumentationTestCase2} and - {@link android.test.ActivityUnitTestCase}. To test an Activity that is launched in a mode - other than standard, you use {@link android.test.SingleLaunchActivityTestCase}. -

    -

    ActivityInstrumentationTestCase2

    -

    - The {@link android.test.ActivityInstrumentationTestCase2} test case class is designed to do - functional testing of one or more Activities in an application, using a normal system - infrastructure. It runs the Activities in a normal instance of the application under test, - using a standard system Context. It allows you to send mock Intents to the activity under - test, so you can use it to test an activity that responds to multiple types of intents, or - an activity that expects a certain type of data in the intent, or both. Notice, though, that it - does not allow mock Contexts or Applications, so you can not isolate the test from the rest of - a production system. -

    -

    ActivityUnitTestCase

    -

    - The {@link android.test.ActivityUnitTestCase} test case class tests a single activity in - isolation. Before you start the activity, you can inject a mock Context or Application, or both. - You use it to run activity tests in isolation, and to do unit testing of methods - that do not interact with Android. You can not send mock Intents to the activity under test, - although you can call - {@link android.app.Activity#startActivity(Intent) Activity.startActivity(Intent)} and then - look at arguments that were received. -

    -

    SingleLaunchActivityTestCase

    -

    - The {@link android.test.SingleLaunchActivityTestCase} class is a convenience class for - testing a single activity in an environment that doesn't change from test to test. - It invokes {@link junit.framework.TestCase#setUp() setUp()} and - {@link junit.framework.TestCase#tearDown() tearDown()} only once, instead of once per - method call. It does not allow you to inject any mock objects. -

    -

    - This test case is useful for testing an activity that runs in a mode other than - standard. It ensures that the test fixture is not reset between tests. You - can then test that the activity handles multiple calls correctly. -

    -

    Mock objects and activity testing

    -

    - This section contains notes about the use of the mock objects defined in - {@link android.test.mock} with activity tests. -

    -

    - The mock object {@link android.test.mock.MockApplication} is only available for activity - testing if you use the {@link android.test.ActivityUnitTestCase} test case class. - By default, ActivityUnitTestCase, creates a hidden MockApplication - object that is used as the application under test. You can inject your own object using - {@link android.test.ActivityUnitTestCase#setApplication(Application) setApplication()}. -

    -

    Assertions for activity testing

    -

    - {@link android.test.ViewAsserts} defines assertions for Views. You use it to verify the - alignment and position of View objects, and to look at the state of ViewGroup objects. -

    -

    What To Test

    -
      -
    • - Input validation: Test that an activity responds correctly to input values in an - EditText View. Set up a keystroke sequence, send it to the activity, and then - use {@link android.view.View#findViewById(int)} to examine the state of the View. You can - verify that a valid keystroke sequence enables an OK button, while an invalid one leaves the - button disabled. You can also verify that the Activity responds to invalid input by - setting error messages in the View. -
    • -
    • - Lifecycle events: Test that each of your application's activities handles lifecycle events - correctly. In general, lifecycle events are actions, either from the system or from the - user, that trigger a callback method such as onCreate() or - onClick(). For example, an activity should respond to pause or destroy events - by saving its state. Remember that even a change in screen orientation causes the current - activity to be destroyed, so you should test that accidental device movements don't - accidentally lose the application state. -
    • -
    • - Intents: Test that each activity correctly handles the intents listed in the intent - filter specified in its manifest. You can use - {@link android.test.ActivityInstrumentationTestCase2} to send mock Intents to the - activity under test. -
    • -
    • - Runtime configuration changes: Test that each activity responds correctly to the - possible changes in the device's configuration while your application is running. These - include a change to the device's orientation, a change to the current language, and so - forth. Handling these changes is described in detail in the topic - Handling Runtime - Changes. -
    • -
    • - Screen sizes and resolutions: Before you publish your application, make sure to test it on - all of the screen sizes and densities on which you want it to run. You can test the - application on multiple sizes and densities using AVDs, or you can test your application - directly on the devices that you are targeting. For more information, see the topic - Supporting Multiple Screens. -
    • -
    -

    Next Steps

    -

    - To learn how to set up and run tests in Eclipse, please refer to -Testing from Eclipse with ADT. - If you're not working in Eclipse, refer to -Testing from Other IDEs. -

    -

    - If you want a step-by-step introduction to testing activities, try the - Activity Testing Tutorial, which - guides you through a testing scenario that you develop against an activity-oriented application. -

    -

    Appendix: UI Testing Notes

    -

    - The following sections have tips for testing the UI of your Android application, specifically - to help you handle actions that run in the UI thread, touch screen and keyboard events, and home - screen unlock during testing. -

    -

    Testing on the UI thread

    -

    - An application's activities run on the application's UI thread. Once the - UI is instantiated, for example in the activity's onCreate() method, then all - interactions with the UI must run in the UI thread. When you run the application normally, it - has access to the thread and does not have to do anything special. -

    -

    - This changes when you run tests against the application. With instrumentation-based classes, - you can invoke methods against the UI of the application under test. The other test classes - don't allow this. To run an entire test method on the UI thread, you can annotate the thread - with @UiThreadTest. Notice that this will run all of the method statements - on the UI thread. Methods that do not interact with the UI are not allowed; for example, you - can't invoke Instrumentation.waitForIdleSync(). -

    -

    - To run a subset of a test method on the UI thread, create an anonymous class of type - Runnable, put the statements you want in the run() method, and - instantiate a new instance of the class as a parameter to the method - appActivity.runOnUiThread(), where appActivity is - the instance of the application you are testing. -

    -

    - For example, this code instantiates an activity to test, requests focus (a UI action) for the - Spinner displayed by the activity, and then sends a key to it. Notice that the calls to - waitForIdleSync and sendKeys aren't allowed to run on the UI thread: -

    -
    -  private MyActivity mActivity; // MyActivity is the class name of the app under test
    -  private Spinner mSpinner;
    -
    -  ...
    -
    -  protected void setUp() throws Exception {
    -      super.setUp();
    -      mInstrumentation = getInstrumentation();
    -
    -      mActivity = getActivity(); // get a references to the app under test
    -
    -      /*
    -       * Get a reference to the main widget of the app under test, a Spinner
    -       */
    -      mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);
    -
    -  ...
    -
    -  public void aTest() {
    -      /*
    -       * request focus for the Spinner, so that the test can send key events to it
    -       * This request must be run on the UI thread. To do this, use the runOnUiThread method
    -       * and pass it a Runnable that contains a call to requestFocus on the Spinner.
    -       */
    -      mActivity.runOnUiThread(new Runnable() {
    -          public void run() {
    -              mSpinner.requestFocus();
    -          }
    -      });
    -
    -      mInstrumentation.waitForIdleSync();
    -
    -      this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    -
    - -

    Turning off touch mode

    -

    - To control the emulator or a device with key events you send from your tests, you must turn off - touch mode. If you do not do this, the key events are ignored. -

    -

    - To turn off touch mode, you invoke - ActivityInstrumentationTestCase2.setActivityTouchMode(false) - before you call getActivity() to start the activity. You must invoke the - method in a test method that is not running on the UI thread. For this reason, you - can't invoke the touch mode method from a test method that is annotated with - @UIThread. Instead, invoke the touch mode method from setUp(). -

    -

    Unlocking the emulator or device

    -

    - You may find that UI tests don't work if the emulator's or device's home screen is disabled with - the keyguard pattern. This is because the application under test can't receive key events sent - by sendKeys(). The best way to avoid this is to start your emulator or device - first and then disable the keyguard for the home screen. -

    -

    - You can also explicitly disable the keyguard. To do this, - you need to add a permission in the manifest file (AndroidManifest.xml) and - then disable the keyguard in your application under test. Note, though, that you either have to - remove this before you publish your application, or you have to disable it with code in - the published application. -

    -

    - To add the permission, add the element - <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/> - as a child of the <manifest> element. To disable the KeyGuard, add the - following code to the onCreate() method of activities you intend to test: -

    -
    -  mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
    -  mLock = mKeyGuardManager.newKeyguardLock("activity_classname");
    -  mLock.disableKeyguard();
    -
    -

    where activity_classname is the class name of the activity.

    -

    Troubleshooting UI tests

    -

    - This section lists some of the common test failures you may encounter in UI testing, and their - causes: -

    -
    -
    WrongThreadException
    -
    -

    Problem:

    - For a failed test, the Failure Trace contains the following error message: - - android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created - a view hierarchy can touch its views. - -

    Probable Cause:

    - This error is common if you tried to send UI events to the UI thread from outside the UI - thread. This commonly happens if you send UI events from the test application, but you don't - use the @UIThread annotation or the runOnUiThread() method. The - test method tried to interact with the UI outside the UI thread. -

    Suggested Resolution:

    - Run the interaction on the UI thread. Use a test class that provides instrumentation. See - the previous section Testing on the UI Thread - for more details. -
    -
    java.lang.RuntimeException
    -
    -

    Problem:

    - For a failed test, the Failure Trace contains the following error message: - - java.lang.RuntimeException: This method can not be called from the main application thread - -

    Probable Cause:

    - This error is common if your test method is annotated with @UiThreadTest but - then tries to do something outside the UI thread or tries to invoke - runOnUiThread(). -

    Suggested Resolution:

    - Remove the @UiThreadTest annotation, remove the runOnUiThread() - call, or re-factor your tests. -
    -
    diff --git a/docs/html/tools/testing/contentprovider_testing.jd b/docs/html/tools/testing/contentprovider_testing.jd deleted file mode 100644 index a6440df0f4c4c86c9cf05b7e62315061283f100c..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/contentprovider_testing.jd +++ /dev/null @@ -1,217 +0,0 @@ -page.title=Content Provider Testing -parent.title=Testing -parent.link=index.html -@jd:body - -
    -
    -

    In this document

    -
      -
    1. - Content Provider Design and Testing -
    2. -
    3. - The Content Provider Testing API -
        -
      1. - ProviderTestCase2 -
      2. -
      3. - Mock object classes -
      4. -
      -
    4. -
    5. - What To Test -
    6. -
    7. - Next Steps -
    8. -
    -

    Key Classes

    -
      -
    1. {@link android.test.InstrumentationTestRunner}
    2. -
    3. {@link android.test.ProviderTestCase2}
    4. -
    5. {@link android.test.IsolatedContext}
    6. -
    7. {@link android.test.mock.MockContentResolver}
    8. -
    -

    Related Tutorials

    -
      -
    1. - Activity Testing Tutorial -
    2. -
    -

    See Also

    -
      -
    1. - - Testing Fundamentals -
    2. -
    3. - - Testing From Eclipse with ADT -
    4. -
    5. - - Testing From Other IDEs -
    6. -
    -
    -
    -

    - Content providers, which store and retrieve data and make it accessible across applications, - are a key part of the Android API. As an application developer you're allowed to provide your - own public providers for use by other applications. If you do, then you should test them - using the API you publish. -

    -

    - This document describes how to test public content providers, although the information is - also applicable to providers that you keep private to your own application. If you aren't - familiar with content providers or the Android testing framework, please read - Content Providers, - the guide to developing content providers, and - Testing Fundamentals, - the introduction to the Android testing and instrumentation framework. -

    -

    Content Provider Design and Testing

    -

    - In Android, content providers are viewed externally as data APIs that provide - tables of data, with their internals hidden from view. A content provider may have many - public constants, but it usually has few if any public methods and no public variables. - This suggests that you should write your tests based only on the provider's public members. - A content provider that is designed like this is offering a contract between itself and its - users. -

    -

    - The base test case class for content providers, - {@link android.test.ProviderTestCase2}, allows you to test your content provider in an - isolated environment. Android mock objects such as {@link android.test.IsolatedContext} and - {@link android.test.mock.MockContentResolver} also help provide an isolated test environment. -

    -

    - As with other Android tests, provider test packages are run under the control of the test - runner {@link android.test.InstrumentationTestRunner}. The section - - Running Tests With InstrumentationTestRunner describes the test runner in - more detail. The topic - Testing From Eclipse with ADT shows you how to run a test package in Eclipse, and the - topic - Testing From Other IDEs - shows you how to run a test package from the command line. -

    -

    Content Provider Testing API

    -

    - The main focus of the provider testing API is to provide an isolated testing environment. This - ensures that tests always run against data dependencies set explicitly in the test case. It - also prevents tests from modifying actual user data. For example, you want to avoid writing - a test that fails because there was data left over from a previous test, and you want to - avoid adding or deleting contact information in a actual provider. -

    -

    - The test case class and mock object classes for provider testing set up this isolated testing - environment for you. -

    -

    ProviderTestCase2

    -

    - You test a provider with a subclass of {@link android.test.ProviderTestCase2}. This base class - extends {@link android.test.AndroidTestCase}, so it provides the JUnit testing framework as well - as Android-specific methods for testing application permissions. The most important - feature of this class is its initialization, which creates the isolated test environment. -

    -

    - The initialization is done in the constructor for {@link android.test.ProviderTestCase2}, which - subclasses call in their own constructors. The {@link android.test.ProviderTestCase2} - constructor creates an {@link android.test.IsolatedContext} object that allows file and - database operations but stubs out other interactions with the Android system. - The file and database operations themselves take place in a directory that is local to the - device or emulator and has a special prefix. -

    -

    - The constructor then creates a {@link android.test.mock.MockContentResolver} to use as the - resolver for the test. The {@link android.test.mock.MockContentResolver} class is described in - detail in the section - Mock object -classes. -

    -

    - Lastly, the constructor creates an instance of the provider under test. This is a normal - {@link android.content.ContentProvider} object, but it takes all of its environment information - from the {@link android.test.IsolatedContext}, so it is restricted to - working in the isolated test environment. All of the tests done in the test case class run - against this isolated object. -

    -

    Mock object classes

    -

    - {@link android.test.ProviderTestCase2} uses {@link android.test.IsolatedContext} and - {@link android.test.mock.MockContentResolver}, which are standard mock object classes. To - learn more about them, please read - - Testing Fundamentals. -

    -

    What To Test

    -

    - The topic What To Test - lists general considerations for testing Android components. - Here are some specific guidelines for testing content providers. -

    -
      -
    • - Test with resolver methods: Even though you can instantiate a provider object in - {@link android.test.ProviderTestCase2}, you should always test with a resolver object - using the appropriate URI. This ensures that you are testing the provider using the same - interaction that a regular application would use. -
    • -
    • - Test a public provider as a contract: If you intent your provider to be public and - available to other applications, you should test it as a contract. This includes - the following ideas: -
        -
      • - Test with constants that your provider publicly exposes. For - example, look for constants that refer to column names in one of the provider's - data tables. These should always be constants publicly defined by the provider. -
      • -
      • - Test all the URIs offered by your provider. Your provider may offer several URIs, - each one referring to a different aspect of the data. The - Note Pad sample, - for example, features a provider that offers one URI for retrieving a list of notes, - another for retrieving an individual note by it's database ID, and a third for - displaying notes in a live folder. -
      • -
      • - Test invalid URIs: Your unit tests should deliberately call the provider with an - invalid URI, and look for errors. Good provider design is to throw an - IllegalArgumentException for invalid URIs. - -
      • -
      -
    • -
    • - Test the standard provider interactions: Most providers offer six access methods: - query, insert, delete, update, getType, and onCreate(). Your tests should verify that all - of these methods work. These are described in more detail in the topic - Content Providers. -
    • -
    • - Test business logic: Don't forget to test the business logic that your provider should - enforce. Business logic includes handling of invalid values, financial or arithmetic - calculations, elimination or combining of duplicates, and so forth. A content provider - does not have to have business logic, because it may be implemented by activities that - modify the data. If the provider does implement business logic, you should test it. -
    • -
    -

    Next Steps

    -

    - To learn how to set up and run tests in Eclipse, please refer to -Testing from Eclipse with ADT. - If you're not working in Eclipse, refer to -Testing From Other IDEs. -

    -

    - If you want a step-by-step introduction to testing activities, try the - Activity Testing Tutorial, which - guides you through a testing scenario that you develop against an activity-oriented application. -

    - diff --git a/docs/html/tools/testing/index.jd b/docs/html/tools/testing/index.jd index 56de4cf80658b2a20e0ff63b40779bf4fa0b2a47..a4548912f4ffd3b5a5235b63e9c1ec3c4d7446e8 100644 --- a/docs/html/tools/testing/index.jd +++ b/docs/html/tools/testing/index.jd @@ -1,40 +1,19 @@ page.title=Testing @jd:body -

    The Android framework includes an integrated testing framework that helps you test all aspects -of your application and the SDK tools include tools for setting up and running test applications. -Whether you are working in Eclipse with ADT or working from the command line, the SDK tools help you -set up and run your tests within an emulator or the device you are targeting.

    - -

    If you aren't yet familiar with the Android testing framework, start by reading Testing Fundamentals. For a step-by-step -introduction to Android testing, try the Activity Testing Tutorial.

    - - - - - - +

    Android provides an integrated testing framework that helps you test all aspects +of your app. The Android SDK and +Testing Support Library include +tools and APIs for setting up and running test apps within an emulator or on the device you are +targeting. You can build and execute tests whether you are working in Android Studio or working +from the command line.

    + +

    To familiarize yourself with mobile app testing in Android, start by reading these resources:

    +
      +
    • Testing Concepts: Learn key + concepts related to Android app testing and get an overview of the testing tools and APIs + that Google provides.
    • +
    • Getting Started with Testing: Learn + how to build and run your tests, step-by-step, using the testing APIs and tools that + Google provides.
    • +
    diff --git a/docs/html/tools/testing/service_testing.jd b/docs/html/tools/testing/service_testing.jd deleted file mode 100644 index 7c56fd947df6419d6074ddfa016f572d09898032..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/service_testing.jd +++ /dev/null @@ -1,176 +0,0 @@ -page.title=Service Testing -parent.title=Testing -parent.link=index.html -@jd:body - -
    -
    -

    In this document

    -
      -
    1. - Service Design and Testing -
    2. -
    3. - ServiceTestCase -
    4. -
    5. - Mock object classes -
    6. -
    7. - What to Test -
    8. -
    -

    Key Classes

    -
      -
    1. {@link android.test.InstrumentationTestRunner}
    2. -
    3. {@link android.test.ServiceTestCase}
    4. -
    5. {@link android.test.mock.MockApplication}
    6. -
    7. {@link android.test.RenamingDelegatingContext}
    8. -
    -

    Related Tutorials

    -
      -
    1. - Activity Testing Tutorial -
    2. -
    -

    See Also

    -
      -
    1. - - Testing From Eclipse with ADT -
    2. -
    3. - - Testing From Other IDEs -
    4. -
    -
    -
    -

    - Android provides a testing framework for Service objects that can run them in - isolation and provides mock objects. The test case class for Service objects is - {@link android.test.ServiceTestCase}. Since the Service class assumes that it is separate - from its clients, you can test a Service object without using instrumentation. -

    -

    - This document describes techniques for testing Service objects. If you aren't familiar with the - Service class, please read the - Services document. If you aren't familiar with Android testing, please read - Testing Fundamentals, - the introduction to the Android testing and instrumentation framework. -

    -

    Service Design and Testing

    -

    - When you design a Service, you should consider how your tests can examine the various states - of the Service lifecycle. If the lifecycle methods that start up your Service, such as - {@link android.app.Service#onCreate() onCreate()} or - {@link android.app.Service#onStartCommand(Intent, int, int) onStartCommand()} do not normally - set a global variable to indicate that they were successful, you may want to provide such a - variable for testing purposes. -

    -

    - Most other testing is facilitated by the methods in the {@link android.test.ServiceTestCase} - test case class. For example, the {@link android.test.ServiceTestCase#getService()} method - returns a handle to the Service under test, which you can test to confirm that the Service is - running even at the end of your tests. -

    -

    ServiceTestCase

    -

    - {@link android.test.ServiceTestCase} extends the JUnit {@link junit.framework.TestCase} class - with with methods for testing application permissions and for controlling the application and - Service under test. It also provides mock application and Context objects that isolate your - test from the rest of the system. -

    -

    - {@link android.test.ServiceTestCase} defers initialization of the test environment until you - call {@link android.test.ServiceTestCase#startService(Intent) ServiceTestCase.startService()} or - {@link android.test.ServiceTestCase#bindService(Intent) ServiceTestCase.bindService()}. This - allows you to set up your test environment, particularly your mock objects, before the Service - is started. -

    -

    - Notice that the parameters to ServiceTestCase.bindService()are different from - those for Service.bindService(). For the ServiceTestCase version, - you only provide an Intent. Instead of returning a boolean, - ServiceTestCase.bindService() returns an object that subclasses - {@link android.os.IBinder}. -

    -

    - The {@link android.test.ServiceTestCase#setUp()} method for {@link android.test.ServiceTestCase} - is called before each test. It sets up the test fixture by making a copy of the current system - Context before any test methods touch it. You can retrieve this Context by calling - {@link android.test.ServiceTestCase#getSystemContext()}. If you override this method, you must - call super.setUp() as the first statement in the override. -

    -

    - The methods {@link android.test.ServiceTestCase#setApplication(Application) setApplication()} - and {@link android.test.AndroidTestCase#setContext(Context)} setContext()} allow you to set - a mock Context or mock Application (or both) for the Service, before you start it. These mock - objects are described in Mock object classes. -

    -

    - By default, {@link android.test.ServiceTestCase} runs the test method - {@link android.test.AndroidTestCase#testAndroidTestCaseSetupProperly()}, which asserts that - the base test case class successfully set up a Context before running. -

    -

    Mock object classes

    -

    - ServiceTestCase assumes that you will use a mock Context or mock Application - (or both) for the test environment. These objects isolate the test environment from the - rest of the system. If you don't provide your own instances of these objects before you - start the Service, then {@link android.test.ServiceTestCase} will create its own internal - instances and inject them into the Service. You can override this behavior by creating and - injecting your own instances before starting the Service -

    -

    - To inject a mock Application object into the Service under test, first create a subclass of - {@link android.test.mock.MockApplication}. MockApplication is a subclass of - {@link android.app.Application} in which all the methods throw an Exception, so to use it - effectively you subclass it and override the methods you need. You then inject it into the - Service with the - {@link android.test.ServiceTestCase#setApplication(Application) setApplication()} method. - This mock object allows you to control the application values that the Service sees, and - isolates it from the real system. In addition, any hidden dependencies your Service has on - its application reveal themselves as exceptions when you run the test. -

    -

    - You inject a mock Context into the Service under test with the - {@link android.test.AndroidTestCase#setContext(Context) setContext()} method. The mock - Context classes you can use are described in more detail in - - Testing Fundamentals. -

    -

    What to Test

    -

    - The topic What To Test - lists general considerations for testing Android components. - Here are some specific guidelines for testing a Service: -

    -
      -
    • - Ensure that the {@link android.app.Service#onCreate()} is called in response to - {@link android.content.Context#startService(Intent) Context.startService()} or - {@link android.content.Context#bindService(Intent,ServiceConnection,int) Context.bindService()}. - Similarly, you should ensure that {@link android.app.Service#onDestroy()} is called in - response to {@link android.content.Context#stopService(Intent) Context.stopService()}, - {@link android.content.Context#unbindService(ServiceConnection) Context.unbindService()}, - {@link android.app.Service#stopSelf()}, or - {@link android.app.Service#stopSelfResult(int) stopSelfResult()}. -
    • -
    • - Test that your Service correctly handles multiple calls from - Context.startService(). Only the first call triggers - Service.onCreate(), but all calls trigger a call to - Service.onStartCommand(). -

      - In addition, remember that startService() calls don't - nest, so a single call to Context.stopService() or - Service.stopSelf() (but not stopSelf(int)) - will stop the Service. You should test that your Service stops at the correct point. -

      -
    • -
    • - Test any business logic that your Service implements. Business logic includes checking for - invalid values, financial and arithmetic calculations, and so forth. -
    • -
    diff --git a/docs/html/tools/testing/testing-tools.jd b/docs/html/tools/testing/testing-tools.jd index 5e0a5bcd26fafaebcb2863d6b632554e61d5fe7f..c60199f78d1d45e28fdf17047f0f5bbc4dff97e5 100644 --- a/docs/html/tools/testing/testing-tools.jd +++ b/docs/html/tools/testing/testing-tools.jd @@ -5,7 +5,7 @@ page.title=Android Testing Tools
    diff --git a/docs/html/tools/testing/testing_android.jd b/docs/html/tools/testing/testing_android.jd old mode 100644 new mode 100755 index 10843e8986d4fcc9a163d825a8a0d56ce4d91e6b..5adb7e96230fe25c01ea259ebe7396897b63d87c --- a/docs/html/tools/testing/testing_android.jd +++ b/docs/html/tools/testing/testing_android.jd @@ -1,4 +1,4 @@ -page.title=Testing Fundamentals +page.title=Testing Concepts parent.title=Testing parent.link=index.html @jd:body @@ -7,14 +7,8 @@ parent.link=index.html

    In this document

      -
    1. - Test Structure -
    2. -
    3. - Test Projects -
    4. -
    5. - The Testing API +
    6. Test Structure
    7. +
    8. Testing APIs
      1. JUnit @@ -23,530 +17,256 @@ parent.link=index.html Instrumentation
      2. - Test case classes + Android Testing Support Library APIs
      3. Assertion classes
      4. -
      5. - Mock object classes -
    9. - Running Tests -
    10. -
    11. - Seeing Test Results -
    12. -
    13. - monkey and monkeyrunner -
    14. -
    15. - Working With Package Names -
    16. -
    17. - What To Test -
    18. -
    19. - Next Steps + Monkey and Monkeyrunner
    -

    Key classes

    -
      -
    1. {@link android.test.InstrumentationTestRunner}
    2. -
    3. {@link android.test}
    4. -
    5. {@link android.test.mock}
    6. -
    7. {@link junit.framework}
    8. -
    -

    Related tutorials

    -
      -
    1. - Activity Testing Tutorial -
    2. -

    See also

    -
      -
    1. - - Testing from Eclipse with ADT -
    2. -
    3. - - Testing from Other IDEs -
    4. -
    5. - - monkeyrunner -
    6. -
    7. - UI/Application Exerciser Monkey -
    8. -
    +
      +
    1. Getting Started with Testing
    2. +
    +

    - The Android testing framework, an integral part of the development environment, - provides an architecture and powerful tools that help you test every aspect of your application - at every level from unit to framework. -

    -

    - The testing framework has these key features: -

    +This document describes key concepts related to Android app testing. It assumes you have a basic +knowledge of the JUnit testing framework.

    + +

    Test Structure

    +

    Android testing is based on JUnit. In general, a JUnit test is a method whose statements test a +part of the application. You organize test methods into classes called +test cases. You can further organize these classes into test suites.

    +

    In JUnit, you build one or more test classes and use a test runner to +execute them. In Android, you use +Android Studio (or the +Android Plugin for Gradle) to build one or more test source files into an +Android test app.

    + +

    From your testing environment, you can run your test in one of the following ways:

      -
    • - Android test suites are based on JUnit. You can use plain JUnit to test a class that doesn't - call the Android API, or Android's JUnit extensions to test Android components. If you're - new to Android testing, you can start with general-purpose test case classes such as {@link - android.test.AndroidTestCase} and then go on to use more sophisticated classes. -
    • -
    • - The Android JUnit extensions provide component-specific test case classes. These classes - provide helper methods for creating mock objects and methods that help you control the - lifecycle of a component. -
    • -
    • - Test suites are contained in test packages that are similar to main application packages, so - you don't need to learn a new set of tools or techniques for designing and building tests. -
    • -
    • - The SDK tools for building and tests are available in Eclipse with ADT, and also in - command-line form for use with other IDEs. These tools get information from the project of - the application under test and use this information to automatically create the build files, - manifest file, and directory structure for the test package. -
    • -
    • - The SDK also provides - monkeyrunner, an API - for testing devices with Python programs, and UI/Application Exerciser Monkey, - a command-line tool for stress-testing UIs by sending pseudo-random events to a device. -
    • +
    • On your local machine: Compile the test classes and + execute them locally on the Java Virtual Machine (JVM) using the JUnit test runner.
    • +
    • On a device or emulator: Install the test app and the app +under test to a physical device or emulator, and then execute your tests using an Android-specific +test runner (such as +{@code AndroidJUnitRunner}).
    -

    - This document describes the fundamentals of the Android testing framework, including the - structure of tests, the APIs that you use to develop tests, and the tools that you use to run - tests and view results. The document assumes you have a basic knowledge of Android application - programming and JUnit testing methodology. -

    -

    - The following diagram summarizes the testing framework: -

    -
    - - The Android testing framework - -
    -

    Test Structure

    -

    - Android's build and test tools assume that test projects are organized into a standard - structure of tests, test case classes, test packages, and test projects. -

    -

    - Android testing is based on JUnit. In general, a JUnit test is a method whose - statements test a part of the application under test. You organize test methods into classes - called test cases (or test suites). Each test is an isolated test of an individual module in - the application under test. Each class is a container for related test methods, although it - often provides helper methods as well. -

    -

    - In JUnit, you build one or more test source files into a class file. Similarly, in Android you - use the SDK's build tools to build one or more test source files into class files in an - Android test package. In JUnit, you use a test runner to execute test classes. In Android, you - use test tools to load the test package and the application under test, and the tools then - execute an Android-specific test runner. -

    -

    Test Projects

    -

    - Tests, like Android applications, are organized into projects. -

    -

    - A test project is a directory or Eclipse project in which you create the source code, manifest - file, and other files for a test package. The Android SDK contains tools for Eclipse with ADT - and for the command line that create and update test projects for you. The tools create the - directories you use for source code and resources and the manifest file for the test package. - The command-line tools also create the Ant build files you need. -

    -

    - You should always use Android tools to create a test project. Among other benefits, - the tools: -

    -
      -
    • - Automatically set up your test package to use - {@link android.test.InstrumentationTestRunner} as the test case runner. You must use - InstrumentationTestRunner (or a subclass) to run JUnit tests. -
    • -
    • - Create an appropriate name for the test package. If the application - under test has a package name of com.mydomain.myapp, then the - Android tools set the test package name to com.mydomain.myapp.test. This - helps you identify their relationship, while preventing conflicts within the system. -
    • -
    • - Automatically create the proper build files, manifest file, and directory - structure for the test project. This helps you to build the test package without - having to modify build files and sets up the linkage between your test package and - the application under test. - The -
    • -
    -

    - You can create a test project anywhere in your file system, but the best approach is to - add the test project so that its root directory tests/ is at the same level - as the src/ directory of the main application's project. This helps you find the - tests associated with an application. For example, if your application project's root directory - is MyProject, then you should use the following directory structure: -

    -
    -  MyProject/
    -      AndroidManifest.xml
    -      res/
    -          ... (resources for main application)
    -      src/
    -          ... (source code for main application) ...
    -      tests/
    -          AndroidManifest.xml
    -          res/
    -              ... (resources for tests)
    -          src/
    -              ... (source code for tests)
    -
    -

    The Testing API

    -

    - The Android testing API is based on the JUnit API and extended with a instrumentation - framework and Android-specific testing classes. -

    + +

    The structure of your test code and the way you build and run the tests in Android Studio depend +on the type of testing you are performing. The following table summarizes the common testing types +for Android:

    + + + + + + + + + + + + + + + + + +
    TypeSubtypeDescription
    Unit tests
    Local Unit TestsUnit tests that run on your local machine only. These tests are compiled to run locally +on the JVM to minimize execution time. Use this approach to run unit tests +that have no dependencies on the Android framework or have dependencies that mock objects can +satisfy.
    Instrumented unit testsUnit tests that run on an Android device or emulator. These tests have access to +{@link android.app.Instrumentation} information, such as the {@link android.content.Context} of the +app under test. Use this approach to run unit tests that have Android dependencies which mock +objects cannot easily satisfy.
    Integration Tests
    Components within your app onlyThis type of test verifies that the target app behaves as expected when a user performs +a specific action or enters a specific input in its activities. For example, it allows you to check +that the target app returns the correct UI output in response to user interactions in the app’s +activities. UI testing frameworks like +Espresso allow you to +programmatically simulate user actions and test complex intra-app user interactions.
    Cross-app ComponentsThis type of test verifies the correct behavior of interactions between different user +apps or between user apps and system apps. For example, you might want to test that your app behaves +correctly when the user performs an action in the Android Settings menu. UI testing frameworks +that support cross-app interactions, such as UI Automator, allow you to create tests for such +scenarios.
    + +

    Based on the type of test you want to create, you need to configure the test code source +location and the project dependencies in Android Studio as described in +Getting Started with Testing.

    + +

    Testing APIs

    +

    The following list summarizes the common APIs related to app testing for Android.

    +

    JUnit

    -

    - You can use the JUnit {@link junit.framework.TestCase TestCase} class to do unit testing on - a class that doesn't call Android APIs. TestCase is also the base class for - {@link android.test.AndroidTestCase}, which you can use to test Android-dependent objects. - Besides providing the JUnit framework, AndroidTestCase offers Android-specific setup, - teardown, and helper methods. -

    -

    - You use the JUnit {@link junit.framework.Assert} class to display test results. - The assert methods compare values you expect from a test to the actual results and - throw an exception if the comparison fails. Android also provides a class of assertions that - extend the possible types of comparisons, and another class of assertions for testing the UI. - These are described in more detail in the section - Assertion classes -

    -

    - To learn more about JUnit, you can read the documentation on the - junit.org home page. - Note that the Android testing API supports JUnit 3 code style, but not JUnit 4. Also, you must - use Android's instrumented test runner {@link android.test.InstrumentationTestRunner} to run - your test case classes. This test runner is described in the - section Running Tests. -

    -

    Instrumentation

    -

    - Android instrumentation is a set of control methods or "hooks" in the Android system. These hooks - control an Android component independently of its normal lifecycle. They also control how - Android loads applications. -

    -

    - Normally, an Android component runs in a lifecycle determined by the system. For example, an - Activity object's lifecycle starts when the Activity is activated by an Intent. The object's - onCreate() method is called, followed by onResume(). When the user - starts another application, the onPause() method is called. If the Activity - code calls the finish() method, the onDestroy() method is called. - The Android framework API does not provide a way for your code to invoke these callback - methods directly, but you can do so using instrumentation. -

    -

    - Also, the system runs all the components of an application into the same - process. You can allow some components, such as content providers, to run in a separate process, - but you can't force an application to run in the same process as another application that is - already running. -

    -

    - With Android instrumentation, though, you can invoke callback methods in your test code. - This allows you to run through the lifecycle of a component step by step, as if you were - debugging the component. The following test code snippet demonstrates how to use this to - test that an Activity saves and restores its state: -

    - -
    -    // Start the main activity of the application under test
    -    mActivity = getActivity();
     
    -    // Get a handle to the Activity object's main UI widget, a Spinner
    -    mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);
    +

    You should write your unit or integration test class as a JUnit 4 test class. +JUnit is the most popular +and widely-used unit testing framework for Java. The framework offers a convenient way to perform +common setup, teardown, and assertion operations in your test.

    - // Set the Spinner to a known position - mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); +

    JUnit 4 allows you to write tests in a cleaner and more +flexible way than its predecessor versions. Unlike the previous approach to Android unit testing +based on JUnit 3, with JUnit 4, you do not need to extend the {@code junit.framework.TestCase} +class. You also do not need to prepend the {@code test} keyword to your test method name, or +use any classes in the {@code junit.framework} or {@code junit.extensions} package.

    - // Stop the activity - The onDestroy() method should save the state of the Spinner - mActivity.finish(); +

    A basic JUnit 4 test class is a Java class that contains one or more test methods. +A test method begins with the {@code @Test} annotation and contains the code to exercise +and verify a single functionality (that is, a logical unit) in the component that you want +to test.

    +

    The following snippet shows an example JUnit 4 integration test that uses the Espresso +APIs to perform a click action on a UI element, then checks to see if an expected string is +displayed.

    +
    +@RunWith(AndroidJUnit4.class)
    +@LargeTest
    +public class MainActivityInstrumentationTest {
     
    -    // Re-start the Activity - the onResume() method should restore the state of the Spinner
    -    mActivity = getActivity();
    +    @Rule
    +    public ActivityTestRule mActivityRule = new ActivityTestRule<>(
    +            MainActivity.class);
     
    -    // Get the Spinner's current position
    -    int currentPosition = mActivity.getSpinnerPosition();
    +    @Test
    +    public void sayHello(){
    +        onView(withText("Say hello!")).perform(click());
     
    -    // Assert that the current position is the same as the starting position
    -    assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
    +        onView(withId(R.id.textView)).check(matches(withText("Hello, World!")));
    +    }
    +}
     
    -

    - The key method used here is - {@link android.test.ActivityInstrumentationTestCase2#getActivity()}, which is a - part of the instrumentation API. The Activity under test is not started until you call this - method. You can set up the test fixture in advance, and then call this method to start the - Activity. -

    -

    - Also, instrumentation can load both a test package and the application under test into the - same process. Since the application components and their tests are in the same process, the - tests can invoke methods in the components, and modify and examine fields in the components. -

    -

    Test case classes

    -

    - Android provides several test case classes that extend {@link junit.framework.TestCase} and - {@link junit.framework.Assert} with Android-specific setup, teardown, and helper methods. -

    -

    AndroidTestCase

    -

    - A useful general test case class, especially if you are - just starting out with Android testing, is {@link android.test.AndroidTestCase}. It extends - both {@link junit.framework.TestCase} and {@link junit.framework.Assert}. It provides the - JUnit-standard setUp() and tearDown() methods, as well as - all of JUnit's Assert methods. In addition, it provides methods for testing permissions, and a - method that guards against memory leaks by clearing out certain class references. -

    -

    Component-specific test cases

    -

    - A key feature of the Android testing framework is its component-specific test case classes. - These address specific component testing needs with methods for fixture setup and - teardown and component lifecycle control. They also provide methods for setting up mock objects. - These classes are described in the component-specific testing topics: -

    +

    In your JUnit 4 test class, you can call out sections in your test code for +special processing by using the following annotations:

      -
    • - Activity Testing -
    • -
    • - - Content Provider Testing -
    • -
    • - Service Testing -
    • +
    • +{@code @Before}: Use this annotation to specify a block of code that contains test setup +operations. The test class invokes this code block before each test. You can have multiple +{@code @Before} methods but the order in which the test class calls these methods +is not guaranteed. +
    • +
    • +{@code @After}: This annotation specifies a block of code that contains test +tear-down operations. The test class calls this code block after every test method. You can define +multiple {@code @After} operations in your test code. Use this annotation to release any +resources from memory. +
    • +
    • +{@code @Test}: Use this annotation to mark a test method. A single test class can contain +multiple test methods, each prefixed with this annotation. +
    • +
    • +{@code @Rule}: Rules allow you to flexibly add or redefine the behavior of each test +method in a reusable way. In Android testing, use this annotation together with +one of the test rule classes that the Android Testing Support Library provides, such as + +{@code ActivityTestRule} or + +{@code ServiceTestRule}. +
    • +
    • +{@code @BeforeClass}: Use this annotation to specify static methods for each test class to +invoke only once. This testing step is useful for expensive operations such as connecting to a +database. +
    • +
    • +{@code @AfterClass}: Use this annotation to specify static methods for the test class to invoke +only after all tests in the class have run. This testing step is useful for releasing any +resources allocated in the {@code @BeforeClass} block. +
    • +
    • +{@code @Test(timeout=<milliseconds>)}: Some annotations support the ability to pass in +elements for which you can set values. For example, you can specify a timeout period for the test. +If the test starts but does not complete within the given timeout period, it automatically fails. +You must specify the timeout period in milliseconds, for example: {@code @Test(timeout=5000)}. +
    +

    For more annotations, see the documentation for + +JUnit annotations and the + +Android-specific annotations.

    - Android does not provide a separate test case class for BroadcastReceiver. Instead, test a - BroadcastReceiver by testing the component that sends it Intent objects, to verify that the - BroadcastReceiver responds correctly. -

    -

    ApplicationTestCase

    -

    - You use the {@link android.test.ApplicationTestCase} test case class to test the setup and - teardown of {@link android.app.Application} objects. These objects maintain the global state of - information that applies to all the components in an application package. The test case can - be useful in verifying that the <application> element in the manifest file is correctly - set up. Note, however, that this test case does not allow you to control testing of the - components within your application package. -

    -

    InstrumentationTestCase

    -

    - If you want to use instrumentation methods in a test case class, you must use - {@link android.test.InstrumentationTestCase} or one of its subclasses. The - {@link android.app.Activity} test cases extend this base class with other functionality that - assists in Activity testing. + You use the JUnit {@link junit.framework.Assert} class to verify the correctness of an object's + state. The assert methods compare values you expect from a test to the actual results and + throw an exception if the comparison fails. + Assertion classes describes these methods in more detail.

    -

    Assertion classes

    -

    - Because Android test case classes extend JUnit, you can use assertion methods to display the - results of tests. An assertion method compares an actual value returned by a test to an - expected value, and throws an AssertionException if the comparison test fails. Using assertions - is more convenient than doing logging, and provides better test performance. -

    -

    - Besides the JUnit {@link junit.framework.Assert} class methods, the testing API also provides - the {@link android.test.MoreAsserts} and {@link android.test.ViewAsserts} classes: -

    -
      -
    • - {@link android.test.MoreAsserts} contains more powerful assertions such as - {@link android.test.MoreAsserts#assertContainsRegex}, which does regular expression - matching. -
    • -
    • - {@link android.test.ViewAsserts} contains useful assertions about Views. For example - it contains {@link android.test.ViewAsserts#assertHasScreenCoordinates} that tests if a View - has a particular X and Y position on the visible screen. These asserts simplify testing of - geometry and alignment in the UI. -
    • -
    -

    Mock object classes

    -

    - To facilitate dependency injection in testing, Android provides classes that create mock system - objects such as {@link android.content.Context} objects, - {@link android.content.ContentProvider} objects, {@link android.content.ContentResolver} - objects, and {@link android.app.Service} objects. Some test cases also provide mock - {@link android.content.Intent} objects. You use these mocks both to isolate tests - from the rest of the system and to facilitate dependency injection for testing. These classes - are found in the packages {@link android.test} and {@link android.test.mock}. -

    -

    - Mock objects isolate tests from a running system by stubbing out or overriding - normal operations. For example, a {@link android.test.mock.MockContentResolver} - replaces the normal resolver framework with its own local framework, which is isolated - from the rest of the system. MockContentResolver also stubs out the - {@link android.content.ContentResolver#notifyChange(Uri, ContentObserver, boolean)} method - so that observer objects outside the test environment are not accidentally triggered.

    +

    Instrumentation

    - Mock object classes also facilitate dependency injection by providing a subclass of the - normal object that is non-functional except for overrides you define. For example, the - {@link android.test.mock.MockResources} object provides a subclass of - {@link android.content.res.Resources} in which all the methods throw Exceptions when called. - To use it, you override only those methods that must provide information. + Android instrumentation is a set of control methods or hooks in the Android system. These + hooks control an Android component independently of its normal lifecycle. They also control how + Android loads applications.

    - These are the mock object classes available in Android: + The following diagram summarizes the testing framework:

    -

    Simple mock object classes

    +
    + + The Android testing framework + +

    - {@link android.test.mock.MockApplication}, {@link android.test.mock.MockContext}, - {@link android.test.mock.MockContentProvider}, {@link android.test.mock.MockCursor}, - {@link android.test.mock.MockDialogInterface}, {@link android.test.mock.MockPackageManager}, and - {@link android.test.mock.MockResources} provide a simple and useful mock strategy. They are - stubbed-out versions of the corresponding system object class, and all of their methods throw an - {@link java.lang.UnsupportedOperationException} exception if called. To use them, you override - the methods you need in order to provide mock dependencies. -

    -

    Note: - {@link android.test.mock.MockContentProvider} - and {@link android.test.mock.MockCursor} are new as of API level 8. +Normally, an Android component runs in a lifecycle that the system determines. For example, an +{@link android.app.Activity} object's lifecycle starts when an {@link android.content.Intent} +activates the {@link android.app.Activity}. The system calls the object's onCreate() +method, on then the onResume() method. When the user starts another application, the +system calls the onPause() method. If the {@link android.app.Activity} code calls +the finish() method, the system calls the onDestroy() method. The Android +framework API does not provide a way for your code to invoke these callback methods directly, but +you can do so using instrumentation.

    -

    Resolver mock objects

    - {@link android.test.mock.MockContentResolver} provides isolated testing of content providers by - masking out the normal system resolver framework. Instead of looking in the system to find a - content provider given an authority string, MockContentResolver uses its own internal table. You - must explicitly add providers to this table using - {@link android.test.mock.MockContentResolver#addProvider(String,ContentProvider)}. +The system runs all the components of an application in the same process. You can allow some +components, such as content providers, to run in a separate process, +but you can't force an application to run in the same process as another application that is +already running.

    - With this feature, you can associate a mock content provider with an authority. You can create - an instance of a real provider but use test data in it. You can even set the provider for an - authority to null. In effect, a MockContentResolver object isolates your test - from providers that contain real data. You can control the - function of the provider, and you can prevent your test from affecting real data. +Instrumentation can load both a test package and the app under test into the +same process. Since the application components and their tests are in the same process, your +tests can invoke methods in the components, and modify and examine fields in the components.

    -

    Contexts for testing

    +

    Android Testing Support Library APIs

    - Android provides two Context classes that are useful for testing: -

    +The Android Testing Support Library +provides a set of APIs that allow you to quickly build and run test code for your apps, including +JUnit 4 and functional user interface (UI) tests. The library includes the following +instrumentation-based APIs that are useful when you want to automate your tests:

      -
    • - {@link android.test.IsolatedContext} provides an isolated {@link android.content.Context}, - File, directory, and database operations that use this Context take place in a test area. - Though its functionality is limited, this Context has enough stub code to respond to - system calls. -

      - This class allows you to test an application's data operations without affecting real - data that may be present on the device. -

      +
    • + {@code AndroidJUnitRunner}: + JUnit 4-compatible test runner for Android
    • -
    • - {@link android.test.RenamingDelegatingContext} provides a Context in which - most functions are handled by an existing {@link android.content.Context}, but - file and database operations are handled by a {@link android.test.IsolatedContext}. - The isolated part uses a test directory and creates special file and directory names. - You can control the naming yourself, or let the constructor determine it automatically. -

      - This object provides a quick way to set up an isolated area for data operations, - while keeping normal functionality for all other Context operations. -

      + +
    • Espresso: + UI testing framework; suitable for functional UI testing within an app
    • -
    -

    Running Tests

    -

    - Test cases are run by a test runner class that loads the test case class, set ups, - runs, and tears down each test. An Android test runner must also be instrumented, so that - the system utility for starting applications can control how the test package - loads test cases and the application under test. You tell the Android platform - which instrumented test runner to use by setting a value in the test package's manifest file. -

    -

    - {@link android.test.InstrumentationTestRunner} is the primary Android test runner class. It - extends the JUnit test runner framework and is also instrumented. It can run any of the test - case classes provided by Android and supports all possible types of testing. -

    -

    - You specify InstrumentationTestRunner or a subclass in your test package's - manifest file, in the -<instrumentation> - element. Also, InstrumentationTestRunner code resides - in the shared library android.test.runner, which is not normally linked to - Android code. To include it, you must specify it in a -<uses-library> - element. You do not have to set up these elements yourself. Both Eclipse with ADT and the - android command-line tool construct them automatically and add them to your - test package's manifest file. -

    -

    - Note: If you use a test runner other than - InstrumentationTestRunner, you must change the <instrumentation> - element to point to the class you want to use. -

    -

    - To run {@link android.test.InstrumentationTestRunner}, you use internal system classes called by - Android tools. When you run a test in Eclipse with ADT, the classes are called automatically. - When you run a test from the command line, you run these classes with - Android Debug Bridge (adb). -

    -

    - The system classes load and start the test package, kill any processes that - are running an instance of the application under test, and then load a new instance of the - application under test. They then pass control to - {@link android.test.InstrumentationTestRunner}, which runs - each test case class in the test package. You can also control which test cases and - methods are run using settings in Eclipse with ADT, or using flags with the command-line tools. -

    -

    - Neither the system classes nor {@link android.test.InstrumentationTestRunner} run - the application under test. Instead, the test case does this directly. It either calls methods - in the application under test, or it calls its own methods that trigger lifecycle events in - the application under test. The application is under the complete control of the test case, - which allows it to set up the test environment (the test fixture) before running a test. This - is demonstrated in the previous code snippet that tests an - Activity that displays a Spinner widget. -

    -

    - To learn more about running tests, please read the topics - - Testing from Eclipse with ADT or - - Testing from Other IDEs. -

    -

    Seeing Test Results

    -

    - The Android testing framework returns test results back to the tool that started the test. - If you run a test in Eclipse with ADT, the results are displayed in a new JUnit view pane. If - you run a test from the command line, the results are displayed in STDOUT. In - both cases, you see a test summary that displays the name of each test case and method that - was run. You also see all the assertion failures that occurred. These include pointers to the - line in the test code where the failure occurred. Assertion failures also list the expected - value and actual value. -

    -

    - The test results have a format that is specific to the IDE that you are using. The test - results format for Eclipse with ADT is described in - - Testing from Eclipse with ADT. The test results format for tests run from the - command line is described in - - Testing from Other IDEs. -

    -

    monkey and monkeyrunner

    + +
  • UI Automator: + UI testing framework; suitable for cross-app functional UI testing across system and installed + apps
  • + + +

    Assertion classes

    +

    Because Android Testing Support Library APIs extend JUnit, you can use assertion methods to +display the results of tests. An assertion method compares an actual value returned by a test to an +expected value, and throws an AssertionException if the comparison test fails. Using assertions +is more convenient than logging, and provides better test performance. +

    +

    To simplify your test development, we recommend that you use the +Hamcrest library, which lets you create more flexible tests using the +Hamcrest matcher APIs.

    + +

    Monkey and Monkeyrunner

    The SDK provides two tools for functional-level application testing:

    @@ -569,72 +289,4 @@ The UI/Application Exerciser Monkeymonkeyrunner command-line tool. - -

    Working With Package names

    -

    - In the test environment, you work with both Android application package names and - Java package identifiers. Both use the same naming format, but they represent substantially - different entities. You need to know the difference to set up your tests correctly. -

    -

    - An Android package name is a unique system name for a .apk file, set by the - "android:package" attribute of the <manifest> element in the package's - manifest. The Android package name of your test package must be different from the - Android package name of the application under test. By default, Android tools create the - test package name by appending ".test" to the package name of the application under test. -

    -

    - The test package also uses an Android package name to target the application package it - tests. This is set in the "android:targetPackage" attribute of the - <instrumentation> element in the test package's manifest. -

    -

    - A Java package identifier applies to a source file. This package name reflects the directory - path of the source file. It also affects the visibility of classes and members to each other. -

    -

    - Android tools that create test projects set up an Android test package name for you. - From your input, the tools set up the test package name and the target package name for the - application under test. For these tools to work, the application project must already exist. -

    -

    - By default, these tools set the Java package identifier for the test class to be the same - as the Android package identifier. You may want to change this if you want to expose - members in the application under test by giving them package visibility. If you do this, - change only the Java package identifier, not the Android package names, and change only the - test case source files. Do not change the Java package name of the generated - R.java class in your test package, because it will then conflict with the - R.java class in the application under test. Do not change the Android package name - of your test package to be the same as the application it tests, because then their names - will no longer be unique in the system. -

    -

    What to Test

    -

    - The topic What To Test - describes the key functionality you should test in an Android application, and the key - situations that might affect that functionality. -

    -

    - Most unit testing is specific to the Android component you are testing. - The topics Activity Testing, - - Content Provider Testing, and - Service Testing each have a section entitled "What To Test" that lists possible testing - areas. -

    -

    - When possible, you should run these tests on an actual device. If this is not possible, you can - use the Android Emulator with - Android Virtual Devices configured for the hardware, screens, and versions you want to test. -

    -

    Next Steps

    -

    - To learn how to set up and run tests in Eclipse, please refer to -Testing from Eclipse with ADT. - If you're not working in Eclipse, refer to -Testing from Other IDEs. -

    -

    - If you want a step-by-step introduction to Android testing, try the - Activity Testing Tutorial. -

    + \ No newline at end of file diff --git a/docs/html/tools/testing/testing_eclipse.jd b/docs/html/tools/testing/testing_eclipse.jd deleted file mode 100644 index 6c9d55b64a83e91e75e818f096ba3e09232e245c..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/testing_eclipse.jd +++ /dev/null @@ -1,535 +0,0 @@ -page.title=Testing from Eclipse with ADT -parent.title=Testing -parent.link=index.html -@jd:body - -

    - This topic explains how create and run tests of Android applications in Eclipse with ADT. - Before you read this topic, you should read about how to create an Android application with the - basic processes for creating and running applications with ADT, as described in - Managing Projects from -Eclipse - and Building and Running -from Eclipse. - You may also want to read - Testing Fundamentals, - which provides an overview of the Android testing framework. -

    -

    - ADT provides several features that help you set up and manage your testing environment - effectively: -

    -
      -
    • - It lets you quickly create a test project and link it to the application under test. - When it creates the test project, it automatically inserts the necessary - <instrumentation> element in the test package's manifest file. -
    • -
    • - It lets you quickly import the classes of the application under test, so that your - tests can inspect them. -
    • -
    • - It lets you create run configurations for your test package and include in - them flags that are passed to the Android testing framework. -
    • -
    • - It lets you run your test package without leaving Eclipse. ADT builds both the - application under test and the test package automatically, installs them if - necessary to your device or emulator, runs the test package, and displays the - results in a separate window in Eclipse. -
    • -
    -

    - If you are not developing in Eclipse or you want to learn how to create and run tests from the - command line, see - Testing from Other IDEs. -

    -

    Creating a Test Project

    -

    - To set up a test environment for your Android application, you must first create a separate - project that holds the test code. The new project follows the directory structure - used for any Android application. It includes the same types of content and files, such as - source code, resources, a manifest file, and so forth. The test package you - create is connected to the application under test by an - - <instrumentation> element in its manifest file. -

    -

    - The New Android Test Project dialog makes it easy for you to generate a - new test project that has the proper structure, including the - <instrumentation> element in the manifest file. You can use the New - Android Test Project dialog to generate the test project at any time. The dialog appears - just after you create a new Android main application project, but you can also run it to - create a test project for a project that you created previously. -

    -

    - To create a test project in Eclipse with ADT: -

    -
      -
    1. - In Eclipse, select File > New > Other. This opens the Select a - Wizard dialog. -
    2. -
    3. - In the dialog, in the Wizards drop-down list, find the entry for Android, then - click the toggle to the left. Select Android Test Project, then at the - bottom of the dialog click Next. The New Android Test Project - wizard appears. -
    4. -
    5. - Next to Test Project Name, enter a name for the project. You may use any name, - but you may want to associate the name with the project name for the application under test. - One way to do this is to take the application's project name, append the string "Test" to - it, and then use this as the test package project name. -

      - The name becomes part of the suggested project path, but you can change this in the - next step. -

      -
    6. -
    7. - In the Content panel, examine the suggested path to the project. - If Use default location is set, then the wizard will suggest a path that is - a concatenation of the workspace path and the project name you entered. For example, - if your workspace path is /usr/local/workspace and your project name is - MyTestApp, then the wizard will suggest - /usr/local/workspace/MyTestApp. To enter your own - choice for a path, unselect Use default location, then enter or browse to the - path where you want your project. -

      - To learn more about choosing the location of test projects, please read - - Testing Fundamentals. -

      -
    8. -
    9. - In the Test Target panel, set An Existing Android Project, click Browse, then select your - Android application from the list. You now see that the wizard has completed the Test - Target Package, Application Name, and Package Name fields for you (the latter two are in - the Properties panel). -
    10. -
    11. - In the Build Target panel, select the Android SDK platform that the application under test - uses. -
    12. -
    13. - Click Finish to complete the wizard. If Finish is disabled, look for error messages at the - top of the wizard dialog, and then fix any problems. -
    14. -
    -

    Creating a Test Package

    -

    - Once you have created a test project, you populate it with a test package. This package does not - require an Activity, although you can define one if you wish. Although your test package can - combine Activity classes, test case classes, or ordinary classes, your main test case - should extend one of the Android test case classes or JUnit classes, because these provide the - best testing features. -

    -

    - Test packages do not need to have an Android GUI. When you run the package in - Eclipse with ADT, its results appear in the JUnit view. Running tests and seeing the results is - described in more detail in the section Running Tests. -

    - -

    - To create a test package, start with one of Android's test case classes defined in - {@link android.test android.test}. These extend the JUnit - {@link junit.framework.TestCase TestCase} class. The Android test classes for Activity objects - also provide instrumentation for testing an Activity. To learn more about test case - classes, please read the topic - Testing Fundamentals. -

    -

    - Before you create your test package, you choose the Java package identifier you want to use - for your test case classes and the Android package name you want to use. To learn more - about this, please read - - Testing Fundamentals. -

    -

    - To add a test case class to your project: -

    -
      -
    1. - In the Project Explorer tab, open your test project, then open the src - folder. -
    2. -
    3. - Find the Java package identifier set by the projection creation wizard. If you haven't - added classes yet, this node won't have any children, and its icon will not be filled in. - If you want to change the identifier value, right-click the identifier and select - Refactor > Rename, then enter the new name. -
    4. -
    5. - When you are ready, right-click the Java package identifier again and select - New > Class. This displays the New Java Class - dialog, with the Source folder and Package values already set. -
    6. -
    7. - In the Name field, enter a name for the test case class. One way to choose a - class name is to append the string "Test" to the class of the component you are testing. - For example, if you are testing the class MyAppActivity, your test case class - name would be MyAppActivityTest. Leave the modifiers set to public. -
    8. -
    9. - In the Superclass field, enter the name of the Android test case class you - are extending. You can also browse the available classes. -
    10. -
    11. - In Which method stubs would you like to create?, unset all the options, then - click Finish. You will set up the constructor manually. -
    12. -
    13. - Your new class appears in a new Java editor pane. -
    14. -
    -

    - You now have to ensure that the constructor is set up correctly. Create a constructor for your - class that has no arguments; this is required by JUnit. As the first statement in this - constructor, add a call to the base class' constructor. Each base test case class has its - own constructor signature. Refer to the class documentation in the documentation for - {@link android.test} for more information. -

    -

    - To control your test environment, you will want to override the setUp() and - tearDown() methods: -

    -
      -
    • - setUp(): This method is invoked before any of the test methods in the class. - Use it to set up the environment for the test (the test fixture. You can use - setUp() to instantiate a new Intent with the action ACTION_MAIN. - You can then use this intent to start the Activity under test. -
    • -
    • - tearDown(): This method is invoked after all the test methods in the class. Use - it to do garbage collection and to reset the test fixture. -
    • -
    -

    - Another useful convention is to add the method testPreconditions() to your test - class. Use this method to test that the application under test is initialized correctly. If this - test fails, you know that the initial conditions were in error. When this happens, further - test results are suspect, regardless of whether or not the tests succeeded. -

    -

    - The Resources tab contains an - Activity Testing - tutorial with more information about creating test classes and methods. -

    -

    Running Tests

    - -

    - When you run a test package in Eclipse with ADT, the output appears in the Eclipse JUnit view. - You can run the entire test package or one test case class. To do run tests, Eclipse runs the - adb command for running a test package, and displays the output, so there is no - difference between running tests inside Eclipse and running them from the command line. -

    -

    - As with any other package, to run a test package in Eclipse with ADT you must either attach a - device to your computer or use the Android emulator. If you use the emulator, you must have an - Android Virtual Device (AVD) that uses the same target as the test package. -

    -

    - To run a test in Eclipse, you have two choices:

    -
      -
    • - Run a test just as you run an application, by selecting - Run As... > Android JUnit Test from the project's context menu or - from the main menu's Run item. -
    • -
    • - Create an Eclipse run configuration for your test project. This is useful if you want - multiple test suites, each consisting of selected tests from the project. To run - a test suite, you run the test configuration. -

      - Creating and running test configurations is described in the next section. -

      -
    • -
    -

    - To create and run a test suite using a run configuration: -

    -
      -
    1. - In the Package Explorer, select the test project, then from the main menu, select - Run > Run Configurations.... The Run Configurations dialog appears. -
    2. -
    3. - In the left-hand pane, find the Android JUnit Test entry. In the right-hand pane, click the - Test tab. The Name: text box shows the name of your project. The Test class: dropdown box - shows one of the test classes in your project. -
    4. -
    5. - To run one test class, click Run a single test, then enter your project name in the - Project: text box and the class name in the Test class: text box. -

      - To run all the test classes, click Run all tests in the selected project or package, - then enter the project or package name in the text box. -

      -
    6. -
    7. - Now click the Target tab. -
        -
      • - Optional: If you are using the emulator, click Automatic, then in the Android - Virtual Device (AVD) selection table, select an existing AVD. -
      • -
      • - In the Emulator Launch Parameters pane, set the Android emulator flags you want to - use. These are documented in the topic - - Android Emulator. -
      • -
      -
    8. -
    9. - Click the Common tab. In the Save As pane, click Local to save this run configuration - locally, or click Shared to save it to another project. -
    10. -
    11. - Optional: Add the configuration to the Run toolbar and the Favorites - menu: in the Display in Favorites pane click the checkbox next to Run. -
    12. -
    13. - Optional: To add this configuration to the Debug menu and toolbar, click - the checkbox next to Debug. -
    14. -
    15. - To save your settings, click Close.
      -

      Note: - Although you can run the test immediately by clicking Run, you should save the test - first and then run it by selecting it from the Eclipse standard toolbar. -

      -
    16. -
    17. - On the Eclipse standard toolbar, click the down arrow next to the green Run arrow. This - displays a menu of saved Run and Debug configurations. -
    18. -
    19. - Select the test run configuration you just created. The test starts. -
    20. -
    -

    - The progress of your test appears in the Console view as a series of messages. Each message is - preceded by a timestamp and the .apk filename to which it applies. For example, - this message appears when you run a test to the emulator, and the emulator is not yet started: -

    - -
    -    [yyyy-mm-dd hh:mm:ss - testfile] Waiting for HOME ('android.process.acore') to be launched...
    -
    -

    - In the following description of these messages, devicename is the name of - the device or emulator you are using to run the test, and port is the - port number for the device. The name and port number are in the format used by the - adb devices - command. Also, testfile is the .apk filename of the test - package you are running, and appfile is the filename of the application under test. -

    -
      -
    • - If you are using an emulator and you have not yet started it, then Eclipse - first starts the emulator. When this is complete, you see - the message: -

      - HOME is up on device 'devicename-port' -

      -
    • -
    • - If you have not already installed your test package, then you see - the message: -

      - Uploading testfile onto device 'devicename-port' - -

      -

      - then the message Installing testfile. -

      -

      - and finally the message Success! -

      -
    • -
    -

    - The following lines are an example of this message sequence: -

    - -[2010-07-01 12:44:40 - MyTest] HOME is up on device 'emulator-5554'
    -[2010-07-01 12:44:40 - MyTest] Uploading MyTest.apk onto device 'emulator-5554'
    -[2010-07-01 12:44:40 - MyTest] Installing MyTest.apk...
    -[2010-07-01 12:44:49 - MyTest] Success!
    -
    -
    -
      -
    • - Next, if you have not yet installed the application under test to the device or - emulator, you see the message -

      - Project dependency found, installing: appfile -

      -

      - then the message Uploading appfile onto device - 'devicename-port' -

      -

      - then the message Installing appfile -

      -

      - and finally the message Success! -

      -
    • -
    -

    - The following lines are an example of this message sequence: -

    - -[2010-07-01 12:44:49 - MyTest] Project dependency found, installing: MyApp
    -[2010-07-01 12:44:49 - MyApp] Uploading MyApp.apk onto device 'emulator-5554'
    -[2010-07-01 12:44:49 - MyApp] Installing MyApp.apk...
    -[2010-07-01 12:44:54 - MyApp] Success!
    -
    -
    -
      -
    • - Next, you see the message - Launching instrumentation instrumentation_class on device - devicename-port -

      - instrumentation_class is the fully-qualified class name of the - instrumentation test runner you have specified (usually - {@link android.test.InstrumentationTestRunner}. -

      -
    • -
    • - Next, as {@link android.test.InstrumentationTestRunner} builds a list of tests to run, - you see the message -

      - Collecting test information -

      -

      - followed by -

      -

      - Sending test information to Eclipse -

      -
    • -
    • - Finally, you see the message Running tests, which indicates that your tests - are running. At this point, you should start seeing the test results in the JUnit view. - When the tests are finished, you see the console message Test run complete. - This indicates that your tests are finished. -
    • -
    -

    - The following lines are an example of this message sequence: -

    - -[2010-01-01 12:45:02 - MyTest] Launching instrumentation android.test.InstrumentationTestRunner on device emulator-5554
    -[2010-01-01 12:45:02 - MyTest] Collecting test information
    -[2010-01-01 12:45:02 - MyTest] Sending test information to Eclipse
    -[2010-01-01 12:45:02 - MyTest] Running tests...
    -[2010-01-01 12:45:22 - MyTest] Test run complete
    -
    -
    -

    - The test results appear in the JUnit view. This is divided into an upper summary pane, - and a lower stack trace pane. -

    -

    - The upper pane contains test information. In the pane's header, you see the following - information: -

    -
      -
    • - Total time elapsed for the test package (labeled Finished after x seconds). -
    • -
    • - Number of runs (Runs:) - the number of tests in the entire test class. -
    • -
    • - Number of errors (Errors:) - the number of program errors and exceptions encountered - during the test run. -
    • -
    • - Number of failures (Failures:) - the number of test failures encountered during the test - run. This is the number of assertion failures. A test can fail even if the program does - not encounter an error. -
    • -
    • - A progress bar. The progress bar extends from left to right as the tests run. If all the - tests succeed, the bar remains green. If a test fails, the bar turns from green to red. -
    • -
    -

    - The body of the upper pane contains the details of the test run. For each test case class - that was run, you see a line with the class name. To look at the results for the individual - test methods in that class, you click the left arrow to expand the line. You now see a - line for each test method in the class, and to its right the time it took to run. - If you double-click the method name, Eclipse opens the test class source in an editor view - pane and moves the focus to the first line of the test method. -

    -

    - The results of a successful test are shown in figure 1. -

    - - Messages for a successful test - -

    - Figure 1. Messages for a successful test. -

    -

    - The lower pane is for stack traces. If you highlight a failed test in the upper pane, the - lower pane contains a stack trace for the test. If a line corresponds to a point in your - test code, you can double-click it to display the code in an editor view pane, with the - line highlighted. For a successful test, the lower pane is empty. -

    -

    The results of a failed test are shown in figure 2.

    - - - -

    - Figure 2. Messages for a test failure. -

    diff --git a/docs/html/tools/testing/testing_otheride.jd b/docs/html/tools/testing/testing_otheride.jd old mode 100644 new mode 100755 index a77408747f80812f9860e35528ba2dcdaaf43c9a..4b2a6b14680b8dc77abbdc0b583997c4b3350635 --- a/docs/html/tools/testing/testing_otheride.jd +++ b/docs/html/tools/testing/testing_otheride.jd @@ -1,34 +1,19 @@ -page.title=Testing from Other IDEs -parent.title=Testing -parent.link=index.html +page.title=Testing from the Command-Line + @jd:body

    - This document describes how to create and run tests directly from the command line. - You can use the techniques described here if you are developing in an IDE other than Eclipse - or if you prefer to work from the command line. This document assumes that you already know how - to create a Android application in your programming environment. Before you start this - document, you should read the topic - Testing Fundamentals, - which provides an overview of Android testing. -

    -

    - If you are developing in Eclipse with ADT, you can set up and run your tests - directly in Eclipse. For more information, please read - - Testing from Eclipse with ADT. + This document describes how to create and run tests directly from the command line. This + document assumes that you already know how to create a Android application in your programming + environment.

    -

    Working with Test Projects

    -

    - You use the android tool to create test projects. - You also use android to convert existing test code into an Android test project, - or to add the test Ant target to an existing Android test project. - These operations are described in more detail in the section - Updating a test project. The test target is described in - Quick build and run with Ant. -

    -

    Creating a test project

    -

    - To create a test project with the android tool, enter: -

    -
    -android create test-project -m <main_path> -n <project_name> -p <test_path>
    -
    + +

    Running Tests

    - You must supply all the flags. The following table explains them in detail: + You can run tests from the command-line, either with Gradle or with an + + Android Debug Bridge (adb) shell.

    +

    Running unit tests with Gradle

    + +

    The Android Plugin for Gradle +lets you run unit tests from your Gradle project via the command-line. For more information on +how to build unit tests for your app, see +Building Effective Unit Tests.

    + +

    The table below summarizes how to run your unit tests with Gradle:

    - - - - - - - - + + + - - - + + + - - - - -
    FlagValueDescription
    -m, --main - Path to the project of the application under test, relative to the test package - directory. - - For example, if the application under test is in source/HelloAndroid, and - you want to create the test project in source/HelloAndroidTest, then the - value of --main should be ../HelloAndroid. -

    - To learn more about choosing the location of test projects, please read - - Testing Fundamentals. -

    -
    Unit Test TypeCommand To RunTest Result Location
    -n, --nameName that you want to give the test project. Local unit testCall the {@code test} task: +
    +./gradlew test
    +
    +HTML test result files: +{@code <path_to_your_project>/app/build/reports/tests/} directory. +

    XML test result files: +{@code <path_to_your_project>/app/build/test-results/} directory. +

    -p, --pathDirectory in which you want to create the new test project. - The android tool creates the test project files and directory structure - in this directory. If the directory does not exist, android creates it. -
    -

    - If the operation is successful, android lists to STDOUT the names of the files - and directories it has created. -

    -

    - This creates a new test project with the appropriate directories and build files. The directory - structure and build file contents are identical to those in a regular Android application - project. They are described in detail in the topic - Managing Projects. -

    -

    - The operation also creates an AndroidManifest.xml file with instrumentation - information. When you run the test, Android uses this information to load the application you - are testing and control it with instrumentation. -

    -

    - For example, suppose you create a project in the directory ~/source/HelloAndroid, -with the package name com.example.helloandroid, - and the activity name HelloAndroid. You can to create the test for this in - ~/source/HelloAndroidTest. To do so, you enter: -

    + Instrumented unit test + Call the {@code connectedAndroidTest} (or {@code cAT}) task:
    -$ cd ~/source
    -$ android create test-project -m ../HelloAndroid -n HelloAndroidTest -p HelloAndroidTest
    +./gradlew cAT
     
    -

    - This creates a directory called ~/src/HelloAndroidTest. In the new directory you - see the file AndroidManifest.xml. This file contains the following - instrumentation-related elements and attributes: -

    -
      -
    • - <application>: to contain the - <uses-library> element. -
    • -
    • - <uses-library android:name="android.test.runner": - specifies this testing application uses the android.test.runner library. -
    • -
    • - <instrumentation>: contains attributes that control Android - instrumentation. The attributes are: -
        -
      • - android:name="android.test.InstrumentationTestRunner": - {@link android.test.InstrumentationTestRunner} runs test cases. It extends both - JUnit test case runner classes and Android instrumentation classes. -
      • -
      • - android:targetPackage="com.example.helloandroid": specifies - that the tests in HelloAndroidTest should be run against the application with the - Android package name com.example.helloandroid. -
      • -
      • - android:label="Tests for .HelloAndroid": specifies a - user-readable label for the instrumentation class. By default, - the android tool gives it the value "Tests for " plus - the name of the main Activity of the application under test. -
      • -
      -
    • -
    -

    Updating a test project

    -

    - You use the android tool when you need to change the path to the - project of the application under test. If you are changing an existing test project created in - Eclipse with ADT so that you can also build and run it from the command line, you must use the - "create" operation. See the section Creating a test project. -

    -

    - Note: If you change the Android package name of the application under test, - you must manually change the value of the <android:targetPackage> - attribute within the AndroidManifest.xml file of the test package. - Running android update test-project does not do this. -

    -

    - To update a test project with the android tool, enter: -

    -
    android update test-project -m <main_path> -p <test_path>
    - - - - - - - - - - - - - - - +HTML test result files: +{@code <path_to_your_project>/app/build/outputs/reports/androidTests/connected/} directory. +

    XML test result files: +{@code <path_to_your_project>/app/build/outputs/androidTest-results/connected/} directory. +

    FlagValueDescription
    -m, --mainThe path to the project of the application under test, relative to the test project - For example, if the application under test is in source/HelloAndroid, and - the test project is in source/HelloAndroidTest, then the value for - --main is ../HelloAndroid.
    -p, --pathThe of the test project. - For example, if the test project is in source/HelloAndroidTest, then the - value for --path is HelloAndroidTest. -
    -

    - If the operation is successful, android lists to STDOUT the names of the files - and directories it has created. -

    -

    Creating a Test Package

    -

    - Once you have created a test project, you populate it with a test package. - The application does not require an {@link android.app.Activity Activity}, - although you can define one if you wish. Although your test package can - combine Activities, Android test class extensions, JUnit extensions, or - ordinary classes, you should extend one of the Android test classes or JUnit classes, - because these provide the best testing features. -

    -

    - If you run your tests with {@link android.test.InstrumentationTestRunner} - (or a related test runner), then it will run all the methods in each class. You can modify - this behavior by using the {@link junit.framework.TestSuite TestSuite} class. -

    +

    Running tests with ADB

    - To create a test package, start with one of Android's test classes in the Java package - {@link android.test android.test}. These extend the JUnit - {@link junit.framework.TestCase TestCase} class. With a few exceptions, the Android test - classes also provide instrumentation for testing. -

    -

    - For test classes that extend {@link junit.framework.TestCase TestCase}, you probably want to - override the setUp() and tearDown() methods: -

    -
      -
    • - setUp(): This method is invoked before any of the test methods in the class. - Use it to set up the environment for the test. You can use setUp() - to instantiate a new Intent object with the action ACTION_MAIN. - You can then use this intent to start the Activity under test. -

      - Note: If you override this method, call - super.setUp() as the first statement in your code. -

      -
    • -
    • - tearDown(): This method is invoked after all the test methods in the class. Use - it to do garbage collection and re-setting before moving on to the next set of tests. -

      Note: If you override this method, you must call - super.tearDown() as the last statement in your code.

      -
    • -
    -

    - Another useful convention is to add the method testPreConditions() to your test - class. Use this method to test that the application under test is initialized correctly. If this - test fails, you know that the initial conditions were in error. When this happens, further - test results are suspect, regardless of whether or not the tests succeeded. -

    -

    - To learn more about creating test packages, see the topic Testing Fundamentals, - which provides an overview of Android testing. If you prefer to follow a tutorial, - try the Activity Testing - tutorial, which leads you through the creation of tests for an actual Android application. -

    -

    Running Tests

    -

    - You run tests from the command line, either with Ant or with an - - Android Debug Bridge (adb) shell. -

    -

    Quick build and run with Ant

    -

    - You can use Ant to run all the tests in your test project, using the target - test, which is created automatically when you create a test project with - the android tool. -

    -

    - This target re-builds your main project and test project if necessary, installs the test - application to the current AVD or device, and then runs all the test classes in the test - application. The results are directed to STDOUT. -

    -

    - You can update an existing test project to use this feature. To do this, use the - android tool with the update test-project option. This is described - in the section Updating a test project. -

    -

    Running tests on a device or emulator

    -

    - When you run tests from the command line with + When you run tests from the command-line with Android Debug Bridge (adb), you get more options for choosing the tests to run than with any other method. You can select individual test methods, filter tests according to their annotation, or specify testing options. Since the test run is controlled - entirely from a command line, you can customize your testing with shell scripts in various ways. + entirely from a command-line, you can customize your testing with shell scripts in various ways.

    - To run a test from the command line, you run adb shell to start a command-line + To run a test from the command-line, you run adb shell to start a command-line shell on your device or emulator, and then in the shell run the am instrument command. You control am and your tests with command-line flags.

    As a shortcut, you can start an adb shell, call am instrument, and specify command-line flags all on one input line. The shell opens on the device or emulator, - runs your tests, produces output, and then returns to the command line on your computer. + runs your tests, produces output, and then returns to the command-line on your computer.

    To run a test with am instrument: @@ -343,7 +122,7 @@ $ android create test-project -m ../HelloAndroid -n HelloAndroidTest -p HelloAnd Install your test package and main application Android package files (.apk files) to your current Android device or emulator

  • - At the command line, enter: + At the command-line, enter:
     $ adb shell am instrument -w <test_package_name>/<runner_class>
     
    @@ -353,7 +132,9 @@ $ adb shell am instrument -w <test_package_name>/<runner_class> runner class you are using. The Android package name is the value of the package attribute of the manifest element in the manifest file (AndroidManifest.xml) of your test package. The Android test runner - class is usually {@link android.test.InstrumentationTestRunner}. + class is usually + +{@code AndroidJUnitRunner}.

    Your test results appear in STDOUT. @@ -371,7 +152,7 @@ $ adb shell am instrument -w <test_package_name>/<runner_class> The general syntax of the am instrument command is:

    -    am instrument [flags] <test_package>/<runner_class>
    +am instrument [flags] <test_package>/<runner_class>
     

    The main input parameters to am instrument are described in the following table: @@ -408,10 +189,13 @@ $ adb shell am instrument -w <test_package_name>/<runner_class> The class name of the instrumented test runner you are using. - This is usually {@link android.test.InstrumentationTestRunner}. + This is usually + +{@code AndroidJUnitRunner}. +

    The flags for am instrument are described in the following table:

    @@ -467,12 +251,11 @@ $ adb shell am instrument -w <test_package_name>/<runner_class> am instrument tool passes these to the specified instrumentation class via its onCreate() method. You can specify multiple occurrences of -e <test_options>. The keys and values are described in the - section am instrument options. -

    - The only instrumentation class that uses these key-value pairs is - {@link android.test.InstrumentationTestRunner} (or a subclass). Using them with - any other class has no effect. -

    + section am instrument options. You can only use these + key-value pairs with + +{@code AndroidJUnitRunner} or with {@link android.test.InstrumentationTestRunner} and its +subclasses. Using them with any other class has no effect. @@ -480,25 +263,24 @@ $ adb shell am instrument -w <test_package_name>/<runner_class>

    am instrument options

    The am instrument tool passes testing options to - InstrumentationTestRunner or a subclass in the form of key-value pairs, - using the -e flag, with this syntax: + +{@code AndroidJUnitRunner} or {@link android.test.InstrumentationTestRunner} in the form of +key-value pairs, using the -e flag, with this syntax:

    -    -e <key> <value>
    +-e <key> <value>
     

    Some keys accept multiple values. You specify multiple values in a comma-separated list. - For example, this invocation of InstrumentationTestRunner provides multiple - values for the package key: + For example, this invocation of + +{@code AndroidJUnitRunner} provides multiple values for the package key:

     $ adb shell am instrument -w -e package com.android.test.package1,com.android.test.package2 \
    -> com.android.test/android.test.InstrumentationTestRunner
    +> com.android.test/android.support.test.runner.AndroidJUnitRunner
     
    -

    - The following table describes the key-value pairs and their result. Please review the - Usage Notes following the table. -

    +

    The following table lists the key-value pairs you can use with your test runner.

    @@ -556,8 +338,8 @@ $ adb shell am instrument -w -e package com.android.test.package1,com.android.te [small | medium | large] @@ -591,8 +373,9 @@ $ adb shell am instrument -w -e package com.android.test.package1,com.android.te
    Key - Runs a test method annotated by size. The annotations are @SmallTest, - @MediumTest, and @LargeTest. + Runs a test method annotated by size. The annotations are @SmallTest, + @MediumTest, and @LargeTest.
    true Runs an EMMA code coverage analysis and writes the output to - /data//coverage.ec on the device. To override the file location, use the - coverageFile key that is described in the following entry. + /data/<app_package>/coverage.ec on the device. To override the + file location, use the coverageFile key that is described in the + following entry.

    Note: This option requires an EMMA-instrumented build of the test application, which you can generate with the coverage target. @@ -634,24 +417,16 @@ They are based on the following structure:

    The test package has the Android package name com.android.demo.app.tests
  • - There are three test classes: + Two instrumented test classes:
      -
    • - UnitTests, which contains the methods - testPermissions and testSaveState. -
    • -
    • - FunctionTests, which contains the methods - testCamera, testXVGA, and testHardKeyboard. -
    • -
    • - IntegrationTests, - which contains the method testActivityProvider. -
    • +
    • {@code Foo1} which contains the test method {@code bar1}, and
    • +
    • {@code Foo2} which contains test methods {@code bar2} and {@code bar3}
  • - The test runner is {@link android.test.InstrumentationTestRunner}. + The test runner is + +{@code AndroidJUnitRunner}.
  • Running the entire test package

    @@ -659,7 +434,7 @@ They are based on the following structure:

    To run all of the test classes in the test package, enter:

    -$ adb shell am instrument -w com.android.demo.app.tests/android.test.InstrumentationTestRunner
    +$ adb shell am instrument -w com.android.demo.app.tests/android.support.test.runner.AndroidJUnitRunner
     

    Running all tests in a test case class

    @@ -667,8 +442,8 @@ $ adb shell am instrument -w com.android.demo.app.tests/android.test.Instrumenta

     $ adb shell am instrument -w  \
    -> -e class com.android.demo.app.tests.UnitTests \
    -> com.android.demo.app.tests/android.test.InstrumentationTestRunner
    +> -e class com.android.demo.app.tests.Foo \
    +> com.android.demo.app.tests/android.support.test.runner.AndroidJUnitRunner
     

    am instrument gets the value of the -e flag, detects the @@ -676,15 +451,11 @@ $ adb shell am instrument -w \

    Selecting a subset of tests

    - To run all of the tests in UnitTests, and the testCamera method in - FunctionTests, enter: + To run all of the tests in Foo1, and the bar3 method in + Foo2, enter:

     $ adb shell am instrument -w \
    -> -e class com.android.demo.app.tests.UnitTests,com.android.demo.app.tests.FunctionTests#testCamera \
    -> com.android.demo.app.tests/android.test.InstrumentationTestRunner
    +> -e class com.android.demo.app.tests.Foo1,com.android.demo.app.tests.Foo2#bar3 \
    +> com.android.demo.app.tests/android.support.test.runner.AndroidJUnitRunner
     
    -

    - You can find more examples of the command in the documentation for - {@link android.test.InstrumentationTestRunner}. -

    diff --git a/docs/html/tools/testing/testing_ui.jd b/docs/html/tools/testing/testing_ui.jd deleted file mode 100644 index 3f4bf7a586423129d3584dbeb6cd60a1c55595f5..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/testing_ui.jd +++ /dev/null @@ -1,356 +0,0 @@ -page.title=UI Testing -parent.title=Testing -parent.link=index.html -@jd:body - - - -

    -In addition to unit testing the individual components that make up your Android application (such as activities, services, and content providers), it is also important that you test the behavior of your application’s user interface (UI) when it is running on a device. UI testing ensures that your application returns the correct UI output in response to a sequence of user actions on a device, such as entering keyboard input or pressing toolbars, menus, dialogs, images, and other UI controls. -

    -

    -Functional or black-box UI testing does not require testers to know the internal implementation details of the app, only its expected output when a user performs a specific action or enters a specific input. This approach allows for better separation of development and testing roles in your organization. -

    -

    One common approach to UI testing is to run tests manually and verify that the app is behaving as expected. However, this approach can be time-consuming, tedious, and error-prone. A more efficient and reliable approach is to automate the UI testing with a software testing framework. Automated testing involves creating programs to perform testing tasks (test cases) to cover specific usage scenarios, and then using the testing framework to run the test cases automatically and in a repeatable manner.

    - -

    Overview

    -

    The Android SDK provides the following tools to support automated, functional UI testing on your application: -

      -
    • {@code uiautomatorviewer} - A GUI tool to scan and analyze the UI components of an Android application.
    • -
    • {@code uiautomator} - A Java library containing APIs to create customized functional UI tests, and an execution engine to automate and run the tests.
    • -

    - -

    To use these tools, you must have the following versions of the Android development tools installed: -

      -
    • Android SDK Tools, Revision 21 or higher
    • -
    • Android SDK Platform, API 16 or higher
    • -
    -

    - -

    Workflow for the the uiautomator testing framework

    -

    Here's a short overview of the steps required to automate UI testing: -

      -
    1. Prepare to test by installing the app on a test device, analyzing the app’s UI components, and ensuring that your application is accessible by the test automation framework.
    2. -
    3. Create automated tests to simulate specific user interactions on your application.
    4. -
    5. Compile your test cases into a JAR file and install it on your test device along with your app.
    6. -
    7. Run the tests and view the test results.
    8. -
    9. Correct any bugs or defects discovered in testing.
    10. -
    -

    - -

    Analyzing Your Application's UI

    -

    Before you start writing your test cases, it's helpful to familiarize yourself with the UI components (including the views and controls) of the targeted application. You can use the {@code uiautomatorviewer} tool to take a snapshot of the foreground UI screen on any Android device that is connected to your development machine. The {@code uiautomatorviewer} tool provides a convenient visual interface to inspect the layout hierarchy and view the properties of the individual UI components that are displayed on the test device. Using this information, you can later create {@code uiautomator} tests with selector objects that target specific UI components to test.

    - - - User interface of uiautomatorviewer tool - -

    - Figure 1. The {@code uiautomatorviewer} showing the captured interface of a test device. -

    - -

    To analyze the UI components of the application that you want to test:

    -
      -
    1. Connect your Android device to your development machine.
    2. -
    3. Open a terminal window and navigate to {@code /tools/}.
    4. -
    5. Run the tool with this command:
      $ uiautomatorviewer
    6. -
    7. To capture a screen for analysis, click the Device Screenshot button in the GUI of the {@code uiautomatorviewer} tool.

      -

      Note: If you have more than one device connected, specify the device for screen capture by setting the {@code ANDROID_SERIAL} environment variable: -

        -
      1. Find the serial numbers for your connected devices by running this command:
        $ adb devices
      2. -
      3. Set the {@code ANDROID_SERIAL} environment variable to select the device to test: -
          -
        • In Windows:
          set ANDROID_SERIAL=<device serial number>
        • -
        • In UNIX:
          export ANDROID_SERIAL=<device serial number>
        • -
        -
      4. -
      -If you are connected to only a single device, you do not need to set the ANDROID_SERIAL environment variable.

      -
    8. -
    9. View the UI properties for your application: -
        -
      • Hover over the snapshot in the left-hand panel to see the UI components identified by the {@code uiautomatorviewer} tool. You can view the component’s properties listed in the lower right-hand panel, and the layout hierarchy in the upper right-hand panel.
      • -
      • Optionally, click on the Toggle NAF Nodes button to see UI components that are not accessible to the {@code uiautomator} testing framework. Only limited information may be available for these components.
      • -
      -
    10. -
    - -

    Preparing to Test

    -

    Before using the {@code uiautomator} testing framework, complete these pre-flight tasks: -

    -

    Load the application to a device

    -

    If you are reading this document, chances are that the Android application that you want to test has not been published yet. If you have a copy of the APK file, you can install the APK onto a test device by using the {@code adb} tool. To learn how to install an APK file using the {@code adb} tool, see the {@code adb} documentation.

    - -

    Identify the application’s UI components

    -

    Before writing your {@code uiautomator} tests, first identify the UI components in the application that you want to test. Typically, good candidates for testing are UI components that are visible and that users can interact with. The UI components should also have visible text labels, {@code android:contentDescription} values, or both. - -

    You can inspect the visible screen objects in an application conveniently by using the {@code uiautomatorviewer} tool. For more information about how to analyze an application screen with this tool, see the section Analyzing Your Application’s UI. For more information about the common types of UI components provided by Android, see User Interface.

    - -

    Ensure that the application is accessible

    -

    This step is required because the {@code uiautomator} tool depends on the accessibility features of the Android framework to execute your functional UI tests. You should include these minimum optimizations to support the {@code uiautomator} tool: -

      -
    • Use the {@code android:contentDescription} attribute to label the {@link android.widget.ImageButton}, {@link android.widget.ImageView}, {@link android.widget.CheckBox} and other user interface controls.
    • -
    • Provide an {@code android:hint} attribute instead of a content description for {@link android.widget.EditText} fields
    • -
    • Associate an {@code android:hint} attribute with any graphical icons used by controls that provide feedback to the user (for example, status or state information).
    • -
    • Make sure that all the user interface elements are accessible with a directional controller, such as a trackball or D-pad.
    • -
    • Use the {@code uiautomatorviewer} tool to ensure that the UI component is accessible to the testing framework. You can also test the application by turning on accessibility services like TalkBack and Explore by Touch, and try using your application using only directional controls.
    • -
    -

    - -

    For more information about implementing and testing accessibility, see Making Applications Accessible.

    - -

    Note: To identify the non-accessible components in the UI, click on the Toggle NAF Nodes option in the {@code uiautomatorviewer} tool.

    - -

    Generally, Android application developers get accessibility support for free, courtesy of the {@link android.view.View} and {@link android.view.ViewGroup} classes. However, some applications use custom view components to provide a richer user experience. Such custom components won't get the accessibility support that is provided by the standard Android UI components. If this applies to your application, ensure that the application developer exposes the custom drawn UI components to Android accessibility services, by implementing the {@link android.view.accessibility.AccessibilityNodeProvider} class. For more information about making custom view components accessible, see Making Applications Accessible.

    - -

    Configure your development environment

    -

    If you're developing in Eclipse, the Android SDK provides additional tools that help you write test cases using {@code uiautomator} and buiild your JAR file. In order to set up Eclipse to assist you, you need to create a project that includes the {@code uiautomator} client library, along with the Android SDK library. To configure Eclipse:

    -
      -
    1. Create a new Java project in Eclipse, and give your project a name that is relevant to the tests you’re about to create (for example, "MyAppNameTests"). In the project, you will create the test cases that are specific to the application that you want to test.
    2. -
    3. From the Project Explorer, right-click on the new project that you created, then select Properties > Java Build Path, and do the following: -
        -
      1. Click Add Library > JUnit then select JUnit3 to add JUnit support.
      2. -
      3. Click Add External JARs... and navigate to the SDK directory. Under the platforms directory, select the latest SDK version and add both the {@code uiautomator.jar} and {@code android.jar} files.
      4. -
      -
    4. -
    -

    If you did not configure Eclipse as your development environment, make sure that the {@code uiautomator.jar} and {@code android.jar} files from the {@code /platforms/} directory are in your Java class path.

    -

    Once you have completed these prerequisite tasks, you're almost ready to start creating your {@code uiautomator} tests. - -

    Creating uiautomator Tests

    -

    To build a test that runs in the {@code uiautomator} framework, create a test case that extends the {@code UiAutomatorTestCase} class. In Eclipse, the test case file goes under the {@code src} directory in your project. Later, you will build the test case as a JAR file, then copy this file to the test device. The test JAR file is not an APK file and resides separately from the application that you want to test on the device.

    - -

    Because the {@code UiAutomatorTestCase} class extends {@code junit.framework.TestCase}, you can use the JUnit {@code Assert} class to test that UI components in the app return the expected results. To learn more about JUnit, you can read the documentation on the junit.org home page.

    - -

    The first thing your test case should do is access the device that contains the target app. It’s also good practice to start the test from the Home screen of the device. From the Home screen (or some other starting location you’ve chosen in the target app), you can use the classes provided by the {@code uiautomator} API to simulate user actions and to test specific UI components. For an example of how to put together a {@code uiautomator} test case, see the sample test case.

    - -

    uiautomator API

    -

    The {@code uiautomator} API is bundled in the {@code uiautomator.jar} file under the {@code /platforms/} directory. The API includes these key classes that allow you to capture and manipulate UI components on the target app:

    -
    -
    {@code UiDevice}
    -

    Represents the device state. In your tests, you can call methods on the {@code UiDevice} instance to check for the state of various properties, such as current orientation or display size. Your tests also can use the {@code UiDevice} instance to perform device level actions, such as forcing the device into a specific rotation, pressing the d-pad hardware button, or pressing the Home and Menu buttons.

    -

    To get an instance of {@code UiDevice} and simulate a Home button press: -

    -getUiDevice().pressHome();
    -

    - -
    {@code UiSelector}
    -
    Represents a search criteria to query and get a handle on specific elements in the currently displayed UI. -If more than one matching element is found, the first matching element in the layout hierarchy is returned as the target {@code UiObject}. When constructing a {@code UiSelector}, you can chain together multiple properties to refine your search. If no matching UI element is found, a {@code UiAutomatorObjectNotFoundException} is thrown. You can use the {@code childSelector()} method to nest multiple {@code UiSelector} instances. For example, the following code example shows how to specify a search to find the first {@link android.widget.ListView} in the currently displayed UI, then search within that {@link android.widget.ListView} to find a UI element with the text property {@code Apps}. -
    -UiObject appItem = new UiObject(new UiSelector()
    -   .className("android.widget.ListView").instance(1)
    -   .childSelector(new UiSelector().text("Apps")));
    -
    -
    - -
    {@code UiObject}
    -
    Represents a UI element. To create a {@code UiObject} instance, use a {@code UiSelector} that describes how to search for, or select, the UI element. -

    The following code example shows how to construct {@code UiObject} instances that represent a Cancel button and a OK button in your application.

    -
    -UiObject cancelButton = new UiObject(new UiSelector().text("Cancel"));
    -UiObject okButton = new UiObject(new UiSelector().text("OK"));
    -
    -

    You can reuse the {@code UiObject} instances that you have created in other parts of your app testing, as needed. Note that the {@code uiautomator} test framework searches the current display for a match every time your test uses a {@code UiObject} instance to click on a UI element or query a property.

    -

    In the following code example, the {@code uiautomator} test framework searches for a UI element with the text property {@code OK}. If a match is found and if the element is enabled, the framework simulates a user click action on the element.

    -
    -if(okButton.exists() && okButton.isEnabled()) 
    -{
    -  okButton.click();
    -}
    -
    -

    You can also restrict the search to find only elements of a specific class. For example, to find matches of the {@link android.widget.Button} class:

    -
    -UiObject cancelButton = new UiObject(new UiSelector().text("Cancel")
    -   .className("android.widget.Button"));
    -UiObject okButton = new UiObject(new UiSelector().text("OK")
    -   .className("android.widget.Button"));
    -
    -
    - -
    {@code UiCollection}
    -
    Represents a collection of items, for example songs in a music album or a list of emails in an inbox. Similar to a {@code UiObject}, you construct a {@code UiCollection} instance by specifying a {@code UiSelector}. The {@code UiSelector} for a {@code UiCollection} should search for a UI element that is a container or wrapper of other child UI elements (such as a layout view that contains child UI elements). For example, the following code snippet shows how to construct a {@code UiCollection} to represent a video album that is displayed within a {@link android.widget.FrameLayout}: -
    -UiCollection videos = new UiCollection(new UiSelector()
    -   .className("android.widget.FrameLayout"));
    -
    -

    If the videos are listed within a {@link android.widget.LinearLayout} view, and you want to to retrieve the number of videos in this collection:

    -
    -int count = videos.getChildCount(new UiSelector()
    -   .className("android.widget.LinearLayout"));
    -
    -

    If you want to find a specific video that is labeled with the text element {@code Cute Baby Laughing} from the collection and simulate a user-click on the video:

    -
    -UiObject video = videos.getChildByText(new UiSelector()
    -   .className("android.widget.LinearLayout"), "Cute Baby Laughing");
    -video.click();
    -
    -

    Similarly, you can simulate other user actions on the UI object. For example, if you want -to simulate selecting a checkbox that is associated with the video:

    -
    -UiObject checkBox = video.getChild(new UiSelector()
    -   .className("android.widget.Checkbox"));
    -if(!checkBox.isSelected()) checkbox.click();
    -
    -
    - -
    {@code UiScrollable}
    -
    Represents a scrollable collection of UI elements. You can use the {@code UiScrollable} class to simulate vertical or horizontal scrolling across a display. This technique is helpful when a UI element is positioned off-screen and you need to scroll to bring it into view. -

    For example, the following code shows how to simulate scrolling down the Settings menu and clicking on an About tablet option:

    -
    -UiScrollable settingsItem = new UiScrollable(new UiSelector()
    -   .className("android.widget.ListView"));
    -UiObject about = settingsItem.getChildByText(new UiSelector()
    -   .className("android.widget.LinearLayout"), "About  tablet");
    -about.click()
    -
    -
    -
    -

    For more information about these APIs, see the {@code uiautomator} reference.

    - -

    A sample uiautomator test case

    -

    The following code example shows a simple test case which simulates a user bringing up the Settings app in a stock Android device. The test case mimics all the steps that a user would typically take to perform this task, including opening the Home screen, launching the All Apps screen, scrolling to the Settings app icon, and clicking on the icon to enter the Settings app.

    -
    -package com.uia.example.my;
    -
    -// Import the uiautomator libraries
    -import com.android.uiautomator.core.UiObject;
    -import com.android.uiautomator.core.UiObjectNotFoundException;
    -import com.android.uiautomator.core.UiScrollable;
    -import com.android.uiautomator.core.UiSelector;
    -import com.android.uiautomator.testrunner.UiAutomatorTestCase;
    -
    -public class LaunchSettings extends UiAutomatorTestCase {   
    -
    -   public void testDemo() throws UiObjectNotFoundException {   
    -      
    -      // Simulate a short press on the HOME button.
    -      getUiDevice().pressHome();
    -      
    -      // We’re now in the home screen. Next, we want to simulate 
    -      // a user bringing up the All Apps screen.
    -      // If you use the uiautomatorviewer tool to capture a snapshot 
    -      // of the Home screen, notice that the All Apps button’s 
    -      // content-description property has the value “Apps”.  We can 
    -      // use this property to create a UiSelector to find the button. 
    -      UiObject allAppsButton = new UiObject(new UiSelector()
    -         .description("Apps"));
    -      
    -      // Simulate a click to bring up the All Apps screen.
    -      allAppsButton.clickAndWaitForNewWindow();
    -      
    -      // In the All Apps screen, the Settings app is located in 
    -      // the Apps tab. To simulate the user bringing up the Apps tab,
    -      // we create a UiSelector to find a tab with the text 
    -      // label “Apps”.
    -      UiObject appsTab = new UiObject(new UiSelector()
    -         .text("Apps"));
    -      
    -      // Simulate a click to enter the Apps tab.
    -      appsTab.click();
    -
    -      // Next, in the apps tabs, we can simulate a user swiping until
    -      // they come to the Settings app icon.  Since the container view 
    -      // is scrollable, we can use a UiScrollable object.
    -      UiScrollable appViews = new UiScrollable(new UiSelector()
    -         .scrollable(true));
    -      
    -      // Set the swiping mode to horizontal (the default is vertical)
    -      appViews.setAsHorizontalList();
    -      
    -      // Create a UiSelector to find the Settings app and simulate      
    -      // a user click to launch the app. 
    -      UiObject settingsApp = appViews.getChildByText(new UiSelector()
    -         .className(android.widget.TextView.class.getName()), 
    -         "Settings");
    -      settingsApp.clickAndWaitForNewWindow();
    -      
    -      // Validate that the package name is the expected one
    -      UiObject settingsValidation = new UiObject(new UiSelector()
    -         .packageName("com.android.settings"));
    -      assertTrue("Unable to detect Settings", 
    -         settingsValidation.exists());   
    -  }   
    -}
    -
    - -

    Building and Deploying Your uiautomator Tests

    -

    Once you have coded your test, follow these steps to build and deploy your test JAR to your target Android test device:

    -
      -
    1. Create the required build configuration files to build the output JAR. To generate the build configuration files, open a terminal and run the following command: -
      <android-sdk>/tools/android create uitest-project -n <name> -t 1 -p <path>
      -The {@code } is the name of the project that contains your {@code uiautomator} test source files, and the {@code } is the path to the corresponding project directory. -
    2. -
    3. From the command line, set the {@code ANDROID_HOME} variable: -
        -
      • In Windows: -
        set ANDROID_HOME=<path_to_your_sdk>
        -
      • -
      • In UNIX: -
        export ANDROID_HOME=<path_to_your_sdk>
        -
      • -
      -
    4. -
    5. Go to the project directory where your {@code build.xml} file is located and build your test JAR.
      ant build
    6. -
    7. Deploy your generated test JAR file to the test device by using the {@code adb push} command:
      adb push <path_to_output_jar> /data/local/tmp/
      -

      Here’s an example:

      adb push ~/dev/workspace/LaunchSettings/bin/LaunchSettings.jar /data/local/tmp/

      -
    8. -
    - -

    Running uiautomator Tests

    -

    Here’s an example of how to run a test that is implemented in the {@code LaunchSettings.jar} file. The tests are bundled in the {@code com.uia.example.my} package:

    -
    adb shell uiautomator runtest LaunchSettings.jar -c com.uia.example.my.LaunchSettings
    -

    To learn more about the syntax, subcommands, and options for {@code uiautomator}, see the {@code uiautomator} reference.

    - - -

    Best Practices

    -

    Here are some best practices for functional UI testing with the {@code uiautomator} framework:

    -
      -
    • Ensure that you validate the same UI functions on your application across the various types of devices that your application might run on (for example, devices with different screen densities).
    • -
    • You should also test your UI against common scenarios such as in-coming phone calls, network interruptions, and user-initiated switching to other applications on the device.
    • -
    - diff --git a/docs/html/tools/testing/what_to_test.jd b/docs/html/tools/testing/what_to_test.jd deleted file mode 100644 index 77ae2114f3e2601f99f175f3ff144642fdc9e09a..0000000000000000000000000000000000000000 --- a/docs/html/tools/testing/what_to_test.jd +++ /dev/null @@ -1,86 +0,0 @@ -page.title=What To Test -parent.title=Testing -parent.link=index.html -@jd:body -

    - As you develop Android applications, knowing what to test is as important as knowing how to - test. This document lists some most common Android-related situations that you should consider - when you test, even at the unit test level. This is not an exhaustive list, and you consult the - documentation for the features that you use for more ideas. The - android-developers Google Groups - site is another resource for information about testing. -

    -

    Ideas for Testing

    -

    - The following sections are organized by behaviors or situations that you should test. Each - section contains a scenario that further illustrates the situation and the test or tests you - should do. -

    -

    Change in orientation

    -

    - For devices that support multiple orientations, Android detects a change in orientation when - the user turns the device so that the display is "landscape" (long edge is horizontal) instead - of "portrait" (long edge is vertical). -

    -

    - When Android detects a change in orientation, its default behavior is to destroy and then - re-start the foreground Activity. You should consider testing the following: -

    -
      -
    • - Is the screen re-drawn correctly? Any custom UI code you have should handle changes in the - orientation. -
    • -
    • - Does the application maintain its state? The Activity should not lose anything that the - user has already entered into the UI. The application should not "forget" its place in the - current transaction. -
    • -
    -

    Change in configuration

    -

    - A situation that is more general than a change in orientation is a change in the device's - configuration, such as a change in the availability of a keyboard or a change in system - language. -

    -

    - A change in configuration also triggers the default behavior of destroying and then restarting - the foreground Activity. Besides testing that the application maintains the UI and its - transaction state, you should also test that the application updates itself to respond - correctly to the new configuration. -

    -

    Battery life

    -

    - Mobile devices primarily run on battery power. A device has finite "battery budget", and when it - is gone, the device is useless until it is recharged. You need to write your application to - minimize battery usage, you need to test its battery performance, and you need to test the - methods that manage battery usage. -

    -

    - Techniques for minimizing battery usage were presented at the 2010 Google I/O conference in the - presentation - - Coding for Life -- Battery Life, That Is. This presentation describes the impact on battery - life of various operations, and the ways you can design your application to minimize these - impacts. When you code your application to reduce battery usage, you also write the - appropriate unit tests. -

    -

    Dependence on external resources

    -

    - If your application depends on network access, SMS, Bluetooth, or GPS, then you should - test what happens when the resource or resources are not available. -

    -

    - For example, if your application uses the network,it can notify the user if access is - unavailable, or disable network-related features, or do both. For GPS, it can switch to - IP-based location awareness. It can also wait for WiFi access before doing large data transfers, - since WiFi transfers maximize battery usage compared to transfers over 3G or EDGE. -

    -

    - You can use the emulator to test network access and bandwidth. To learn more, please see - Network Speed Emulation. - To test GPS, you can use the emulator console and {@link android.location.LocationManager}. To - learn more about the emulator console, please see - - Using the Emulator Console. -

    diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs index 72f9f217515808963d322c2b8c5c87b56876febc..3ce0d11809edfc0e3fdedc569b06b80611bef431 100644 --- a/docs/html/tools/tools_toc.cs +++ b/docs/html/tools/tools_toc.cs @@ -5,32 +5,11 @@ ?>sdk/index.html">Download - - - - - + + + + + + + @@ -190,6 +242,8 @@ class="en">Tools Help Build System -